购物网站做推广,网站强制字体wordpress,网站免费建站性盈盈影院,顺德网站建设效果反射
对于反射这个概念来说#xff0c;直白的讲就是#xff1a;
对象可以通过反射获取他的类#xff0c;类可以通过反射拿到所有⽅法#xff08;包括私有#xff09;#xff0c;拿到的⽅法可以调⽤而众所周知 JAVA 是一门静态语言#xff0c;我们通过反射就可以达到动…反射
对于反射这个概念来说直白的讲就是
对象可以通过反射获取他的类类可以通过反射拿到所有⽅法包括私有拿到的⽅法可以调⽤而众所周知 JAVA 是一门静态语言我们通过反射就可以达到动态的语言的特性。 我们可以类比一下我们的老朋友 PHP 对于 PHP 来说一个简单的 webshell 可以完成各种操作而对于 JAVA 来说我们就可以用 反射 达到一些动态的特性。 比如以下代码
public void execute(String className, String methodName) throws Exception {Class clazz Class.forName(className);clazz.getMethod(methodName).invoke(clazz.newInstance());
}以上代码有几个反射中重要的方法
获取类的⽅法 forName实例化类对象的方法newInstance获取函数的方法getMethod执行函数的方法invoke
但是 forName 并不是获取类的唯一方法对于反射来说还有两种方法来获取类 1、如果已经有了一个类的实例我们只需要用 obj.getClass() 来获取他的类。 2、如果你知道某个类的名字想获取到这个类就可以使⽤ forName 来获取。 当然除了以上反射的方法还有一种方法 如果你已经加载了某个类只是想获取到它的 java.lang.Class 对象那么就直接拿它的 class 属性即可。
如果我们看一下 forName 会发现他有两个重载方法
public static Class? forName(String className) throws ClassNotFoundException { Class? caller Reflection.getCallerClass(); return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}public static Class? forName(String name, boolean initialize, ClassLoader loader) throws ClassNotFoundException
{ Class? caller null; SecurityManager sm System.getSecurityManager(); if (sm ! null) { // Reflective call to get caller class is only needed if a security manager // is present. Avoid the overhead of making this call otherwise. caller Reflection.getCallerClass(); if (sun.misc.VM.isSystemDomainLoader(loader)) { ClassLoader ccl ClassLoader.getClassLoader(caller); if (!sun.misc.VM.isSystemDomainLoader(ccl)) { sm.checkPermission( SecurityConstants.GET_CLASSLOADER_PERMISSION); } } } return forName0(name, initialize, loader, caller);
}对比着来看会发现第一个比第二个少了个点东西
public static Class? forName(String className)public static Class? forName(String name, boolean initialize, ClassLoader loader)对于第二种
name : 类名initialize : 是否初始化ClassLoader : 类的加载器
ClassLoader 看到这个名字就能看出来他就是一个类的 加载器 。 而 Java 默认的 ClassLoader 是根据类的名字类的全称例如 : java.lang.runtime加载类。
反射的主要目的就是不通过 import 来导入类这一点是攻击者想要的而 forName 就可以做到这一点。
Java 中可以在类中编写内部类在编译的时候会产生两个类文件如下
public class main { public static void main(String[] args) throws Exception { System.out.println(Hello World!); } class test1{ public String test hello; }
}会生成如下文件
├── main$test1.class
└── main.class而你想要通过反射拿到 test1 类的时候可以通过 Class.forName(main$test1) 的方式来加载内部类从而一系列操作。
说了这么多我们举个小例子
public static void main(String[] args) throws Exception { Class c Class.forName(java.lang.Runtime); c.getMethod(exec, String.class).invoke(c.newInstance(), open -a Calculator );
}发现会报错对吧
Exception in thread main java.lang.IllegalAccessException: Class main can not access a member of class java.lang.Runtime with modifiers private这里很好理解c.newInstance() 的作用就是调用这个类的无参构造函数所以存在以下情况就会失败
加载的类没有无参构造函数加载的类的构造函数是私有的 我们可以看一下 newInstance() 的代码
if (cachedConstructor null) { if (this Class.class) { throw new IllegalAccessException( Can not call newInstance() on the Class for java.lang.Class ); }这里就很明确了 if (cachedConstructor null) 如果没有构造函数就抛出一个异常。
// Security check (same as in java.lang.reflect.Constructor)
int modifiers tmpConstructor.getModifiers();
if (!Reflection.quickCheckMemberAccess(this, modifiers)) { Class? caller Reflection.getCallerClass(); if (newInstanceCallerCache ! caller) { Reflection.ensureMemberAccess(caller, this, null, modifiers); newInstanceCallerCache caller; }
}跟进 ensureMemberAccess 方法
public static void ensureMemberAccess(Class? var0, Class? var1, Object var2, int var3) throws IllegalAccessException { if (var0 ! null var1 ! null) { if (!verifyMemberAccess(var0, var1, var2, var3)) { throw new IllegalAccessException(Class var0.getName() can not access a member of class var1.getName() with modifiers \ Modifier.toString(var3) \); } } else { throw new InternalError(); }
}可以看到在这里抛出了异常在此先不细说中间是怎么校验的。
所以我们可以这样来改造
public static void main(String[] args) throws Exception { Class c Class.forName(java.lang.Runtime); c.getMethod(exec, String.class).invoke(c.getMethod(getRuntime).invoke(c), open -a Calculator);
}这里有两个方法需要说一下 getMethod 和 invoke getMethod 的作用是通过反射获取一个类的某个特定的公有方法然后利用这个方式来获取 Runtime.exec 方法。
invoke 大家应该很熟悉我们可以看看他的源码
public Object invoke(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException
{ if (!override) { if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) { Class? caller Reflection.getCallerClass(); checkAccess(caller, clazz, obj, modifiers); } } MethodAccessor ma methodAccessor; // read volatile if (ma null) { ma acquireMethodAccessor(); } return ma.invoke(obj, args);
}这里做了一些操作可以自己跟一下这里就不过多叙述了。
invoke 的作用是执行方法它的第一个参数是
如果这个方法是一个普通方法那么第一个参数是类对象如果这个方法是一个静态方法那么第一个参数是类 正常执行方法是 [1].method([2], [3], [4]...)在反射里就是这样的 method.invoke([1], [2], [3], [4]...)。