反射

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

反射仅作为一种实现反序列化的手段,并非直接攻击方式

反射内容

典型代码

 public static void main(String[] args) throws Exception{  
     Person person = new Person();  
     Class c = person.getClass();
 }

这里出现了未知的关键字 Class ,这与定义类时使用的 class 并不是一个作用,下面记录其具体含义。

Java Class 含义

Java 程序在运行前会进行编译生成一个 .class 后缀文件,这个文件的内容就是对应编译的类的二进制信息,而在上述代码中,Class c 所记录的内容就是 Person 类的具体信息。

简而言之,person.class == Class , Class 即为描述类的类。

Class 类的对象的作用是在运行时提供获得某个对象的类型信息,在当前语境中的实例化对象 c 就记录了 person 实例的对象信息。

Java 原生与反射相关类

反射机制相关操作一般位于 java.lang.reflect 包中。

而 java 的反射机制组成需要重点注意以下类:

反射相关使用方法

实例化对象

 Person test = new Person();

注意,此种声明仅适用于普通类,针对于 Class 类并不适用。

 // 会抛出异常
 Class aaa = new Class();

获取实例化对象类的方法

getClass() 方法

如果上下执行环境中存在某个类的实例 obj ,那么可以通过 obj.getClass 来获取该类。

 Person test = new Person();
 Class testC = test.getClass();
类的 .Class 方法

如果已经加载了某个类在运行环境中,仅仅只是想获取到其 java.lang.Class 对象,可以直接获取其 Class 属性。

 Class class2 = Person.class;
动态加载类 Class.forName(String className)

已知某类名。可以使用 Class 中的 forName 方法获取其类,但具体使用仍然需要对类进行实例化

 Class class3 = Class.forName("Person");

获取成员变量 Filed

方法位于 java.lang.reflect.Filed 包中。

例:

 import java.lang.reflect.Field;
 ​
 public class Example {
     public String publicField;
     private String privateField;
 ​
     public static void main(String[] args) {
         Class<?> clazz = Example.class;
         
         // 获取所有公共字段,包括父类的公共字段
         Field[] fields = clazz.getFields();
         
         for (Field field : fields) {
             System.out.println("Field name: " + field.getName());
         }
     }
 }

其中,Class<?> 是 Java 中的一个通用类型,表示某个类的 Class 对象,它是通过反射机制访问类的元数据的一个关键工具。Class<?> 中的问号 (?) 是一个通配符,表示可以是任何类型的 Class 对象。

如果知道具体类型,也可以使用 Class<T> 来指定类型,例如:Class<String> 表示 String 类的 Class 对象。

获取成员方法 Method

法位于 java.lang.reflect.Method 包中。

 Method getMethod(String name, 类<?>... parameterTypes) //返回该类所声明的public方法
 ​
 Method getDeclaredMethod(String name, 类<?>... parameterTypes) //返回该类所声明的所有方法
 ​
 //第一个参数获取该方法的名字,第二个参数获取标识该方法的参数类型
 ​
 Method[] getMethods() //获取所有的public方法,包括类自身声明的public方法,父类中的public方法、实现的接口方法
 ​
 Method[] getDeclaredMethods() // 获取该类中的所有方法

example:

 import java.lang.reflect.Method;
 ​
 public class Example {
     public void publicMethod() {}
     public void publicMethod(String name) {}
 ​
     public static void main(String[] args) throws NoSuchMethodException {
         Class<?> clazz = Example.class;
         
         // 获取带有一个 String 参数的 publicMethod 方法
         Method method = clazz.getMethod("publicMethod", String.class);
         
         // 打印方法名
         System.out.println("Method name: " + method.getName());
     }
 }

获取构造函数 Constructor

方法包含在 java.lang.reflect.Constructor

使用方法类似 getMethod

 Constructor<?>[] getConstructors() :只返回public构造函数
 ​
 Constructor<?>[] getDeclaredConstructors() :返回所有构造函数
 ​
 Constructor<> getConstructor(类<?>... parameterTypes) : 匹配和参数配型相符的public构造函数
 ​
 Constructor<> getDeclaredConstructor(类<?>... parameterTypes) : 匹配和参数配型相符的构造函数
私有构造函数的调用

私有构造函数通常用于 限制类的实例化,或者实现 单例模式。虽然私有构造函数不能直接通过 new 关键字来创建对象,但可以通过 反射 访问和调用私有构造函数。

如:

 public class Example {
     private Example() {
         System.out.println("Private Constructor");
         // 无法通过直接 new Example 的方法直接进行触发
     }
 }

可以使用 Constructor 类的 setAccessible(true) 方法来绕过 Java 的访问控制,访问和调用私有构造函数。

example:

 import java.lang.reflect.Constructor;
 ​
 public class Example {
     private Example() {
         System.out.println("Private Constructor");
     }
 ​
     public static void main(String[] args) throws Exception {
         Class<?> clazz = Example.class;
 ​
         // 获取私有构造函数
         Constructor<?> constructor = clazz.getDeclaredConstructor();
 ​
         // 设置构造函数可访问
         constructor.setAccessible(true);
 ​
         // 通过反射调用私有构造函数
         Example example = (Example) constructor.newInstance();
 ​
         System.out.println("Object created using private constructor");
     }
 }

反射实际使用

反射创建对象

java 9 以后 Class 类中的 newInstance() 方法被弃用了,推荐使用 Constructor 类中的 newInstance() 方法,而在 java8 及之前 newInstance() 方法是 Class 中的一部分。

使用上例提到的 newInstance() 方法

 Class example = Class.forName("className"); // 创建 Class 类对象
 Object class1 = example.newInstance(); // 创建类对象

invoke() 方法

位于 java.lang.reflect.Method 类中,用于执行任何类型的某个对象的目标方法。一般与 getMethod() 结合使用。

 public Object invoke(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException

第一个参数为类的实例,第二个参数为相应函数中的参数

obj:从中调用底层方法的对象,必须是实例化对象,如果是静态方法,可以传递 null

args: 用于方法的调用,是一个 object 的数组,参数有可能是多个,如果方法没有参数,可以传递一个空数组或者 null.

但需要注意的是,invoke 方法第一个参数并不是固定的:

example:

调用实例代码

 import java.lang.reflect.Method;
 ​
 public class Example {
     private void privateMethod(String name) {
         System.out.println("Hello, " + name);
     }
 ​
     public static void main(String[] args) throws Exception {
         Example example = new Example();
         
         // 获取私有方法
         Method method = Example.class.getDeclaredMethod("privateMethod", String.class);
         
         // 设置方法可访问
         method.setAccessible(true);
         
         // 通过反射调用私有方法
         method.invoke(example, "John");
     }
 }

调用静态方法

 import java.lang.reflect.Method;
 ​
 public class Example {
     public static void staticMethod(String name) {
         System.out.println("Hello from static method, " + name);
     }
 ​
     public static void main(String[] args) throws Exception {
         // 获取静态方法
         Method method = Example.class.getDeclaredMethod("staticMethod", String.class);
         
         // 通过反射调用静态方法
         method.invoke(null, "John");  // 静态方法 obj 为 null
     }
 }

总结

基于此,可以通过反射修改实例化后的对象成员变量从而实现后续反序列化载荷生成时的序列状态,从而实现漏洞利用.

参考

https://drun1baby.top/2022/05/20/Java%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E5%9F%BA%E7%A1%80%E7%AF%87-02-Java%E5%8F%8D%E5%B0%84%E4%B8%8EURLDNS%E9%93%BE%E5%88%86%E6%9E%90/