RMI流程分析

说明

将就着看吧,代码块太多了,打不过来了o.O

环境

OpenJDK 8u392 https://mirrors.tuna.tsinghua.edu.cn/Adoptium/8/jdk/x64/windows/
Oracle jdk 8u65
sun包 https://github.com/unofficial-openjdk/openjdk/tree/jdk8u/jdk8u
sun包 https://hg.openjdk.org/jdk8/jdk8/jdk/file/687fd7c7986d

Tips

Decompiled.class file, bytecode version:52.0 (Java 8) 说明没有java源文件,也就是src.zip里边没有这个Java文件,所以需要下载sun包
Library source does not match the bytecode for class 说明源文件和class文件不对应,上边两个sun包结合着用

RMI(Remote Method Invocation)的流程

RMI 是 Java 中用于实现远程过程调用(RPC)的机制。其中有三个重要的角色:服务端(Server)、注册中心(Registry)、客户端(Client)。
在这里插入图片描述
以下是简化的 RMI 流程:

服务端(Server):
创建远程对象(Remote Object)并实现远程接口。
实例化注册表(Registry)
将远程对象绑定到注册表中,以便客户端能够查找和调用。

注册中心(Registry):
运行在指定的端口上,将服务端绑定的远程对象注册到注册表中。
等待客户端和服务端的连接。

客户端(Client):
查找注册表,获取对远程对象的引用。
通过获得的引用,调用远程对象的方法,就像调用本地对象一样。

RMI 中还有 Stub(客户端存根)和 Skeleton(服务端骨架)两个概念,客户端和服务端都通过 Stub 和 Skeleton 进行网络通信。

流程图

Client Stub Registry Skeleton Server 远程对象(remoteObj) 1、创建远程对象 2、创建注册中心 3、把远程对象 绑定(bind)到 注册中心 4、获取注册中心 Registry_Stub 5、Registry_Stub 和 Registry_Skeleton 通信,通过 name 查找远程对象 6、Registry_Skel 返回远程对象(remoteObj)的代理给Registry_Stub 6、返回给客户端 7、调用远程对象(remoteObj) 8、服务端调用远程对象 10、执行结果返回给Server 11、Server返回给Client Client Stub Registry Skeleton Server 远程对象(remoteObj)

代码分析

Demo代码

定义 IRemoteObj 接口
    进行远程方法调用的类,需要实现一个继承Remote接口的接口,远程方法要抛RemoteException。

public interface IRemoteObj extends Remote {
    public String sayHello(String keywords) throws RemoteException;
}

创建 IRemoteObj 接口的实现类
    实现类需要继承UnicastRemoteObject,初始化时会自动将 RemoteObjImpl 发布出去。

public class RemoteObjImpl extends UnicastRemoteObject implements IRemoteObj {
    public RemoteObjImpl() throws RemoteException{
    }
    @Override
    public String sayHello(String keywords) {
    	//转换大写
        String upKeywords = keywords.toUpperCase();
        System.out.println(upKeywords);
        return upKeywords;
    }
}

创建服务端

public class RMIServer {
    public static void main(String[] args) throws RemoteException, AlreadyBoundException {
    	//创建对象并发布 (RemoteObjImpl 继承 UnicastRemoteObject,所以初始化时会自动发布出去)
        IRemoteObj remoteObj = new RemoteObjImpl();
        //创建注册中心
        Registry r = LocateRegistry.createRegistry(1099);
        //将远程对象绑定到注册中心
        r.bind("remoteObj",remoteObj);
    }
}

创建客户端

public class RMIClient {
    public static void main(String[] args) throws RemoteException, NotBoundException {
    	//获取注册中心
        Registry registry = LocateRegistry.getRegistry("127.0.0.1",1099);
        //通过注册中心查找远程对象
        IRemoteObj remoteObj = (IRemoteObj) registry.lookup("remoteObj");
        //调用远程对象上的方法
        remoteObj.sayHello("hello");
    }
}

服务端调试

流程图

