当前位置: 首页 > news >正文

【Java安全】RMI基础

文章目录

    • 介绍
    • 实现
      • 服务端 Server
      • 客户端 Client
    • 通信过程
      • 数据端与注册中心(1099 端口)建立通讯
      • 客户端与服务端建立 TCP 通讯
      • 客户端序列化传输 调用函数的输入参数至服务端
      • 总结

介绍

RMI 全称 Remote Method Invocation(远程方法调用),即在一个 JVM 中 Java 程序调用在另一个远程 JVM 中运行的 Java 程序,这个远程 JVM 既可以在同一台实体机上,也可以在不同的实体机上,两者之间通过网络进行通信。

RMI的一般要用到的组件:

  • Remote Interface:远程接口

需要定义一个接口,继承自 java.rmi.Remote,表明可以被远程对象调用的方法。
远程调用可能发生网络异常 , 所以每个方法都必须显式抛出 RemoteException

  • Remote Object Implementation:远程接口的具体实现

一般需要继承UnicastRemoteObject类, 将对象导出成一个 可以通过 TCP 调用的远程对象

  • Server:服务端,注册远程对象到 RMI 注册中心。
  • Client:客户端,查找远程对象并调用其方法。
  • Registry:注册端提供服务注册与服务获取。即 Server 端向 Registry 注册服务,比如地址、端口等一些信息,Client 端从 Registry 获取远程对象的一些信息,如地址、端口等,然后进行远程调用。

实现

服务端 Server

定义远程接口

package RMI.Server;import java.rmi.Remote;
import java.rmi.RemoteException;public interface Hello extends Remote {public String sayHello(String name) throws RemoteException;
}

远程接口的实现

