简单的概述一下全过程:
首先分为三端:
- registry端
- server端
- client端
registry作为一个中间人,
server端使用bind或者rebind和registry进行交互,在交互过程中使用tcp协议进行通信,并且在invoke方法时,进行序列化和反序列化
client端使用look和registry进行交互,通过在invoke方法中进行序列化和反序列化
实际上,server和client是没有进行直接交互的。
server 过程全部的server内容:
public class Server {
public static void main(String args[]) throws Exception{
//以1099作为LocateRegistry接收客户端请求的端口,并注册服务的映射关系
Registry registry= LocateRegistry.createRegistry(1099);
IOperation iOperation=new OperationImpl();
Naming.rebind("rmi://127.0.0.1:1099/Operation",iOperation);
System.out.println("service running...");
}
}
server在启动后会进行顺序启动:
1.开启createRegistry首先是createRegistry
- javarmiregistryLocateRegistry.java
跟进 RegistryImpl
jetbrains://idea/navigate/reference?project=rmi_demo&path=D:/jdk1.8.0_152/jre/lib/rt.jar!/sun/rmi/registry/RegistryImpl.class
public RegistryImpl(final int var1) throws RemoteException {
this.bindings = new Hashtable(101);
//判断端口是否是1099,也就是默认端口
if (var1 == 1099 && System.getSecurityManager() != null) {
try {
AccessController.doPrivileged(new PrivilegedExceptionAction() {
public Void run() throws RemoteException {
LiveRef var1x = new LiveRef(RegistryImpl.id, var1);
RegistryImpl.this.setup(new UnicastServerRef(var1x, (var0) -> {
return RegistryImpl.registryFilter(var0);
}));
return null;
}
}, (AccessControlContext)null, new SocketPermission("localhost:" + var1, "listen,accept"));
} catch (PrivilegedActionException var3) {
throw (RemoteException)var3.getException();
}
} else {
//创建LiveRef对象
LiveRef var2 = new LiveRef(id, var1);
//关键点:
this.setup(new UnicastServerRef(var2, RegistryImpl::registryFilter));
}
}
接着来看setup
jetbrains://idea/navigate/reference?project=rmi_demo&path=D:/jdk1.8.0_152/jre/lib/rt.jar!/sun/rmi/registry/RegistryImpl.class
跟进exportObject
public Remote exportObject(Remote var1, Object var2, boolean var3) throws RemoteException {
Class var4 = var1.getClass();
Remote var5;
try {
//创建了一个Registry_stub
var5 = Util.createProxy(var4, this.getClientRef(), this.forceStubUse);
} catch (IllegalArgumentException var7) {
throw new ExportException("remote object implements illegal remote interface", var7);
}
if (var5 instanceof RemoteStub) {
//将输入的配置和stub绑定
this.setSkeleton(var1);
}
Target var6 = new Target(var1, this, var5, this.ref.getObjID(), var3);
//以当前配置,开启一条线程,监听ip+port
this.ref.exportObject(var6);
this.hashToMethod_Map = (Map)hashToMethod_Maps.get(var4);
return var5;
}
2.绑定registry
首先这里是rebind,不管是bind还是rebind还是look,都要触发的是newCall
public void rebind(String var1, Remote var2) throws AccessException, RemoteException {
try {
RemoteCall var3 = this.ref.newCall(this, operations, 3, 4905912898345647071L);
try {
ObjectOutput var4 = var3.getOutputStream();
var4.writeObject(var1);
var4.writeObject(var2);
} catch (IOException var5) {
throw new MarshalException("error marshalling arguments", var5);
}
//在invoke这里进行数据传递
this.ref.invoke(var3);
this.ref.done(var3);
} catch (RuntimeException var6) {
throw var6;
} catch (RemoteException var7) {
throw var7;
} catch (Exception var8) {
throw new UnexpectedException("undeclared checked exception", var8);
}
}
newCall
newCall实际上就是创建了一个交互连接,没有进行实际数据传递。
public RemoteCall newCall(RemoteObject var1, Operation[] var2, int var3, long var4) throws RemoteException {
clientRefLog.log(Log.BRIEF, "get connection");
//建立了一个tcp的连接
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;
} catch (RemoteException var10) {
this.ref.getChannel().free(var6, false);
throw var10;
}
}
StreamRemoteCall中进行判断
jetbrains://idea/navigate/reference?project=rmi_demo&path=D:/jdk1.8.0_152/jre/lib/rt.jar!/sun/rmi/transport/StreamRemoteCall.class
在这里进入2之后就会进行readObject
public class Client {
public static void main(String args[]) throws Exception{
IOperation iOperation= (IOperation) Naming.lookup("rmi://127.0.0.1:1099/Operation");
System.out.println(iOperation.add(1,1));
}
}
这里首先进入的newcall,newcall和rebind的一致。流程也大差不差。 简单的理解就是,首先和registry进行交互,然后确定好之后发送序列化数据,然后就会收到registry的数据,然后当client数据对存在问题的代码进行了反序列化漏洞就会触发。
各种福利可咨询:570714949