UnicastRemoteObject UnicastServerRef LiveRef TCPEndpoint TCPTransport Util Target Transport 1、exportObject(obj, new UnicastServerRef(port)) 2、new LiveRef(port) 3、TCPEndpoint.getLocalEndpoint(port) 4、new TCPTransport() 5、return ep UnicastServerRef包含的就是LiveRef 6、sref.exportObject() 7、stub = Util.createProxy() 创建客户端存根 8、target = new Target(impl, this, stub, ref.getObjID(), permanent) 把远程对象、stub等封装成target 9、ref.exportObject(target) 10、ep.exportObject(target) 11、transport.exportObject(target) 12、listen() 分配一个随机端口,打开socket,监听客户端的请求 13、super.exportObject(target) 把target保存在ObjectTable的静态变量objTable和implTable里 UnicastRemoteObject UnicastServerRef LiveRef TCPEndpoint TCPTransport Util Target Transport

创建远程对象

UnicastRemoteObject

打断点进行调试
开始调试

调用父类UnicastRemoteObject的构造方法,然后调用exportObject方法,注意这里并没有返回值,只是把对象发布出去
UnicastRemoteObject

调用返回值是 Remote 类型的exportObject方法
UnicastRemoteObject

UnicastServerRef

进入 UnicastServerRef 类,这是一个负责将远程对象发布的类。可以理解为 UnicastServerRef 是服务端对象的引用,所以 UnicastRef 是客户端对象的引用。
OpenAI

UnicastServerRef

LiveRef

进入LiveRef
LiveRef

TCPEndpoint

进入LiveRefthis方法,看到TCPEndpoint
LiveRef

通过查询发现 TCPEndpoint 这个类主要是用作网络通信,跟进TCPEndpoint#getLocalEndpoint方法

在这里插入图片描述
最后的返回 ep 里主要内容就是 IP 地址和 port ,说明TCPEndpoint类中主要内容是 IP 地址和 port ,也就是说LiveRef中存放的也是 IP 和 port
TCPEndpoint

public LiveRef(ObjID objID, int port) {
        this(objID, TCPEndpoint.getLocalEndpoint(port), true);
    }
UnicastRef

走到UnicastServerRef类中,获取到 IP 和 port 信息,刚才我们进入的是LiveRef类,继续往下调试,执行 super 父类 UnicastRef 的构造方法
UnicastServerRef

一个赋值,把 LiveRef 赋值到 UnicastRef 的成员变量 ref 里
UnicastRef

继续往下走,会回到 UnicastRemoteObject 类中
在这里插入图片描述

目前其实就创建了一个UnicastServerRef,它的 ref 是一个 LiveRef ,ref 里面有一个 TCPEndpoint 叫 ep ,ep 里面有个 TCPTransport 叫transport。
以上说明,LiveRef、TCPEndPoint主要处理网络通信(IP、port),LiveRef里有TCPEndPoint,TCPEndPoint里有TCPTranspoint,而UnicastServerRef 是把 LiveRef 又封装了一下,可以理解为 UnicastServerRef 是远程对象的引用。

继续调试,把 UnicastServerRef 赋值到 obj.ref
在这里插入图片描述

UnicastServerRef

执行 UnicastServerRef#exportObject
UnicastServerRef#exportObject
出现了客户端 stub ,createProxy 很明显这是一个代理类,先跟进getClientRef
UnicastServerRef#getClientRef
新建了一个 UnicastRef 把刚才的 LiveRef 放了进去

Util

跟进Util#createProxy方法,走到 if 判断
Util#createProxy

判断是否存在类名为 远程对象_Stub (RemoteObjImpl_Stub) 名称的类,如果存在就调用createStub直接实例化一个
Util#createStub

很显然 远程对象是我们自己创建的,并没有这个类
Util#stubClassExists

所以会执行到创建动态代理这一步,调用 RemoteObjectInvocationHandler 远程对象调用处理器
在这里插入图片描述

把新创建的 UnicastRef 放了进去,之前创建的远程对象里有个 UnicastServerRef,这个 stub 里面有个 UnicastRef,但它们两个里面是同一个 LiveRef
在这里插入图片描述
把 UnicastRef 放到 RemoteObject 类的成员变量 ref 中
在这里插入图片描述

之后 newProxyInstance 创建代理
Util#createProxy

UnicastServerRef

之后把远程对象、UnicastServerRef、stub、ref.ObjID封装到 Target 中,此时的 stub 中的端口还是 0
UnicastServerRef#exportObject

LiveRef#exportObject

之后调用 LiveRef#exportObject 方法
在这里插入图片描述

TCPEndpoint#exportObject

调用 TCPEndpoint#exportObject
在这里插入图片描述

TCPTransport#exportObject

