当前位置:首页>python>Frida 之 RPC 远程调用:让 Python 直接调用 App 内部函数

Frida 之 RPC 远程调用:让 Python 直接调用 App 内部函数

  • 2026-10-11 08:37:39
Frida 之 RPC 远程调用:让 Python 直接调用 App 内部函数

📌 前置知识:Frida 基础用法、Python 基础🎯 目标:通过 RPC 在 Python 中调用 Android App 的加解密函数


一、为什么需要 RPC?

传统方式的痛点

以前我们分析 App 时,通常这样做:

1. 写 Frida 脚本 → 注入 App2. 手动触发 App 行为3. 查看 console 输出4. 重复步骤 2-3...

问题:需要手动操作 App,无法自动化批量调用。

RPC 带来的变革

有了 RPC,Python 可以直接调用 App 内部的函数:

┌─────────────────────────────────────────────────────────────────┐│                      RPC 调用流程                               │├─────────────────────────────────────────────────────────────────┤│                                                                 ││  Python 脚本                                                   ││     │                                                          ││     │  rpc.encrypt("hello")  ──▶                               ││     ▼                                                          ││  ┌─────────────────────────────────────┐                       ││  │         Frida RPC Server           │                       ││  │   (运行在 Android 进程中)          │                       ││  └──────────────┬──────────────────────┘                       ││                 │                                               ││                 ▼                                               ││  ┌─────────────────────────────────────┐                       ││  │    App 内部函数被调用               │                       ││  │    encrypt("hello")                │                       ││  └──────────────┬──────────────────────┘                       ││                 │                                               ││                 ▼ 返回密文                                       ││     ◀─────────────────────────────────                          ││                                                                 │└─────────────────────────────────────────────────────────────────┘

二、RPC 核心概念

2.1 工作原理

组件作用
rpc.exports暴露函数给 Python 调用
Python 端创建 session,加载脚本,调用 exports
数据传递自动序列化:JS ↔ Python

2.2 支持的数据类型

JavaScript 类型Python 类型说明
Numberint/float自动转换
StringstrUTF-8 编码
Booleanbool直接映射
Arraylist数组转换
Objectdict对象转字典
Uint8Arraybytes二进制数据
NativePointerint内存地址

三、快速入门

3.1 第一步:编写 Frida 脚本

// rpc_agent.js// 通过 rpc.exports 导出函数rpc.exports= {// 简单函数add: function(a, b) {returna+b;    },// 获取进程信息getInfo: function() {return {pid: Process.id,name: Process.enumerateModules()[0].name        };    },// 调用 Java 层函数encrypt: function(plaintext) {varresult=null;Java.perform(function() {varCryptoUtils=Java.use("com.example.app.SecurityUtils");result=CryptoUtils.encrypt(plaintext);        });returnresult;    },// 异步函数decryptAsync: asyncfunction(ciphertext) {returnnewPromise(function(resolve) {Java.perform(function() {varresult=Java.use("com.example.app.SecurityUtils")                    .decrypt(ciphertext);resolve(result);            });        });    }};

3. 第二步:Python 调用

# rpc_client.pyimportfridaimportsys# 附加到目标 Appsession = frida.attach("com.example.app")# 加载 RPC 脚本script = session.create_script(open("rpc_agent.js").read())script.load()# 获取导出对象rpc = script.exports# 调用 RPC 函数print("1 + 2 =", rpc.add(1, 2))print("进程信息:", rpc.getInfo())print("加密结果:", rpc.encrypt("hello"))print("解密结果:", rpc.decryptAsync("xxx"))session.detach()

3.3 运行

python rpc_client.py

四、实战:自动调用加解密

场景

假设有一个 App,内部有复杂的加密算法。我们想批量调用这个加密函数,不需要理解算法细节。

4.1 Frida 脚本

// crypto_rpc.jsrpc.exports= {// 加密encrypt: function(plaintext) {varresult=null;Java.perform(function() {varAesUtil=Java.use("com.example.app.utils.AesUtil");result=AesUtil.encrypt(plaintext);        });returnresult;    },// 解密decrypt: function(ciphertext) {varresult=null;Java.perform(function() {varAesUtil=Java.use("com.example.app.utils.AesUtil");result=AesUtil.decrypt(ciphertext);        });returnresult;    },// 生成签名sign: function(data, key) {varresult=null;Java.perform(function() {varSignUtil=Java.use("com.example.app.utils.SignUtil");result=SignUtil.sign(data, key);        });returnresult;    }};

4.2 Python 批量调用脚本

