前言

我们之前已经学习了URLDNS,通过对该利用链的分析,对反序列化的利用又有新的理解,不过相对于CommonCollections反序列化链的分析来说,URLDNS只能算是给我们做一个准备,我们首先对针对CommonCollections1学习分析前进行简单的前置内容学习,让我们在后续分析的时候有更加透彻的了解

关键的接口和类

AbstractMapDecorator

CommonsCollections库中提供了一个抽象类org.apache.commons.collections.map.AbstractMapDecorato,这个类是 Map 的扩展,并且从名字中可以知道,这是一个基础的装饰器,用来给 map 提供附加功能,被装饰的 map 存在该类的属性中,并且将所有的操作都转发给这个 map。这个类有很多实现类,各个类触发的方式不同,重点关注的是 TransformedMap 以及 LazyMap

Transformer接口

TransformerApache Commons Collections 库引入的一个接口,每个具体的 Transformer 类必须实现 Transformer 接口,Transformer 只有一个方法transform(),这将是解锁计算器的关键方法。

ConstantTransformer

这个类实现了Transformer,并且也实现了Serializable接口,说明是可序列化的。它的作用就是构造函数的时候传入一个对象,在执行回调(transform方法)时返回这个对象,进而方便后续操作。这个类用于和 ChainedTransformer 配合,将其结果传入 InvokerTransformer 来调用我们指定的类的指定方法

1667278689671

ChainedTransformer

这个同样是实现了Transformer接口的一个类,同时也实现了Serializable接口,作用是将内部的多个Transformer串在一起。ChainedTransformertransform函数中指定了将传入对象按顺序执行transformers数组中的transform方法,前一个transformer的输出作为后一个transformer的输入。通俗来说就是,前一个回调返回的结果,作为后一个回调的参数传入。

那么这样我们可以利用ChainedTransformerConstantTransformerInvokerTransformertransform方法串起来。通过ConstantTransformer返回某个类,交给InvokerTransformer去调用类中的某个方法。

官方介绍:

​ 将指定的转换器连接在一起的转化器实现。输入的对象将被传递到第一个转化器,转换结果将会输入到第二个转化器,并以此类推

1667279224261

InvokerTransformer

这个实现类从 Commons Collections 3.0 引入,功能是使用反射创建一个新对象,我们来看一下它的 transfrom 方法,方法注释写的很清楚,通过调用 input 的方法,并将方法返回结果作为处理结果进行返回。

InvokerTransformer中实现了转变的方法transform,该方法会在map被改变的时候调用。在途中红色框中的代码块使用反射的方式,获得转变后的对象的类。然后第二行取得该类的一个方法,方法名(this.iMethodName)和对应的参数列表(this.iParamTypes)在构造函数中已经指定。第三行以输入的该对象来执行获得的这个方法,执行的参数(this.iArgs)也在构造函数中指定。

1667279991356

测试代码

import org.apache.commons.collections.functors.InvokerTransformer;
public class InvokerTransformerDemo {
    public static void main(String[] args) throws Exception {

        Runtime runtime = Runtime.getRuntime();

        new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}).transform(runtime);
    }
}

1667293858017

TransformedMap

org.apache.commons.collections.map.TransformedMap 类可以在一个元素被加入到集合内时,自动对该元素进行特定的修饰变换,具体的变换逻辑由 Transformer 来定义,TransformerTransformedMap 实例化时作为参数传入,Transformer 实现类分别绑定到 mapkeyvalue 上,当 mapkeyvalue 被修改时,会调用对应 Transformer 实现类的 transform() 方法。我们主要用这个类中的decorate()来衔接chains。

编写TransformedMap测试

MyTransformer

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

public class MyTransformer implements Transformer, Serializable {
    private String name;
    //protected修饰的构造方法,给成员变量赋值
    protected MyTransformer(String name){
        System.out.println("MyTransformer:MyTransformer()");
        this.name = name;
    }
    //使用getInstance获取对象
    public static Transformer getInstance(String name){
        System.out.println("MyTransformer:getInstance()");
        return new MyTransformer(name);
    }
    //重写transform方法
    @Override
    public Object transform(Object input) {
        System.out.println("MyTransformer:transform()");
        System.out.println("input is : " + input);
        return this.name;
    }
}

测试类

import org.apache.commons.collections.map.TransformedMap;
import java.util.HashMap;
import java.util.Map;

public class MyTransformerDemo {
    public static void main(String[] args) {
        //获取一个MyTransformer实例
        MyTransformer myTransformer = (MyTransformer) MyTransformer.getInstance("trans-value");
        //创建map集合
        HashMap map = new HashMap();
        map.put("key1", "value1");
        map.put("key2", "value2");
        System.out.println(map);
        System.out.println("=========================");
        // 将 MyTransformer实例作为 valueTransformer 绑定到 map 上
        // 即当 map 的 value 更改时,自动调用 MyTransformer 的 transform 方法
        Map transformedMap = TransformedMap.decorate(map, null, myTransformer);

        // 获取map中的第一组元素
        // 将键值对封装成一个对象, iterator() 获取迭代器 next()获取第一对值
        Map.Entry entry = (Map.Entry) transformedMap.entrySet().iterator().next();
        System.out.println(entry.toString());

        // 更改 map 的 value 来触发 MyTransformer 的 tranform 方法
        entry.setValue("newValue");
        System.out.println(map); // {key1=trans-value, key2=value2}

    }
}

输出结果

1667287342100

  • Map.Entry是Map声明的一个内部接口,此接口为泛型,定义为Entry<K,V>。它表示Map中的一个实体(一个key-value对),Map.Entry里有相应的getKey和getValue方法,让我们能够从一个项中取出Key和Value。

    • Map.Entry的作用
    • Map.Entry是为了更方便的输出map键值对。一般情况下,要输出Map中的key 和 value 是先得到key的集合keySet(),然后再迭代(循环)由每个key得到每个value。values()方法是获取集合中的所有值,不包含键,没有对应关系。而Entry可以一次性获得这两个值。
HashMap<String,String> hashMap = new HashMap<String, String>();
        hashMap.put("1","aa");
        hashMap.put("2","bb");
        hashMap.put("3","c");
        //方法1,由于二次取值,效率比方法2,方法3慢一倍
        for(String key : hashMap.keySet()){
            System.out.println(key+"--------"+hashMap.get(key));
        }
 
        /**
         * Map.entrySet迭代器会生成EntryIterator,其返回的实例是一个包含key/value键值对的对象。
         * 而keySet中迭代器返回的只是key对象,还需要到map中二次取值。故entrySet要比keySet快一倍左右。
         */
        Iterator<Map.Entry<String,String>> it = hashMap.entrySet().iterator();
        while (it.hasNext()){
            Map.Entry<String,String> entry = it.next();
            System.out.println(entry.getKey()+"--------"+entry.getValue());
        }
 
        //第三种:无法在for循环时实现remove等操作 
        System.out.println("通过Map.entrySet遍历key和value");
        for(Map.Entry<String,String> entry : hashMap.entrySet()){
            System.out.println("key="+entry.getKey()+" and value="+entry.getValue());
        }

Map.entrySet() 将Map集合转换成set集合。Map.entrySet迭代器会生成EntryIterator,其返回的实例是一个包含key/value键值对的对象。而keySet中迭代器返回的只是key对象,还需要到map中二次取值。故entrySet要比keySet快一倍左右。

最后修改:2022 年 11 月 11 日
如果觉得我的文章对你有用,请随意赞赏