调用 TCPTransport#exportObject
在这里插入图片描述

看到一个 listen 方法,跟进看看是不是要打开一个端口
在这里插入图片描述
看样子应该是要开启一个 socket 和线程,进入 newServerSocket
在这里插入图片描述
如果端口为空,设置一个随机端口,这个端口是远程服务发布出去的端口,此时 TCPEndpoint 中就有 port 值了
在这里插入图片描述
把 newServerSocket 的值赋值给 TCPTransport 类的成员变量 server
然后新建一个线程,发步出去。
个人理解到这里其实就是开启了一个socket接口,等待后续客户端的连接在这里插入图片描述

Transport#exportObject

继续调试,发现又执行到 Transport#exportObject
在这里插入图片描述
把 TCPTransport 保存到 Target 类中的成员变量 exportedTransport 中
在这里插入图片描述
下一步执行 ObjectTable.putTarget
在这里插入图片描述
在这里插入图片描述
在ObjectTable类中 通过 ObjID(id) 和 TCPTransport(exportedTransport) 创建一个 ObjectEndpoint 类型的返回值 oe ,下一句的target.getWeakImpl() 也是类似,把这两个值和 target 对应,存放到 objTable 和 implTable 中,留到后续注册中心到 objTable 中查找 target,到这一步,服务端创建远程对象就完成了
总结一下:
Target里保存了:远程对象、UnicastServerRef、stub、objID、还有一个名为 exportedTransport 的值,里边保存的是TCPTransport
    远程对象里有:UnicastServerRef
    stub里有:UnicastRef
    UnicastServerRef 和 UnicastRef 中都保存了同一个 LiveRef
最后注册到ObjectTable中

创建注册中心

在这里插入图片描述
LocateRegistry#createRegistry
在这里插入图片描述

RegistryImpl

跟进 RegistryImpl 构造函数,发现又是 new LiveRef(),只是端口是 RMI服务的端口,默认是1099
最后跟下来,和创建远程对象时的流程一样,LiveRef–>TCPEndpoint–>TCPTrancepoint
继续往下执行,来到setup(),这里和前面创建远程对象的地方一样,创建远程对象时,也是实例化了一个 UnicastServerRef ,值是LiveRef,又把值赋给了 UnicastRef 的成员变量 ref
在这里插入图片描述

进入setup
在这里插入图片描述
把 UnicastServerRef 赋值给 ref

UnicastServerRef

执行 UnicastServerRef#exportObject
UnicastServerRef#getClientRef 还是创建一个 UnicastRef
在这里插入图片描述

Util#createStub

又到了创建 stub 的地方,这个地方和创建远程对象的的时候不太一样,跟进去
在这里插入图片描述
在这个判断本地是否有 RegistryImpl_Stub 这个名字的对象时,jdk里是有这个对象的
在这里插入图片描述
sun.rmi.registry.RegistryImpl_Stub
在这里插入图片描述
然后通过反射的方式实例化这个 stub 对象
在这里插入图片描述
在这里插入图片描述

UnicastServerRef

RegistryImpl_Stub 继承了 RemoteStub ,所以会走到 setSkeleton 在这里插入图片描述
继续跟进
在这里插入图片描述

Util#createSkeleton

创建Skeleton。还是通过反射的机制,实例化 sun.rmi.registry.RegistryImpl_Skel 对象
在这里插入图片描述

UnicastServerRef

现在 RegistryImpl 对象中 UnicastServerRef 中多了一个skel,继续往下走
在这里插入图片描述
接下来就和发布远程对象是一样的了,封装成 target ,开启 socket ,然后把 target 放到 objTable 和 implTable 中。

把远程对象绑定到注册中心

最后一步 绑定
在这里插入图片描述
把远程对象和名字,放到 Hashtable 中
在这里插入图片描述

客户端调试

获取注册中心

在这里插入图片描述
跟进 LocateRegistry#getRegistry
在这里插入图片描述
通过 IP 和 port 新建了一个LiveRef对象 ,又用 liveRef 新建一个 UnicastRef 对象,然后又调用 Util#createProxy( RegistryImpl.class )
在这里插入图片描述
最后通过反射机制,创建一个 RegistryImpl_Stub 对象
在这里插入图片描述
和注册中心的 RegistryImpl_Stub 不一样的是:
    注册中心的的 ref 是new UnicastServerRef()
    而客户端的 ref 是new UnicastRef()
