RPC概念和实例

xiaoeryu Lv5

本章来看一下RPC相关的问题

RPC远程过程调用,是一种计算机通信协议,用于在计算机网络中的程序之间进行通信。它允许程序调用另一个地址空间(通常是远程机器上的程序)的过程,就像调用本地过程一样,而无需显式地处理网络细节。

在RPC中,客户端程序调用远程服务器上的过程,就像调用本地过程一样。RPC框架负责将参数传递给远程过程并返回结果。这使得分布式计算变得更加容易,因为开发人员可以将远程调用抽象为本地调用,而不必去关心底层网络通信的实现细节。

  • 在进行RPC之前,要先把问题在hook和主动调用阶段解决,然后再去进行RPC的互联

远程调用

这里拿lesson4 编写的apk来进行RPC调用测试

调用secret()

先执行这个脚本看是否起作用了

  • 起作用了我们开始进行下一步的RPC调用

RPC注入的脚本:

PS:Amazon CodeWhisperer的代码智能提示挺好用的,支持中文还免费

lesson7_lesson4.js
function invoke(){
    Java.perform(function(){
        Java.choose("com.xiaoeryu.lesson4_4.MainActivity",{
            onMatch:function(instance){
                console.log("found instance: ", instance)
                console.log("found instance: ", instance.secret())
            },onComplete:function(){}
        })
    })
}

// setTimeout(invoke, 3000);

rpc.exports = {
    invokefunc:invoke
}
lesson7_loader.py
import time
import frida

def my_message_handler(message, payload):
    # 处理Frida脚本发送的消息
    print(message)
    print(payload)

# 获取连接到计算机的USB设备,通常是Android设备
device = frida.get_usb_device()

# ==============================
# 选择一种注入方式(方式1或方式2),注释掉另一种方式

# Way 1: 通过spawn方式创建进程并附加
# pid = device.spawn(["com.xiaoeryu.lesson4_4"])
# device.resume(pid)
# time.sleep(1)
# session = device.attach(pid)

# Way 2: 直接绑定到指定的进程
session = device.attach("com.xiaoeryu.lesson4_4")  # 绑定指定进程
# ==============================

# 打开名为lesson7_lesson4.js的Frida脚本文件
with open("lesson7_lesson4.js") as f:
    # 创建Frida脚本对象并加载脚本内容
    script = session.create_script(f.read())

# 将my_message_handler函数注册为脚本的消息处理程序
script.on("message", my_message_handler)

# 加载脚本到目标应用程序中,使其开始执行
script.load()

command = ""
while True:
    # 等待用户输入命令,提示符为>>>
    command = input(">>>")
    if command == "1":
        # 如果用户输入为"1",跳出循环,结束程序
        break
    elif command == "2":
        # 如果用户输入为"2",调用Frida脚本中导出的invokefunc函数
        script.exports.invokefunc()
执行效果

多主机多手机多端口混连

先把手机的wifi adb打开

打开frida-server绑定9999端口

  • 现在我们局域网下面的设备就都可以使用adb命令连接192.168.1.7这台设备了

修改一下我们的python脚本为远程连接

打印设备的信息也是可以的

互联互通、动态修改、上传到PC打印

下面先写一下用来测试的登陆界面 ,然后截取登陆界面的用户名和密码并修改

PS:使用下载的代码的时候最好把IDE的代理挂上,不然有些包下载不下来各种报错

  • 在这里我们设置一下不允许使用admin用户名登陆,后面hook后强制修改
  • 然后把输入的用户名和密码base64后传给服务端
  • 可以通过HOOK message_tv.setText()来拿到base64后的用户名和密码
编写hook脚本

setText()是一个通用函数肯定有多个重载,可以先确定这里使用的是哪个重载

  • 使用objection来确定一下使用了哪个重载

    • 这里frida-server不小心断开了,重连了一遍,所以端口改为了8888
    • Called android.widget.TextView.setText(java.lang.CharSequence)
function main(){
    Java.perform(function(){
        Java.use("android.widget.TextView").setText.overload('java.lang.CharSequence').implementation = function(x){
            console.log("TextView.setText: " + x)
            return this.setText(x)
        }
    })
}
setImmediate(main)
  • 在使用RPC之前先试一下本地调用是否ok

    • 测试ok,开始修改脚本去跟服务端联动
修改脚本:添加发送和接收进行RPC联动

修改后的lesson7sec.js

Java.perform(function(){
    Java.use("android.widget.TextView").setText.overload('java.lang.CharSequence').implementation = function(x){
        var string_to_send_x = x.toString()
        var string_to_recv
        send(string_to_send_x)
        recv(function(recv_json_objection){
            string_to_recv = recv_json_objection.my_data
            // x = string_to_recv.toString()
            console.log("string_to_recv: " + string_to_recv)
        }).wait()
        var javaStringToSend = Java.use("java.lang.String").$new(string_to_recv)

        var result = this.setText(javaStringToSend)
        // console.log("TextView.setText: " + x)
        return result
    }
})

lesson7secLoader.py

import time
import frida
import base64

# 定义消息处理函数
def my_message_handler(message, payload):
    # 打印接收到的消息和负载
    print(message)
    print(payload)

    # 检查消息类型是否为 "send"
    if message["type"] == "send":
        # 打印接收到的负载数据
        print(message["payload"])

        # 解码 base64 编码的数据,并转换为字符串
        data = message["payload"].split(":")[1].strip()
        data = str(base64.b64decode(data), encoding="utf-8")
        print("解码后的数据: ", data)

        # 将解码后的数据拆分成用户名和密码
        usr, pw = data.split(":")
        print("密码: ", pw)

        # 将用户名替换为 "admin",然后重新编码为 base64
        data = str(base64.b64encode(("admin" + ":" + pw).encode()))
        print("编码后的数据: ", data)

        # 向脚本发送修改后的数据
        script.post({"my_data": data})
        print("修改后的数据已发送!")

# 连接到远程设备
device = frida.get_device_manager().add_remote_device("192.168.1.7:8888")

# 附加到目标应用进程
session = device.attach("com.example.lesson7sec")

# 从文件中读取 JavaScript 脚本
with open("lesson7sec.js") as f:
    script = session.create_script(f.read())

# 设置消息处理回调函数
script.on("message", my_message_handler)

# 加载并执行 JavaScript 脚本
script.load()

# 等待输入,保持脚本运行
input()
  • 运行结果

  • 标题: RPC概念和实例
  • 作者: xiaoeryu
  • 创建于 : 2023-11-24 16:15:00
  • 更新于 : 2023-11-24 21:30:36
  • 链接: https://github.com/xiaoeryu/2023/11/24/RPC概念和实例/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论