反射
反射(Reflection)是 Java 的重要机制之一。通过反射,我们可以在程序运行过程中,利用 Class 对象去动态的查看和操作一个类,对我们后续学习类加载机制至关重要,因此,我们选择首先学习反射。
Class对象
Java 反射是直接对 Class 对象进行操作的,Class 对象就是 JVM 在运行时用来描述一个类的信息对象,这里有四种方式获取。
- <类名>.class
java public class Test { public static void main(String[] args) { Class<?> clazz = Test.class; } } - Class.forName()
这个需要我们知道一个类的完整类名。java public class Test { public static void main(String[] args) throws ClassNotFoundException { Class<?> clazz = Class.forName("com.Test"); } } - <实例>.getClass()
通过实例后的对象获取。java public class Test { public static void main(String[] args) { Test test = new Test(); Class<?> clazz = test.class; } } - 类加载器
同样需要完整的类名。java public class Test { public static void main(String[] args) throws ClassNotFoundException { Class<?> clazz = ClassLoader.getSystemClassLoader().loadClass("com.test"); } }
这里需要注意的一点是,在对数组对象获取 Class 时,写法有所不同。
Class<?> clazz = double[].class;
Class<?> clazz = Class.forName("[D");
Class<?> clazz = Class.forName("[[D");
在调用内部类时,需要使用$来代替.,如com.Test类有一个叫做Hello的内部类,那么调用时类名就为:com.Test$Hello。
API
对于一个类,我们获得到 Class 对象后,就可以通过反射 API 获得一切信息。
- 获取父类
clazz.getSuperclass(); - 获取字段
java Field[] field1 = clazz.getDeclaredFields(); // 当前类:所有字段,不包括父类 Field[] field2 = clazz.getFields(); // 当前类 + 父类:所有 public 字段 Field field4 = clazz.getDeclaredField("name"); // 当前类:找名字叫 name 的字段,不管 public/private/protected Field field3 = clazz.getField("name"); // 当前类 + 父类:找名字叫 name 的 public 字段 - 获取方法
java Method[] method1 = clazz.getDeclaredMethods(); // 当前类:所有方法,不包括父类 Method[] method2 = clazz.getMethods(); // 当前类 + 父类:所有 public 方法 Method method4 = clazz.getDeclaredMethod("hello"); // 当前类:找名字为 hello 的方法,不管 public/private/protected Method method3 = clazz.getMethod("hello"); // 当前类 + 父类:找名字为 hello 的 public 方法 - 获取构造方法
java Constructor<?>[] constructor1 = clazz.getDeclaredConstructors(); // 当前类:所有构造方法,不包括父类 Constructor<?>[] constructor2 = clazz.getConstructors(); // 当前类:所有 public 构造方法,不包括父类 Constructor<?> constructor4 = clazz.getDeclaredConstructor(String.class); // 当前类:找参数类型为 String 的构造方法,不管 public/private/protected Constructor<?> constructor3 = clazz.getConstructor(String.class); // 当前类:找参数类型为 String 的 public 构造方法
下面通过 java.lang.Runtime 类进行演示,该类有个 exec 方法可以执行命令,因此许多 payload 都有这个。
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class Test {
public static void main(String[] args) throws Exception {
// 获取 Class 对象
Class clazz = Class.forName("java.lang.Runtime");
// 获取构造方法
Constructor<?> constructor = clazz.getDeclaredConstructor();
// 修改访问权限
constructor.setAccessible(true);
// 创建实例
Object runtime = constructor.newInstance();
// 获取 exec 方法
Method method = runtime.getClass().getMethod("exec", String.class);
// 调用 exec
Process process = (Process) method.invoke(runtime, "whoami");
//获取结果
InputStream in = process.getInputStream();
// 输出
byte[] buffer = new byte[1024];
int len = in.read(buffer);
System.out.println(new String(buffer, 0, len));
}
}

评论区
评论加载中...