简介
在我们遇到需要对代码的值进行动态修改时每次去代码中修改显得很麻烦所以我们需要写一个python脚本来实现后面跟着使用burp插件实现自动化加解密,这篇文章我们来分析如何来编写我们的python交互脚本。
介绍frida
首先我们解析一下frida的功能参数
Frida -U -f 包名-1加载的脚本--no-pause
-U
device = frida.get_usb_device()
-f
pid-device.spawn(["com.apk.xxx "])
session- device.attach(pid)
-l
withopen("xxx.js", "r", encoding-"utf-8") as f:
script = session.create_script(f.read())
script.on("message", on_message)
script.load()
--no-pause
device.resume(pid)
以及我们的-H参数的实现
使用-H的编写方式
Frida -H 127.0.0.1:6666 -l xxxx.js
#定义目标设备的主机地址和端口号
rpc_host = '127.0.0.1' # 替换为你的远程设备的IP地址
rpc_port = 6666 # 替换为你的远程设备的端口号
#组合成字符串形式的地址
remote_address = f'{rpc_host}:{rpc_port}'
#连接到远程设备
device = frida.get_device_manager().add_remote_device(remote_address)
实验(网络连接方式下)
我们这篇文张主要使用网络连接方式来实现我们的这个实验
在此之前我们简单讲解一下:
Frida 脚本关键api
send({type
: "message", payload: "hello world"})
send 方法是 Frida RPC 中用于从注入的 JavaScript 脚本发送消息到python方法。
可以包含你想要传递的任何数据。通常,你至少会包含一个类型字段(type)和一个负载字段(payload)。
Java.perform(function(){
var TargetClass = Java.use("com.xxxx.xxxxxx.MainActivity");
TargetClass.exampleMethod.implementation = function(input){
console.log("n目标方法的传入参数: "+input);
send({type: "exampleMethod_arg_input",payload: input});
send({type: "message",payload: "Hello from the app!"});
var result = this.exampleMethod(input)
send({type: "message",payload: result });
return result;
}
})
输出中,message 字典包含两个键:type 和 payload。
- type: 这通常是一个字符串,描述消息的类型。在这个例子中,它的值是 "send",表示这是一个由 send 函数发送的消息。
- `payload`: 这是一个包含实际消息数据的对象。在这个例子中,payload 本身也是一个字典,包含在 JavaScript 脚本中发送的数据:
- {type: 'message', 'payload': 'Hello from the app!'}
- 这个内部的 payload 字典包含两个键:
- type: "message",用来描述消息类型的自定义字符串。
- payload: "Hello from the app!",发送的实际消息内容。
可以选择使用 send 函数的第二个参数来发送一个 ArrayBuffer,这样它就会在 Python 端作为 data 参数接收。上面的输出结果显示 data: None,这就是省略第二个参数的结果,我们暂时用不到,可以省略。
不省略的示例如下:
Java.perform(function(){
var TargetClass = Java.use("com.xxxx.xxxxx.MainActivity");
TargetClass.exampleMethod.implementation = function(input){
var StringClass = Java.use("java.lang.String");
var byteArray = StringClass.$new("Hello World").getBytes();
console.log("n目标方法的传入参数: "+input);
var buffer = new ArrayBuffer(4);
// 发送消息和二进制数据
send({type: "message",payload: "Hello from the app!"}, buffer);
return this.exampleMethod(input);
}
})
**python脚本关键api:**
py脚本中获取设备和进程
Frida
# 获取设备和进程
# spawn方式启动
device = frida.get_usb_device()
pid = device.spawn(["com.xxxxx.xxxxxx"])
session = device.attach(pid)
# 继续应用程序执行,执行script.load()后再执行
device.resume(pid)
py脚本中加载并注入frida hook脚本:
withopen("xxxxx.js", "r", encoding="utf-8") as f:
script = session.create_script(f.read())
script.on("message", on_message)
script.load()
script.on("message", on_message) 是一个事件监听器的设置,它用于处理从注入的 JavaScript 脚本发送到 Python 脚本的消息。
这里的 script 是一个 Frida Script 对象,它代表一个注入到目标进程的 JavaScript 脚本。
script.on 方法用于设置事件监听器,它接受两个参数:
- 第一个参数是要监听的事件的名称。在模板中,我们监听的是 "message" 事件,这个事件在 JavaScript 脚本中调用 send 函数时触发。
- 第二个参数是一个回调函数,它在事件触发时被调用。在这个模板中,回调函数的名称是 on_message。
py脚本中定义回调函数以及取出hook脚本传递过来的数据:
# 定义一个回调函数来处理从 JavaScript 代码发送来的消息
def on_message(message, data):
# print("py output >>> ",message)
if message['type'] == 'send':
payload = message['payload']
_type, data = payload['type'], payload['payload']
if _type == 'message':
data = str(data)
print("py output payload['payload'] >>> ", data)
py脚本中定义回调函数以及取出hook脚本传递过来的数据:
# 发送一个消息到 JavaScript 代码
script.post({"type": "input", "payload": "Hello from Python!"})
py脚本中定义回调函数以及取出hook脚本传递过来的数据:
**frida脚本关键api:
接收py处理的数据结果
:
var modinput = null;
var op = recv('fromepy', function (data) {
modinput = data.payload;
});
// 等待 recv 函数处理完数据
op.wait();
完整的从我们的实验里进行看吧
使用某牛进行分析
抓包看看原来的值
我们本次实验解决改包并加密发包
这是我们使用的frida脚本跑路一下我们修改后的值
这里数据包里面也可以看到我们修改的值也改变了我们的请求包
我们将脚本放出来
Java.perform(function () {
let DesSecurity = Java.use("com.xxxx.xxxxx.xxxxx.DesSecurity");
DesSecurity["encrypt64"].implementation = function (data) {
var moddata = StringToByteArray("hell word");
let result = this["encrypt64"](moddata);
send({type: "message", data: result});
return result;
};
});
// 定义将字符串转换为字节数组的函数
function StringToByteArray(str) {
let byteArray = [];
for (let i = 0; i < str.length; i++) {
byteArray.push(str.charCodeAt(i));
}
return byteArray;
}
我们把完整的脚本放出来
Rpc.js
// 使用Java.perform确保代码在Java运行时环境中执行
Java.perform(function () {
// 1. 获取目标Java类
// 使用Java.use获取对com.dodonew.online.util.DesSecurity类的引用
let DesSecurity = Java.use("com.xxxxx.xxxxx.xxxxx.DesSecurity");
// 2. Hook目标方法
// 替换DesSecurity类的encrypt64方法的实现
DesSecurity.encrypt64.implementation = function (data) {
// 2.1 发送原始数据到Python端
// 通过send函数将原始数据发送给Python脚本处理
send({ type: "message", data: data });
// 2.2 接收Python修改后的数据
// 初始化modified_data变量用于存储接收到的数据
var modified_data = null;
// 使用recv函数等待Python返回数据,消息类型为"adb-post"
recv("adb-post", function (mod_data) {
// 回调函数,当收到消息时执行
modified_data = mod_data.payload; // 将接收到的数据存入modified_data
console.log("Received from Python:", modified_data); // 打印接收到的数据
}).wait(); // 阻塞等待,直到收到Python的响应
// 打印最终接收到的数据
console.log("在JS中接收到的数据:", modified_data);
// 2.3 数据格式转换
// 将字符串转换为字节数组(因为加密方法可能需要字节数组输入)
let arr = StringToByteArray(modified_data);
console.log("Byte array:", arr); // 打印转换后的字节数组
// 2.4 调用原始加密方法
// 使用this调用原始的encrypt64方法,并传入转换后的字节数组
let result = this.encrypt64(arr);
console.log("Final result:", result); // 打印加密结果
// 返回加密结果
return result;
};
// 3. 辅助函数:字符串转字节数组
// 将JavaScript字符串转换为字节数组
function StringToByteArray(str) {
let byteArray = []; // 创建空数组存储字节
// 遍历字符串每个字符
for (let i = 0; i < str.length; i++) {
// 获取字符的Unicode码点并存入数组
byteArray.push(str.charCodeAt(i));
}
return byteArray; // 返回字节数组
}
});
Rpc.py
# 导入必要的库
import frida # Frida核心库
import sys # 系统库(用于标准输入)
import json # JSON处理库
# ================ 设备连接配置 ================
# 定义目标设备的主机地址和端口号
rpc_host = '127.0.0.1' # 远程设备的IP地址(本地测试使用回环地址)
rpc_port = 6666 # Frida服务端监听的端口号
# 组合成Frida可识别的远程地址字符串
remote_address = f'{rpc_host}:{rpc_port}'
# ================ 设备连接 ================
# 获取设备管理器并添加远程设备
device = frida.get_device_manager().add_remote_device(remote_address)
# ================ 目标进程处理 ================
# 设置要注入的目标应用包名
process_name = 'com.xxxxx.xxxx' # 目标Android应用的包名
# 启动目标应用并获取其PID(进程ID)
pid = device.spawn([process_name]) # spawn会启动应用但暂停执行
device.resume(pid) # 恢复应用运行
# ================ 加载Frida脚本 ================
# 读取JS脚本文件内容
with open('rpc.js', 'r', encoding='utf-8') as file:
script_content = file.read() # 读取完整的JS脚本内容
# ================ 脚本注入 ================
# 附加到目标进程
script = device.attach(pid) # 建立与目标进程的连接
# 创建脚本会话(将JS代码载入目标进程)
script_session = script.create_script(script_content)
# ================ 消息处理函数 ================
def on_message(message, data):
"""
处理来自JS脚本的消息回调函数
:param message: 消息主体(字典类型)
:param data: 附加的二进制数据
"""
if message['type'] == 'send': # 处理普通发送消息
# 检查payload是否是字典,如果不是则解析为字典
if isinstance(message['payload'], dict):
payload = message['payload']
else:
payload = json.loads(message['payload']) # 将JSON字符串转为字典
# 提取消息类型和数据
_type = payload['type']
data = payload['data']
print(f"[on_message] 类型: {_type}, 数据: {data}")
# 构造要发送回JS的修改后数据(示例)
mod_data = "hello word"
# 发送响应给JS脚本
script_session.post({'type': 'adb-post', 'payload': mod_data})
print("发送成功")
else:
# 打印其他类型的消息(如错误)
print(f"[on_message] 其他消息: {message}")
# 注册消息处理回调函数
script_session.on('message', on_message)
# ================ 启动脚本 ================
script_session.load() # 加载并执行JS脚本
# 保持脚本运行(等待用户输入退出)
sys.stdin.read() # 阻塞主线程,保持脚本运行
# ================ 清理工作 ================
script.detach() # 从目标进程分离
device.remove_remote_device(remote_address) # 移除远程设备
运行修改效果
总结
感兴趣的可以公众号回答回复"深情哥"进群,有公开课会在群里面通知,包括审计和src。edu邀请码获取也可以联系深情哥。
内部edu+src培训,包括src挖掘,edu挖掘,小程序逆向,js逆向,app渗透,导师是挖洞过30w的奥特曼,edu上千分的带头大哥!!!联系深情哥即可。
原文始发于微信公众号(湘安无事):APK hook脚本与python交互实现(基础篇)
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论