在这里插入图片描述

客户端获取注册中心这一步,是直接通过 host 和 port 在本地生成的。

查找远程对象

通过注册中心获取远程对象的动态代理
在这里插入图片描述
跟进 registry#lookup
在这里插入图片描述
调用UnicastRef#newCall发起一个请求和注册中心建立连接
在这里插入图片描述
把远程对象名称序列化写入输出流,然后调用UnicastRef#invoke
在这里插入图片描述
调用StreamRemoteCall#executeCall
在这里插入图片描述
当执行到 StreamRemoteCall#releaseOutputStrem() ,刷新输出流,此时客户端已经发送远程对象的名字到注册中心,而且注册中心已经处理完成,把远程对象的动态代理返回(分析注册中心时会分析具体的流程)
在这里插入图片描述
在这里插入图片描述

在这里有一个异常存在潜在攻击的可能性,如果服务端返回异常,会对输入流(服务端返回的流)进行反序列化
在这里插入图片描述
也就是说,只要调用了 StreamRemoteCall#executeCall就可能被攻击,再往上分析,就是只要调用 UnicastRef#invoke 就会被攻击,也就是说只要客户端调用 registry#lookup(RegistryImpl_Stub#lookup)就有可能会被攻击,所以这块可以通过注册中心攻击客户端
在这里插入图片描述
继续往下跟进,获取输入流(服务端返回的流),如果没有异常,就返回到 RegistryImpl_Stub#lookup
在这里插入图片描述
在 RegistryImpl_Stub#lookup 中,还有一个存在被攻击的点,当客户端获取到 in 流(远程对象的代理)时,会对流进行 readObject 反序列化,所以这块也可以通过注册中心攻击客户端
在这里插入图片描述
之后在 RegistryImpl_Stub#lookup 会执行一个 UnicastRef#done
在这里插入图片描述
在执行完一系列的方法时,客户端会把获取到的动态代理发送给服务端
在这里插入图片描述
数据包
在这里插入图片描述
总结:
1、注册中心可以通过返回异常,攻击客户端
2、注册中心可以通过返回恶意对象,让客户端反序列化,攻击客户端

调用远程对象上的方法

在这里插入图片描述
因为获取到的是一个动态代理(远程对象的stub),所以先执行 RemoteObjectInvocationHandler#invoke
在这里插入图片描述
执行RemoteObjectInvocationHandler#invokeRemoteMethod
在这里插入图片描述
执行UnicastRef#invoke,建立连接,获取输出流
在这里插入图片描述
进入 UnicastRef#marshalValue 这个方法
在这里插入图片描述
判断type是否属于基本数据类型,我们传递的是 string ,对传入的值 hello 进行序列化
在这里插入图片描述
继续往下,调用了executeCall,那么这里客户端还有可能被攻击,通过异常处理 返回一个恶意的对象,客户端进行反序列化
在这里插入图片描述
跟进 StreamRemoteCall#executeCall 会把我们传过去的参数值 hello 序列化给服务端,服务端返回 HELLO
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

继续往下,如果返回值类型不是 void.class ,会执行 UnicastRef#unmarshalValue ,跟进,在老版本中,没有 else if 这个判断,会直接到

return in.readObject()

判断type是否属于基本数据类型,如果不是,反序列化 输入流(服务端的返回结果) ,所以这块服务端可以通过返回恶意对象来攻击客户端
在这里插入图片描述
总结:
1、服务中心通过返回异常,攻击客户端
2、服务中心通过返回恶意对象,攻击客户端

注册中心调试

查找远程对象

