安装示例 APK
首先在模拟器上安装好官方提供的示例 APK。
打开运行,可以看到是一道 CTF 题,通过和 CPU 玩石头剪刀布连续获胜 1000 次可以得到 flag。
显然这道题的做法有很多种,直接分析源码、修改源码、通过 Hook 修改获胜状态等方式都可以搞定。
那就根据官方文档来 Hook 一把玩玩吧。
执行 python 脚本
直接把官方文档的 python 代码拷下来:
import frida, sys
def on_message(message, data):
if message['type'] == 'send':
print("[*] {0}".format(message['payload']))
else:
print(message)
jscode = """
Java.perform(function () {
// 这里定义了需要被hook的函数(即安卓程序中的关键方法)
var MainActivity = Java.use('com.example.seccon2015.rock_paper_scissors.MainActivity');
// 当按钮按下时
var onClick = MainActivity.onClick;
onClick.implementation = function (v) {
// 打印一条消息来说明函数被调用
send('onClick');
// 执行原始的点击事件
onClick.call(this, v);
// 在执行原始点击事件后设置变量的值
this.m.value = 0;
this.n.value = 1;
this.cnt.value = 999;
// 在控制台打印消息,此时应该已经得到flag
console.log('Done:' + JSON.stringify(this.cnt));
};
});
"""
# 官方文档用的是frida.get_usb_device(),这里因为使用模拟器,换成frida.get_remote_device()
process = frida.get_remote_device().attach('com.example.seccon2015.rock_paper_scissors')
script = process.create_script(jscode)
script.on('message', on_message)
print('[*] Running CTF')
script.load()
sys.stdin.read()
这段 python 代码的作用是,连接到设备并附加到目标进程上,再将 JS 脚本注入到进程中。
而 JS 代码的作用则是让点击事件执行后变量的值改变。
通过 adb 连接到模拟器 shell,启动 frida 服务端。
再设置 TCP转发:
adb forward tcp:27042 tcp:27042
adb forward tcp:27043 tcp:27043
然后在模拟器上启动石头剪刀布程序,最后在 Win10 上执行上面的 python 脚本。
现在,在模拟器上随便点一个按钮,都能拿到 flag:
控制台也打印了语句:
显然这一次 Hook 操作是成功了。
直接使用 JS 脚本
先不启动 app,在 Win10 上执行命令:
frida -U -f com.example.seccon2015.rock_paper_scissors --no-pause
此时模拟器上 app 会自动启动。并且命令行中会显示 frida 的界面:
现在可以使用 frida 的 JavaScript Api 了。在 cmd 中执行以下 JS 脚本:
// 这里要用performNow
Java.performNow(function () {
var MainActivity = Java.use('com.example.seccon2015.rock_paper_scissors.MainActivity');
var onClick = MainActivity.onClick;
onClick.implementation = function (v) {
onClick.call(this, v);
this.m.value = 0;
this.n.value = 1;
this.cnt.value = 999;
console.log('Done:' + JSON.stringify(this.cnt));
};
});
现在回到模拟器中点击任意按钮,现象与使用 python 时相同,说明 Hook 成功。
实际上,frida 允许在 app 启动时直接以 -l xx.js
指定 JS 脚本。但在我进行测试时,使用 Java.performNow()
会出现类未加载的问题;而使用 Java.perform()
则会出现必须重新编辑 js 文件才执行的问题——这个缺点也是可利用的,修改 JS 文件后不需要重新运行 frida。