java渗透测试基础之——RMI

2023-10-31

一、概述

RMI全称是Remote Method Invocation(远程方法调用),是专为Java环境设计的远程方法调用机制,远程服务器提供API,客户端根据API提供相应参数即可调用远程方法。

由此可见,使用RMI时会涉及到参数传递和结果返回,参数为对象时,要求对象可以被序列化。目的是为了让两个隔离的java虚拟机,如虚拟机A能够调用到虚拟机B中的对象,而且这些虚拟机可以不存在于同一台主机上。

RMI存在着三个主体:

  • RMI Registry

  • RMI Client

  • RMI Server

RMI中主要的api大致有:

  • java.rmi:提供客户端需要的类、接口和异常;

  • java.rmi.server:提供服务端需要的类、接口和异常;

  • java.rmi.registry:提供注册表的创建以及查找和命名远程对象的类、接口和异常;

二、举例

1、服务端

就服务端而言,需要提供远程对象给与客户端远程调用,所谓远程对象即实现java.rmi.Remote接口的类或者继承了java.rmi.Remote接口的所有接口的远程对象。

一个RMI Server分为三部分:

  • 部分1:一个继承了java.rmi.Remote 的接口,其中定义我们要远程调用的函数,比如这里的sayHello()

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface RemoteObj extends Remote {
    public String sayHello(String keywords) throws Exception;
}
  • 部分2:一个实现了此接口的类,首先有几个关键点:

    • 实现方法必须抛出RemoteException异常

    • 实现类需要同时继承UnicastRemoteObject类,如果不继承,则需要手工初始化远程对象,在远程对象的构造方法的调用UnicastRemoteObject.exportObject()静态方法

    • 只有在接口中声明的方法才能被调用到

import java.rmi.server.UnicastRemoteObject;

public class RemoteObjImpl extends UnicastRemoteObject implements RemoteObj {
    public RemoteObjImpl() throws Exception{
    }
    
    public String sayHello(String keywords) throws Exception {
        String upKeywrods = keywords.toUpperCase();
        System.out.println("Server:" + upKeywrods);
        return upKeywrods;
    }
}
  • 部分3:一个主类,用来创建Registry,并将上面的类实例化后绑定到一个地址。就服务端而言其实现的关键在于Naming这个类,利用bind方法将对象绑定一个名。

import java.rmi.Naming;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class RMIServer {
    public static void main(String[] args) throws Exception {
        RemoteObjImpl remoteObj = new RemoteObjImpl();
        Registry registry = LocateRegistry.createRegistry(1099);
//         写法一:通过Naming绑定
//        Naming.bind("remoteObj",remoteObj);
//         写法二:通过registry绑定
        registry.bind("remoteObj",remoteObj);
    }
}

2、客户端

客户端也需要具有接口类

import java.rmi.Remote;

public interface RemoteObj extends Remote {
    public String sayHello(String keywords) throws Exception;
}

客户端的操作可变性就很多了,同样是通过Naming类中提供的方法来操作,有如下几种方法:

  • lookup

  • list

  • bind

  • rebind

  • unbind

拿list举例子(可以列出目标上所有绑定的对象):

  • 我们可以通过list方法获取到目标server上有哪些绑定的方法

import java.rmi.Naming;

public class HelloRmiClientList {
    public static void main(String[] args) throws Exception {
        String[] clazz = Naming.list("rmi://127.0.0.1:1099");
        for (String s:clazz) {
            System.out.println(s);
        }
    }
}

用lookup举例子(获得某个远程对象):

  • 当上面获取到接口的方法时,用lookup可以执行远程方法,代码是在远程服务器器上执行,返回给客户端的为一个Remote对象,可以强转为我们本地的对象进行利用:

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

import java.rmi.Naming;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class RMIClient {
    public static void main(String[] args) throws Exception {
//        方法一:通过registry的lookup
//        Registry registry = LocateRegistry.getRegistry("127.0.0.1", 1099);
//        RemoteObj remoteObj = (RemoteObj)registry.lookup("remoteObj");
//        System.out.println("Client:" + remoteObj.sayHello("hello"));
//        方法二:通过Naming的lookup
        RemoteObj RemoteObj = (RemoteObj)Naming.lookup("rmi://127.0.0.1:1099/remoteObj");
        System.out.println("Client:" + RemoteObj.sayHello("hello"));
    }
}

Server的窗口:

Client的窗口:

在上述操作后,我们会发现Server输出了Server:HELLO,并且Client端也输出了Client:HELLO。到底怎么回事呢?

其实,在Server启动的时候,Server启动了一个RMI的注册中心,接着把RemoteObjImpl暴露并注册到RMI注册中心,其中存储着RemoteObjImpl的stub数据,包含有RemoteObjImpl所在服务器的ip和port。在Client启动之后,通过连接RMI注册中心,并从其中根据名称查询到了对应的对象(JNDI),并把其数据下载到本地,然后RMI会根据stub存储的信息,也就是Server中RemoteObjImpl实现暴露的ip和port,最后通过JRMP协议发起RMI请求,RMI后,Server输出大写之后的hello并通过JRMP协议把大写之后的hello的序列化数据返回给程序B,程序B对其反序列化后输出。