注册中心根据客户端传过来的 name 查找远程对象
发布远程对象时会调用TCPTransprt#listen,实际上listen里面开启了一个AcceptLoop线程,接收到网络请求时会调用它的run方法
AcceptLoop 是一个非静态内部类,只能通过 TCPTransprt 类的内部或者 TCPTransprt 的实例来访问
在这里插入图片描述
然后调用 executeAcceptLoop ,走到 ConnectionHandler,然后调用它的 run 方法
ConnectionHandler 主要目的是处理与客户端或其他服务之间的通信,在 Java 中,常见的情况是使用 Socket 进行连接处理
所以断点可以打在 new ConnectionHandler() 处,和它的 run 方法处
在这里插入图片描述
在这里插入图片描述
逻辑上来说,target 放到了 objTable 中,客户端获取到的是 target 中的 stub 动态代理,所以注册中心应该有一步是从 objTable 中获取 target 进行处理,所以全局搜索 objtable.get ,然后查看都是什么地方调用的这个方法
在这里插入图片描述
查到有三个地方调用了 objTable.get 方法,第一个是返回 target ,有点像处理的过程,查看 ObjectTable#getTarget 的调用关系,第一个是 Transport#serviceCall,ConnectionHandler 这个内部类是在 TCPTransport 中,而 Transport 是 TCPTransport 的父类,应该是对应上了,查看 Transport#serviceCall 的调用关系
在这里插入图片描述
所以当客户端发送请求到注册中心后,注册中心的获取 target 代码处理流程应该是:

new ConnectionHandler -> run() -> run0() -> handleMessages() -> Transport#serviceCall() -> ObjectTable.getTarget()

进行调试,到 handleMessages在这里插入图片描述
跟进到 serviceCall(call)
在这里插入图片描述

读取id,根据 id 和 transport 到 ObjectTable 中查找对应的Target ,得到注册中心的 Target
获取 Target 中的远程引用disp
在这里插入图片描述

然后调用disp.dispatch,也就是UnicastServerRef#dispatch
在这里插入图片描述
判断 UnicastServerRef 里的 skel 是否为空,如果不为空,调用oldDispatch
这是注册中心的 UnicastServerRef ,里面是有 skel 的
在这里插入图片描述
调用RegistryImpl_Skel#dispatch
在这里插入图片描述
旧版本是没有RegistryImpl.checkAccess的,也就是可以远程调用所有方法。加上这个限制后只能远程调用lookup和list方法了,其他方法只能本地调用
在这里插入图片描述
旧版本这个地方,除了 list 方法,其他四种方法bind、rebind、unbind、lookup,都可以反序列化,都可以利用。
在这里插入图片描述
注册中心反序列化客户端传过来的远程对象名称,查找远程对象,把远程对象写入到输出流
在这里插入图片描述
但是客户端获取到的是 远程对象的动态代理 。这是因为 out 序列化时会调用到 MarshalOutputStream#replaceObject ,所以输出的就是远程对象的 stub
在这里插入图片描述
总结:
    客户端可以通过传输恶意的参数值攻击注册中心

服务端调用方法

客户端获取到远程对象的代理时,连接服务端,服务端调用方法。
和注册中心查询远程对象一样,都会先获取 Target,还在 Transport#serviceCall 处下断点
在这里插入图片描述
第一次获取的是 DGCImpl_Stub 这是Java中垃圾回收机制,继续调试,获取到远程对象的target
在这里插入图片描述
获取 target 中的 disp(UnicastServerRef),远程对象的 UnicastServerRef中没有 skel ,所以会跳过 oldDispatch
在这里插入图片描述
继续跟进到 UnicastServerRef#unmarshalParameters ,获取参数
在这里插入图片描述
这个地方之前分析过,UnicastRef#unmarshalValue 会造成反序列化,所以这个地方客户端可以通过传入的参数来攻击服务端
在这里插入图片描述
最后执行 sayhello(hello)
在这里插入图片描述
走到这一步,服务端把结果输出到服务端
在这里插入图片描述
个人理解,在服务端执行方法调用时,并没有和所谓的 skeleton 通信,而是在 UnicastServerRef 对象的引用中处理。

Transport#serviceCall->UnicastServerRef#dispatch->if(skel != null)->UnicastServerRef#unmarshalParameters

DGC 垃圾回收

服务端调用流程

创建

在我们创建远程对象保存到 ObjTable 中时,DGC 对象是第一个保存进去的
在 ObjectTablet#putTarget 方法处
在这里插入图片描述
调用了DGCImpl的静态变量dgcLog,在这个过程中会对类进行初始化,初始化的过程中静态代码块中的内容会自动执行
在这里插入图片描述
可以看到,创建了一个 target 对象,和创建注册中心的逻辑一样,有 UnicastServerRef 、 stub 等信息,它的 UnicastServerRef 里也有 skel,然后存放到 objtTable 中
在这里插入图片描述
它和远程服务用的是同一个端口,DGC的服务端就创建完成了

通信流程

和 注册中心 一样,服务端也调用了in.readObject,也就是说DGC客户端可以攻击DGC服务端
在这里插入图片描述