package RMI.Server;import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;public class HelloImpl extends UnicastRemoteObject implements Hello {public HelloImpl() throws RemoteException {super(); //也可以什么都不写,隐式调用//如果没有继承UnicastRemoteObject,就需要手动导出: UnicastRemoteObject.exportObject(this, 0); }@Overridepublic String sayHello(String name) throws RemoteException {return "Hello " + name;}
}

服务端

主要是创建 RMI 注册表(使用默认端口 1099),创建服务实现类的实例,将远程对象绑定到注册表中

package RMI.Server;import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;public class RMIServer {public static void main(String[] args) {try{//实例化远程对象HelloImpl obj = new HelloImpl();//启动本地的RMI注册服务(一般默认 1099 端口),创建注册中心LocateRegistry.createRegistry(1099);Registry registry = LocateRegistry.getRegistry();//绑定远程对象registry.bind("HelloImpl", obj);//或者import java.rmi.Naming;//Naming.bind("rmi://127.0.0.1:1099/HelloImpl", obj);}catch (Exception e){e.printStackTrace();}}
}

客户端 Client

连接到本地(localhost)的 RMI 注册表然后查找相应名字的远程对象,最后调用远程方法,传入相应参数

package RMI.Client;import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import RMI.Server.Hello; // 导入服务器端的远程接口public class RMIClient {public static void main(String[] args) throws Exception {//连接到服务器Registry registry = LocateRegistry.getRegistry("localhost", 1099);//通过名字查找远程对象Hello hello = (Hello) registry.lookup("HelloImpl");//调用远程对象上面的方法String response = hello.sayHello("xpw");System.out.println("response :"+response);}}

先运行服务端, 再运行客户端, 在客户端就可以看到调用了远程对象的方法了

在这里插入图片描述

通信过程

很多复制粘贴的来自其他师傅的博客,了解了一下内部通信的知识,还没有动手去尝试抓包

数据端与注册中心(1099 端口)建立通讯

  • 客户端查询需要调用的函数的远程引用,注册中心返回远程引用和提供该服务的服务端 IP 与端口。

在这里插入图片描述

客户端与注册中心(1099 端口)建立通讯完成后,客户端 向注册中心发送了⼀个 “Call” 消息,注册中心回复了⼀个 “ReturnData” 消息,然后客户端新建了⼀个 TCP 连接,连到服务端的 33769 端⼝

在这里插入图片描述

AC ED 00 05是常见的 Java 反序列化 16 进制特征
注意以上两个关键步骤都是使用序列化语句

客户端与服务端建立 TCP 通讯

客户端发送远程引用给服务端,服务端返回函数唯一标识符,来确认可以被调用

在这里插入图片描述

同样使用序列化的传输形式

以上两个过程对应的代码是这两句

Registry registry = LocateRegistry.getRegistry("127.0.0.1", 1099);  
RemoteObj remoteObj = (RemoteObj) registry.lookup("remoteObj"); // 查找远程对象

这里会返回一个 Proxy 类型函数,这个 Proxy 类型函数会在我们后续的攻击中用到。

客户端序列化传输 调用函数的输入参数至服务端

  • 这一步的同时:服务端返回序列化的执行结果至客户端

在这里插入图片描述

以上调用通讯过程对应的代码是这一句

remoteObj.sayHello("hello");

可以看出所有的数据流都是使用序列化传输的,那必然在客户端和服务带都存在反序列化的语句。

总结

整个过程进⾏了两次TCP握⼿,也就是我们实际建⽴了两次 TCP连接。

第⼀次建⽴TCP连接是连接远端 ip 的1099端⼝,这也是我们在代码⾥看到的端⼝,⼆ 者进⾏沟通后,我向远端发送了⼀个“Call”消息,远端回复了⼀个“ReturnData”消息,然后我新建了⼀ 个TCP连接,连到远端的33769端⼝。

之所以是33769端口, 因为在“ReturnData”这个包中,返回了⽬标的IP地址,其后跟的⼀个字节 \x00\x00\x83\xE9 ,刚好就是整数 33769 的网络序列

所以捋一下整个的过程: 首先客户端连接Registry,并在其中寻找Name是HelloImpl的对象,这个对应数据流中的Call消息;然后Registry返回⼀个序列化的数据,这个就是找到的Name=HelloImpl的对象,这个对应数据流中的ReturnData消息;客户端反序列化该对象,发现该对象是⼀个远程对象,地址在 127.0.0.1:33769 ,于是再与这个地址建⽴TCP连接;在这个新的连接中,才执⾏真正远程 ⽅法调⽤,也就是 HelloImpl()

各个元素之间的关系

在这里插入图片描述

RMI Registry就像⼀个⽹关,他⾃⼰是不会执⾏远程⽅法的,但RMI Server可以在上⾯注册⼀个Name 到对象的绑定关系;RMI Client通过Name向RMI Registry查询,得到这个绑定关系,然后再连接RMI Server;最后,远程⽅法实际上在RMI Server上调⽤。

参考文章

代码审计社区 Java安全漫谈
https://drun1baby.top/2022/07/19/Java%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E4%B9%8BRMI%E4%B8%93%E9%A2%9801-RMI%E5%9F%BA%E7%A1%80/#Java-%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E4%B9%8B-RMI-%E4%B8%93%E9%A2%98-01-RMI-%E5%9F%BA%E7%A1%80
https://fushuling.com/index.php/2023/01/30/java%e5%ae%89%e5%85%a8%e7%ac%94%e8%ae%b0/
http://www.dtcms.com/a/269980.html

相关文章:

  • go go go 出发咯 - go web开发入门系列(二) Gin 框架实战指南
  • WiFi协议学习笔记
  • 点云的无监督语义分割方法
  • 寻找两个正序数组的中位数(C++)
  • 成都算力租赁新趋势:H20 八卡服务器如何重塑 AI 产业格局?
  • 基于 Rust 的Actix Web 框架的应用与优化实例
  • C++ 选择排序、冒泡排序、插入排序
  • mac安装docker
  • APISEC安全平台
  • 嵌入式学习笔记-MCU阶段-DAY01
  • WPF之命令
  • 使用elasticdump高效备份与恢复Elasticsearch数据
  • WebSocket详细教程 - SpringBoot实战指南
  • EPLAN 电气制图(四):EPLAN 总电源电路设计知识详解
  • mit6.5840-lab3-3D-SnapShot-25Summer
  • 常见前端开发问题的解决办法
  • 深度学习——神经网络1
  • JK触发器Multisim电路仿真——硬件工程师笔记
  • HMI安全设计规范:ISO 26262合规的功能安全实现路径
  • python2.7/lib-dynload/_ssl.so: undefined symbol: sk_pop_free
  • 查询依赖冲突工具maven Helper
  • 常见的网络攻击方式及防御措施
  • 人工智能与人工智障———仙盟创梦IDE
  • Go HTTP 调用(上)
  • LeetCode 1248.统计优美子数组
  • cocos2dx3.x项目升级到xcode15以上的iconv与duplicate symbols报错问题
  • 云原生时代的日志管理:ELK、Loki、Fluentd 如何选型?
  • C++11 算法详解:std::copy_if 与 std::copy_n
  • UVC(USB Video Class,USB 视频类)协议
  • 代码详细注释:ARM-Linux字符设备驱动开发案例:LCD汉字输出改进建议开发板断电重启还能显示汉字,显示汉字位置自定义