PS:java.rmi.registry.Registry 和 java.rmi.Naming 类之间的区别

不同之处在于Naming是一个具有静态方法的实用程序类,Registry而是一个远程接口。

请注意,传递给java.rmi.Naming中name的参数是 URL 格式,并包括注册表的位置,而使用java.rmi.registry.Registry,name只是名称。

例如,可以这样调用:

Naming.rebind("//host/objName", myObj);

而使用Registry,您需要注册表对象上的现有句柄,并且调用:

Registry registry = LocateRegistry.getRegistry("host");

registry.rebind("objName", myObj);

所以Naming实际上只是一个方便的类,它可以让您不必Registry手动查找 - 它一步执行注册表查找和重新绑定。

三、逻辑关系

1、基本逻辑关系

RMI Registry就像一个网关,他自己是不会执行远程方法的,但RMI Server可以在上面注册一个Name到对象的绑定关系;RMI Client通过Name向RMI Registry查询,得到这个绑定关系,然后再连接RMI Server;最后,远程方法实际上在RMI Server上调用。

在JVM之间通信时,RMI对远程对象和非远程对象的处理方式是不一样的,它并没有直接把远程对象复制一份传递给客户端,而是传递了一个远程对象的Stub,Stub基本上相当于是远程对象的引用或者代理。Stub对开发者是透明的,客户端可以像调用本地方法一样直接通过它来调用远程方法。Stub中包含了远程对象的定位信息,如Socket端口、服务端主机地址等等,并实现了远程调用过程中具体的底层网络通信细节,所以RMI远程调用逻辑是这样的:

从逻辑上来看,数据是在Client和Server之间横向流动的,但是实际上是从Client到Stub,然后从Skeleton到Server这样纵向流动的。

  • 1.Server端监听一个端口,这个端口是JVM随机选择的;

  • 2.Client端并不知道Server远程对象的通信地址和端口,但是Stub中包含了这些信息,并封装了底层网络操作;

  • 3.Client端可以调用Stub上的方法;

  • 4.Stub连接到Server端监听的通信端口并提交参数;

  • 5.远程Server端上执行具体的方法,并返回结果给Stub;

  • 6.Stub返回执行结果给Client端,从Client看来就好像是Stub在本地执行了这个方法一样;

2、Stub如何获取呢

Stub的获取方式有很多,常见的方法是调用某个远程服务上的方法,向远程服务获取存根。但是调用远程方法又必须先有远程对象的Stub,所以这里有个死循环问题。JDK提供了一个RMI注册表(RMIRegistry)来解决这个问题。RMIRegistry也是一个远程对象,默认监听在传说中的1099端口上,可以使用代码启动RMIRegistry,也可以使用rmiregistry命令。

  • 要注册远程对象,需要RMI URL和一个远程对象的引用

IHello rhello = newHelloImpl();
LocateRegistry.createRegistry(1099);
Naming.bind("rmi://0.0.0.0:1099/hello", rhello);

LocateRegistry.getRegistry()会使用给定的主机和端口等信息本地创建一个Stub对象作为Registry远程对象的代理,从而启动整个远程调用逻辑。服务端应用程序可以向RMI注册表中注册远程对象,然后客户端向RMI注册表查询某个远程对象名称,来获取该远程对象的Stub。

Registry registry = LocateRegistry.getRegistry("kingx_kali_host",1099);
IHello rhello = (IHello) registry.lookup("hello");
rhello.sayHello("test");
  • 使用RMI Registry之后,RMI的调用关系是这样的:

所以其实从客户端角度看,服务端应用是有两个端口的,一个是RMI Registry端口(默认为1099),另一个是远程对象的通信端口(随机分配的)。这个通信细节比较重要,真实利用过程中可能会在这里遇到一些坑。

参考:

【技术分享】一文回顾攻击Java RMI方式

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

java渗透测试基础之——RMI 的相关文章

