代码审计CC1 LazyMap链,代理问:动态代理如何实现?
摘要:代码审计 | CC1 LazyMap 链 —— 动态代理 学习笔记,记录自己学 CC1 LazyMap 版的思路历程 前置:已经看完 TransformedMap 版,Transformer 链那部分这里就不重复了,直接从&
代码审计 | CC1 LazyMap 链 —— 动态代理
学习笔记,记录自己学 CC1 LazyMap 版的思路历程
前置:已经看完 TransformedMap 版,Transformer 链那部分这里就不重复了,直接从"触发点不同"讲起。
目录
和 TransformedMap 版有什么区别
第一步:LazyMap 的触发机制
第二步:谁来调用 LazyMap.get()
AnnotationInvocationHandler.invoke() 里有 get()
invoke() 怎么自动被调用——动态代理
第三步:readObject 怎么触发 invoke()
第四步:两层 Handler 的构造
完整 Payload
完整调用链总结
和 TransformedMap 版对比
和 TransformedMap 版有什么区别
TransformedMap 版的触发路径是这样的:
readObject()
└─ setValue()
└─ checkSetValue()
└─ ChainedTransformer.transform() ← 命令执行
LazyMap 版的触发路径是这样的:
readObject()
└─ proxyMap.entrySet() ← 调用代理对象上的任意方法
└─ invoke() ← 动态代理拦截,转到 AnnotationInvocationHandler.invoke()
└─ LazyMap.get() ← 这里触发 transform()
└─ ChainedTransformer.transform() ← 命令执行
核心差异:
TransformedMap 触发的是 setValue()
LazyMap 触发的是 get(),具体是 LazyMap.get(不存在的key) 时自动调用 factory.transform()
Transformer 链本身(ConstantTransformer + 三个 InvokerTransformer)完全一样,不用改。
第一步:LazyMap 的触发机制
先看 LazyMap.get() 的源码,在 org.apache.commons.collections.map.LazyMap 里:
关键逻辑:
public Object get(Object key) {
// create value for key if key is not currently in the map
if (map.containsKey(key) == false) {
Object value = factory.transform(key);
map.put(key, value);
return value;
}
return map.get(key);
}
只要 get() 时传入一个 map 里不存在的 key,就会自动调用 factory.transform(key)。如果 factory 是我们的 ChainedTransformer,命令就执行了。
还有个问题,LazyMap 类虽然是 public 的,但构造方法是 protected:
查找用法找到 decorate(),它是 public 的并且内部会调用 LazyMap(map, factory):
所以构造方式为:
Map innerMap = new HashMap();
Map lazyMap = LazyMap.decorate(innerMap, chain);
innerMap 是空的,目的是让后续 get(任意key) 时永远命中 containsKey == false 的分支,稳定触发 transform()。