客户端调用流程

在客户端调试 RegistryImpl_Stub#lookup 时,等客户端反序列化完服务端传过来的动态代理时, 执行完
(ref.done())UnicastRef#done -> StreamRemoteCall#getServerException -> StreamRemoteCall#releaseInputStream.done() 后,客户端会给服务端发几个包,不太明白是干啥的,发现里边有和 DGC 相关的内容,专门调试一下
在这里插入图片描述
ref.done() 打断点
在这里插入图片描述
跟进到StreamRemoteCall#releaseInputStream,在ConnectionInputStream#registerRefs()发现了DGCClient.registerRefs()
在这里插入图片描述
继续跟进,到do..while()循环,打上断点,因为当执行完循环体内的代码后,还会再执行循环条件
在这里插入图片描述
DGCClient#EndpointEntry 处,发现开始和客户端创建 RegistryImpl_Stub 时一样的逻辑,此时DGC LiveRef和服务端远程对象使用的同一个端口
在这里插入图片描述
通过反射的方式,创建客户端 DGC_Stub
在这里插入图片描述
继续往下跟进,发现了一个跟线程有关的对象,new RenewCleanThread()
在这里插入图片描述
跟进,跟线程相关的断点都要打到它的 run() 方法内才能进去,一路跟到 PrivilegedAction#run() 方法,发现打了断点,程序偶尔会在这个地方停止,但是会报错
在这里插入图片描述
大多数的情况都会回到刚才do..while()处,执行DGCClient#registerRefs(refs)
在这里插入图片描述
不管是执行哪个方法,都是会执行DGCClient#makeDirtyCall方法
在这里插入图片描述
然后执行DGCClient_Stub#dirty()方法,此处可以发现客户端会对服务端返回的数据进行反序列化,所以DGC客户端会被DGC服务端攻击
在这里插入图片描述

总结

流程分析

1、服务端
        创建远程对象:创建 UnicastServerRefstub(Proxy) ,把对象UnicastServerRefstub 等值封装到target
        发布远程对象:LiveRef#exportObject -> TCPEndpoint#exportObjec -> TCPTransport#exportObjec -> TCPTransport#listen创建socket分配端口 -> AcceptLoop开启线程监听
        绑定远程对象:ObjectTable#putTarget
2、注册中心
        创建、发布、绑定注册中心:和远程对象大致流程一样,比远程对象的UnicastServerRef中多了一个skel值,值是 RegistryImpl_Skel对象,注册中心的stub RegistryImpl_Stub
3、客户端
        创建注册中心:通过 IP和 port ,本地创建一个和注册中心一样的 RegistryImpl_Stub,剩下的都是和注册中心、远程对象服务端通信的过程

通信分析

1、客户端
        查找远程对象:通过RegistryImpl_Stub#lookup去注册中心查找远程对象的 stub(Proxy)
        调用远程对象的方法:主要逻辑都在UnicastRef
2、注册中心
        查找远程方法:服务监听new ConnectionHandler -> run() -> run0() -> handleMessages() -> Transport#serviceCall() -> ObjectTable.getTarget() -> UnicastServerRef#dispatch -> UnicastServerRef#oldDispatch -> RegistryImpl_Skel#dispatch返回客户端
3、服务端
        执行方法:Transport#serviceCall() -> ObjectTable.getTarget() -> UnicastServerRef#dispatch -> UnicastServerRef#unmarshalParameters -> Method#invoke
        主要逻辑都在UnicastServerRef

反序列化点

  1. 攻击客户端
    • StreamRemoteCall#executeCall -> 服务端/注册中心 通过返回异常攻击客户端
    • RegistryImpl_Stub#lookup -> 注册中心通过返回恶意对象攻击客户端
    • UnicastRef#unmarshalValue -> 服务端通过返回恶意对象攻击客户端
    • DGCClient_Stub#dirty->服务端攻击客户端
  2. 攻击服务端
    • UnicastRef#unmarshalValue->客户端通过传入恶意参数攻击服务端
    • DGCImpl_Skel#dispatch->客户端攻击服务端
  3. 攻击注册中心
    • RegistryImpl_Skel#dispatch->客户端/服务端通过传入恶意参数,在bind、lookup、rebind、unbind处攻击注册中心

参考

白日梦组长B站视频
白日梦组长博客

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值