代码审计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()。
阅读全文