# batch_encrypt.pyimportfridadefmain():# 连接目标 Appsession = frida.attach("com.example.app")# 加载脚本script = session.create_script(open("crypto_rpc.js").read())script.load()rpc = script.exports# 批量加密测试test_data = ["hello world","123456","test@email.com","订单号: 12345",    ]print("========== 批量加密测试 ==========")forplaintextintest_data:try:encrypted = rpc.encrypt(plaintext)print(f"[+] {plaintext} -> {encrypted}")exceptExceptionase:print(f"[!] {plaintext} 失败: {e}")print("\n========== 批量解密测试 ==========")# 假设我们知道一个密文cipher = "U29tZXRoaW5nIHRoYXQgbmVlZCB0byBiZSBkZWNyeXB0ZWQ="try:decrypted = rpc.decrypt(cipher)print(f"[+] {cipher} -> {decrypted}")exceptExceptionase:print(f"[!] 解密失败: {e}")session.detach()if__name__ == "__main__":main()

4.3 运行效果

========== 批量加密测试 ==========[+] hello world -> x7q2K9pLmN8...[+] 123456 -> a1b2c3d4e5f6...[+] test@email.com -> ZXN0cnlAbWFpbC5jb20...[+] 订单号: 12345 -> S2lYaWxsT3JkZXI...========== 批量解密测试 ==========[+] U29tZXRoaW5nIHRoYXQgbmVlZCB0byBiZSBkZWNyeXB0ZWQ -> Something that needs to be decrypted

五、高级:处理复杂参数

5.1 传递 Java 对象

// 处理 Java 对象参数rpc.exports= {login: function(username, password) {varresult=null;Java.perform(function() {varLoginRequest=Java.use("com.example.app.model.LoginRequest");varrequest=LoginRequest.$new();request.setUsername(username);request.setPassword(password);varApiClient=Java.use("com.example.app.network.ApiClient");result=ApiClient.login(request);        });returnresult;    }};

5.2 处理二进制数据

// 处理 bytes[] 参数rpc.exports= {hash: function(data_hex) {varresult=null;Java.perform(function() {// 十六进制字符串转字节数组varbytes=Java.use("java.util.Hex")                .decode(data_hex);varMessageDigest=Java.use("java.security.MessageDigest");varmd=MessageDigest.getInstance("SHA-256");varhash=md.digest(bytes);result=Java.use("java.util.Hex")                .encodeToString(hash);        });returnresult;    }};

5.3 返回二进制数据

// 返回 bytes[] 给 Pythonrpc.exports= {getCert: function() {varcert=null;Java.perform(function() {varcertObj=Java.use("java.security.cert.X509Certificate")                .$new(/** 参数 **/);cert=certObj.getEncoded();        });// 返回 bytes (Uint8Array -> Python bytes)returncert;    }};

六、完整案例:自动化签名生成

场景

App 的签名算法很复杂,但我们只需要批量生成签名用于测试。

6.1 Frida 脚本

// sign_rpc.jsrpc.exports= {// 获取签名getSign: function(params) {varresult=null;Java.perform(function() {varSignHelper=Java.use("com.example.app.SecurityHelper");// params 是 dict,转为 Java MapvarMap=Java.use("java.util.HashMap");varmap=Map.$new();for (varkeyinparams) {map.put(key, params[key]);            }result=SignHelper.getSign(map);        });returnresult;    },// 验证签名verifySign: function(params, sign) {varresult=false;Java.perform(function() {varMap=Java.use("java.util.HashMap");varmap=Map.$new();for (varkeyinparams) {map.put(key, params[key]);            }varSignHelper=Java.use("com.example.app.SecurityHelper");result=SignHelper.verifySign(map, sign);        });returnresult;    }};

6.2 Python 调用

# auto_sign.pyimportfridaimportjsonsession = frida.attach("com.example.app")script = session.create_script(open("sign_rpc.js").read())script.load()rpc = script.exports# 模拟批量请求requests = [    {"appId": "app1", "timestamp": "1700000001", "nonce": "abc"},    {"appId": "app2", "timestamp": "1700000002", "nonce": "def"},    {"appId": "app3", "timestamp": "1700000003", "nonce": "ghi"},]print("========== 批量生成签名 ==========")forreqinrequests:sign = rpc.getSign(req)print(f"参数: {json.dumps(req)}")print(f"签名: {sign}\n")

七、常见问题汇总

问题原因解决方案
rpc is not defined未加载脚本确保 script.load() 已执行
参数类型错误Java 对象需要构造使用 $new() 创建实例
返回 null函数执行失败添加 try-catch 打印错误
异步返回Promise 未解决使用 async/await 或回调
内存地址转换Pointer 需要处理使用 parseInt(ptr) 转整数

八、总结

RPC 核心流程:

1️⃣  编写 rpc.exports ──▶ 暴露内部函数2️⃣  Python 加载脚本 ──▶ session.create_script()3️⃣  调用 exports ──────▶ script.exports.函数名()4️⃣  获取返回值 ───────▶ 自动反序列化为 Python 类型

使用场景:

  • 🔐 自动化加解密调用

  • ✍️ 批量签名生成

  • 🧪 自动化安全测试

  • 🔄 批量接口测试

  • 📊 数据批量采集


🔗 关联阅读:《Frida 环境搭建》《Java 层 Hook 基础》《登录 & 加密实战》💬 有问题? 评论区聊聊!


本文仅供学习与安全研究使用,请勿用于非法目的。

最新文章

随机文章