对TransformMap调用了setValue(),但因为他的父类是AbstractInputCheckedMapDecorator其中的MapEntry继承AbstractMapEntryDecorator,而这个AbstractMapEntryDecorator实现了Map.entry部分,同时其子类MapEntry重写了setValue,所以TransformMap在Mapentry轮询的时候调用setValue调用的是他爹的
AnnotationInvocationHandler因为不是公共类,所以只能通过反射的形式获取而后实例化
1 | Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); |
循环调用部分
1 | Method getruntimemethod = (Method) new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}).transform(Runtime.class); |
可能比较难理解的是InvokerTransformer里面这个部分
1 | public Object transform(Object input) { |
这里第一步先传入一个Runtime.class再反射获取getMethod方法,然后再通过invoke,这里iArgs传参的是("getRuntime,null"),相当于执行Class.getMethod("getRuntime",null),所以返回的是个getRuntime的method
然后是第二个
1 | Runtime runtime = (Runtime) new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}).transform(getruntimemethod); |
这一部分说白了就是getruntimemethod.invoke(null,null)
getruntimemethod是第一步返回的执行getRuntime的method,这属于调用反射的invoke给它执行下就是,所以返回的是个Runtime。
谁来调用ChainedTransformer
1 | InvokerTransformer[] Testtransformers = new InvokerTransformer[]{ |
这里ChainedTransformer我们写到了到TransformedMap.decorate(map,null,TestTransformer)
所以当最外层的AnnotationInvocationHandler实现类反序列化时候,ReadObject之中的MapEntry轮询在调用到memberValue.setValue()也就是调用我们给他的TransformedMap的setValue()
而TransformedMap它的父类重写了setValue所以又会去调它TransformedMap.checkValue(Value)的时候会调用到他的valueTransformer.transform(value),这里valueTransformer就是我给他的ChainedTransformer
那自然也就调用了ChainedTransformer.Transformer(Value)
ChainedTransformer调用的时候需要输入value但他这里已经写死了是个AnnotationTypeMismatchExceptionProxy,我们如何将他ChainedTransformer数组最初输入执行的做一个Runtime类
1 | Object value = memberValue.getValue(); |
首先看我们的最初的ChainTransformer
1 | public Object transform(Object object) { |
确实,Transformer数组保持如下写法在输入的时候会把AnnotationTypeMismatchExceptionProxy会通过
setValue->checkValue->valueTransformer.Transformer(Value)给传入,污染的执行数组的初始value。
1 | Transformer[] transformers = new Transformer[]{ |
所以这里数组中第一个我们需要添加一个,会无视传入的value并且可以满足在轮询执行的第一轮会返回Runtime.class的一个Transformer类。
所以我们引入了ConstantTransformer,他构造传入的Object,即便调用transform(value)对他传参,它返回的还是构造时候传入的Object
1 | public ConstantTransformer(Object constantToReturn) { |
所以我们可以通过在第一个使用ConstantTransformer(Runtime.class)来让AnnotationTypeMismatchExceptionProxy输入的setValue(Value)废掉,并使得第一个会直接返回Runtime.class以供后面的使用。
InvokerTransformer第一次调用时候获取的是method这里可能会比较绕
一开始想为什么这里不直接调用getRuntime,而是去调用getMethod让他返回getRuntime的Method,主要还是Runtime的序列化问题,其次
InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},neew Object[]{"getRuntime",null})
因为,我们传入的是Runtime.class,进入之后cls相当于是Runtime.class.getClass()
1 | Class cls = input.getClass(); |
那cls当然是没有getRuntime()能被获取到
但getMethod是有的,所以可以获取cls的getMethod然后反射执行相当于Runtime.class.getMethod("getRuntime",null)
所以返回一个getRuntime的Method,之后再由其他几个InvokerTransformer往后执行。
差不多就这个意思
1 | Method th = (Method) Runtime.class.getClass().getMethod("getMethod", String.class, Class[].class).invoke(Runtime.class,new Object[]{"getRuntime",null}); |
getMethod套一个getMethod确实稍微有点绕,只要牢记getMethod的两个参,前一个是方法名,后一个是变量类型就行。
invoke也是同理,牢记前面是obj后面是传入的参就行。
另一条cc1
正向来说,前半部入口是AnnotationInvokcationHandler运行到memberValues.entrySet()再调用一个做动态代理类用的AnnotationInvokcationHandler类
1 | for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) { //<-这里调用代理类的.entrySet() |
而这个代理类AnnotationInvokcationHandler又是去代理的lazymap的一个实现类,所以动态代理中memberValues就是lazymap
1 | class AnnotationInvocationHandler implements InvocationHandler, Serializable { |
我们要通过memberValues.entrySet()触发的invoke走到最后触发memberValues.get()
他这里要求传入的method也就是调用的方法,必须是空参调用才能走到我们目标的memberValues.get(member)
题外话:这里其实原本设计的是要传入注解方法的,所以才做的这些个判断
1 | public Object invoke(Object proxy, Method method, Object[] args) { |
中间相较于第一个cc1区别是用lazyMap替换了TransformedMap往后的都是一样,用的lazyMap.get()中会调用factory.transform()
1 | public Object get(Object key) { |
再简单捋一下这条链子,先是有了LazyMap.get()再往上走发现了AnnotationInvokcationHandler的invoke里有get()
所以也就说明AnnotationInvokcationHandler在作为LazyMap的动态代理调用方法的时候可以执行到LazyMap的get
但是这里有个if导致需要动态代理在执行无参调用的方法才可以走到get()
巧合的是他AnnotationInvokcationHandler本身ReadObject的时候就会调用一个memberValues.entrySet()就省得再去找别的ReadObject的了。
所以我们就可以新建一个AnnotationInvokcationHandler1作为LazyMap的动态代理
而后再新建一个正常的AnnotationInvokcationHandler2把上一个AnnotationInvokcationHandler1塞进去
对AnnotationInvokcationHandler2正常序列化
再反序列化的时候就readObject->.entry()->invoke->.get(Value)->.transformer(Value)后面就和一开始那条链一样了
cc6为什么Lazymap需要处理一下删除掉其中map的key
因为我们在TiedMaoEntry将其put到Hashmap的时候,会触发hashcode()
1 | TiedMapEntry ti = new TiedMapEntry(lazymap,"aaa"); |
最终走到Lazymap这里,并把外部的TiedMapEntry的value最终作为keyput给lazymap的map当中
1 | public Object get(Object key) { |
所以当触发反序列化的时候我们的put时候lazymap中的key和value会下面判True也就自然调用不了transform
1 | if (map.containsKey(key) == false) |
解决访问这里两种
1.lazymap.remove(TiedMapEntry传入的value),让lazymap这里map中的table[0]变为空
2.修改掉这个键值对,让他换成别的反正key别和TiedMapEntry传入的value一样就行(还是推荐第一种 我比较闲才会用这种)

cc3的过程
1 | InstantiateTransformer(paramstype,params) -> transformer(TrAXFilter.class) -> TrAXFilter(tp) -> .newTransformer() -> TemplatesImpl.newTransformer() -> .getTransletInstance() -> .defineTransletClasses() -> loader.defineClass(_bytecodes) -> newInstance() |
刚接触时其中容易不好理解的部分是InstantiateTransformer.transformer中实例化TrAXFilter的部分,所以需要正向简单过一下流程
将TrAXFilter.class通过InstantiateTransformer.transformer中获取构造器,而后实例化
1 | public Object transform(Object input) { |
TrAXFilter实例化时被我们传入了TemplatesImpl
实例化时他会直接调用我们传入TemplatesImpl的newTransformer方法
1 | public TrAXFilter(Templates templates) throws |
在TemplatesImpl.newTransformer中会调用getTransletInstance
1 | public synchronized Transformer newTransformer() |
然后getTransletInstance中就会调用到TemplatesImpl自己defineTransletClasses方法来给_class赋值
1 | private Translet getTransletInstance() |
通过我们反射修改读取test.class传入的_bytecodes二维数组,在这里获取类
1 | private void defineTransletClasses() |
又回到外层newTransformer对我们传入的class进行实例化最终实现rce
1 | AbstractTranslet translet = (AbstractTranslet) _class[_transletIndex].newInstance(); |
这个因为最外层的InstantiateTransformer也是transform所以和cc6以及cc1的chain中的部分可以做替换
1 | Transformer[] transformers = new Transformer[]{ |
cc4流程和一些容易出问题的点
首先,cc4是commons.collections4的
transformingComparamtor中的compare()方法会触发transform
1 | public int compare(final I obj1, final I obj2) { |
往前走找到个PriorityQueue类的readObject->heapify()->siftDown()
1 | private void heapify() { |
往下走comparator不为空,调用siftDownUsingComparator()
1 | private void siftDown(int k, E x) { |
siftDownUsingComparator里面就调用了comparator.compare()
1 | private void siftDownUsingComparator(int k, E x) { |
他那个comparator是构造方法里直接传的
1 | public PriorityQueue(int initialCapacity, |
所以可以替代像cc3这类链条的前半段
HashMap.readObject()->TideEntrymap.hashcode()->Lazymap.get()这部分
整个前半段流程就是PriovrityQueue.readObject()->heapify()->siftDown()->siftDownUsingComparator->TransformingComparator.compare()->transformer.transform()
比较容易出问题的是heapify()中size>>>1的话才能进入siftDown方法
1 | private void heapify() { |
所以这里我直接用反射改的size为2就可,yso则是另一种方式
直接priorityQueue.add(1)加了两个,让他数组里有东西就行
然后它add()和hashmap.put那个问题差不多,它自己又会调用priorityQueue.offer()里面又会调用siftDown最后就给执行了。
所以这里和cc6前半段一样处理就行,把执行路径中的一个给扬了,后面反射再给加回来就行。
所以可以直接选一个倒霉蛋比如TransformingComparator本来要传入的chaintansformers
1 | ChainedTransformer chaintransformers = new ChainedTransformer(transformers); |
直接改成ConstantTransformer(1)
1 | TransformingComparator transformingComparator = new TransformingComparator<>(new ConstantTransformer(1)); |
让链子执行到TransformingComparator.compare()时候调用个寂寞。
cc2的流程和一些小细节
cc2其实可以看作是cc4将链条的中段
chainTransformer.transfrom()->InstantiateTransformer.transform()->TrAXFilter()->TemplatesImpl.newTransformer()
这一大段替换为直接用InvokerTransformer().transfrom反射执行TemplatesImpl.newTransformer()
说白了就是想办法给InvokerTransformer的Transform(value)传入TemplateImpl以反射调用的他的newTransfromer()
这里找到可以用PriorityQueue.add将TemplatesImpl当作参数给传入,一直到链条最后让invokeTransformer.transform(value)来执行
下面的add会将传入的值添加到queue数组
1 | PriorityQueue |
这里给它用add传入观察一下参数怎么才能传到InvokerTransformer

这里传入的在heapify中被以轮询的形式传入的siftDown()

然后在这里又被传入到comparator.compare()

调用到了TransformingComparator.compare()部分,可以看到最初传入的1作为参数被传入给了.transformer(obj1)

既然能传过去就把1替换为TemplateImpl即可实现后续的反射调用newTransform()
所以这就是cc2与cc4不同的地方,cc4中段和cc3一样是是依赖于类的实例化来最后实现调用TemplateImpl.newTransform()
cc2是直接用了cc4的前段直到触发transform的部分
而中段直接传参TemplateImpl给invokerTransformer.transform()来反射调用TemplateImpl.newTransform()
当然,这也是得益于这个参数能传的下来..