随机推荐

  • yapi的安装

    Yapi的安装 Yapi是一款不错的接口管理软件 我主要用它来进行接口Mock Yapi安装所需环境 Node js 7 6 Mongodb 2 6 git 各环境安装地址 git https git scm com downloads N
  • F.softmax()的用法

    F softmax 的用法 gt gt gt import torch gt gt gt import torch nn functional as F gt gt gt logits torch rand 2 2 gt gt gt pre
  • C++函数返回引用

    注 C 有三种传递方式 值传递 指针传递 引用传递 返回 值 和返回 引用 是不同的 函数返回值时会产生一个临时变量作为函数返回值的副本 而返回引用时不会产生值的副本 既然是引用 那引用谁呢 这个问题必须清楚 否则将无法理解返回引用到底是个
  • Spring Cloud Nacos源码讲解(五)- Nacos服务端健康检查

    Nacos服务端健康检查 长连接 概念 长连接 指在一个连接上可以连续发送多个数据包 在连接保持期间 如果没有数据包发送 需要双方发链路检测包 注册中心客户端2 0之后使用gRPC代替http 会与服务端建立长连接 但仍然保留了对旧http
  • The Black Tux

    The Black Tux IT桔子 The Black Tux IT桔子 The Black Tux theblacktux com posted on 2014 07 27 17 34 lexus 阅读 评论 编辑 收藏 转载于 htt
  • 从coco数据集中提取需要的类别

    进行目标检测时 有时只需要训练数据集中的部分图像 以 coco128 为例 只选出其中的车辆类 bicycle car motorcycle bus truck coco128 数据集中的标签为 txt 文件 每一个图像由若干行 每一行对应
  • 转载之remosaic

    一 Quadra CFA 介绍 Quadra CFA Quadra Color Filter array 功能提高了在弱光条件下的性能和信噪比 此功能在弱光条件下提供明亮和清晰的图像 在正常光照条件下提供高分辨率图像 Quadra CFA有
  • HTTPWeb服务器---HTTP整体设计框架

    整个项目采用B S模式 浏览器 服务器模式 通过浏览器发送的method 只要包含GET和POST两种方法 server对此进行响应 最终通过html显示所得到的结果 因为服务器同时处理多条连接 因此采用了多线程的结构 HTTP是在TCP之
  • Dijkstra算法和Floyd算法对比分析

    转载 http blog csdn net liuyanling cs article details 56330652 首先 Dijkstra算法与Floyd算法都是广度优先搜索的算法 都可以用来求单源点到其他所有点的最短路径 那么这两者
  • Go mod 全部操作命令

    一 基础理论 shell 1 Go mod 初始化 go mod init 模块名 2 Go mod 下载到本地Cache go mod download 2 Go mod 清理本地Cache go clean modcache 3 Go
  • IDEA查看调用方法代码上一步和下一步

    IDEA默认上一步是CTRL ALT 箭头 IDEA默认下一步是CTRL ALT 箭头
  • 2020暨南大学计算机专硕考研经验分享

    具体的研究生招生录取情况 初试复试比例 和初复试的流程可以看下面链接这篇 总结得很好 2020暨南大学计算机考研经验分享 精品 含录取名单 学习等资料获取 前期都是各位学长的帮助 真的非常感谢 尤其了知乎上分享经验的18级李学长 集中资料
  • stata怎么判断是否存在异常值_异常值的识别与处理,看这一篇就够了

    原标题 异常值的识别与处理 看这一篇就够了 在数据分析工作中 面对收集而来的数据 数据清洗是首要环节 而异常值处理是其中的一个重要部分 下面就给大家介绍一下如何处理数据中的异常值 一 异常值判断 何为异常值 异常值 指的是样本中的一些数值明
  • 关于CLion有时找不到标准库的解决方案

    关于CLion有时找不到标准库的解决方案 CLion是linux下C 开发的利器 出色的语法高亮 支持cmake工程让同类IDE望尘莫及 但是我在实际开发中遇到了标准库 STL 相关的语法高亮不能正常运行的问题 问题情境 我们用UBUNTU
  • PyQt QTextEdit 详细用法示例 Python

    PyQt QTextEdit 详细用法示例 Python QTextEdit 是 PyQt 中用于显示和编辑文本的小部件之一 它提供了丰富的功能 包括文本格式化 文本样式 撤销和重做操作等 在本文中 我们将探讨 QTextEdit 的详细用
  • unity could not produce class with id 210 --- TerrainInstance 其实是 UnityEngine.CoreModule.Sorting

    Strip Engine Code 幾重 張 巡 罠 https qiita com warapuri items e2562e9535bfae5013d0 More than 1 year has passed since last up
  • 在windows下详解:大端对齐和小端对齐

    计算机的内存最小单位是什么 是BYTE 是字节 一个大于BYTE的数据类型在内存中存放的时候要有先后顺序 高内存地址放整数的高位 低内存地址放整数的低位 这种方式叫倒着放 术语叫小端对齐 电脑X86和手机ARM都是小端对齐的 高内存地址放整
  • IP地址、子网掩码、网络地址、广播地址、IP网段

    文章目录 IP地址 IP地址分类 子网掩码 网络地址 广播地址 IP网段 本文主要讨论iPv4地址 IP地址 实际的 IP 地址是一串32 比特的数字 按照 8 比特 1 字节 为一组分成 4 组 分别用十进制表示然后再用圆点隔开 这就是我
  • 关于CMAKE 报错CMAKE_CUDA_ARCHITECTURES的问题

    背景 新版本cmake 增加了CMAKE CUDA ARCHITECTURES检测 某些手动安装cuda的同学会遇到该报错问题 该问题不影响代码 只是cmake内部的编译设置 cmake 3 23版本该问题报错为 CMAKE CUDA AR
  • java渗透测试基础之——RMI

    一 概述 RMI全称是Remote Method Invocation 远程方法调用 是专为Java环境设计的远程方法调用机制 远程服务器提供API 客户端根据API提供相应参数即可调用远程方法 由此可见 使用RMI时会涉及到参数传递和结果