Frida 是一款基于 Python + JavaScript 的 Hook 与调试框架。是一款跨平台 Hook 动态的插桩工具,通过可以插入代码到原生 App 的内存空间中,动态的去监视和修改行为,原生平台包括 Win、Mac、Linux、Android、iOS 全平台。这里我们以Hook android APK 为例。
0x01 环境准备
1. 环境搭建
安装容易遇到坑:pip install 有时候会遇到 ssl 问题,主要是python3的版本,最好用3.6。
pip3 install frida-tools
安装Frida客户端,frida --version
输出版本信息代表安装成功;- GitHub官方下载手机CPU型号对应的frida-server(目前最新版本frida-server-10.7.7-android-arm64.xz);
- 解压frida-server-10.7.7-android-arm64.xz压缩文件,将frida-server push到Android手机.
adb push ./frida-server-10.7.7-android-arm64 /data/local/tmp
- 设置frida-server权限
chmod 755 /data/local/tmp/frida-server-10.7.7-android-arm64
- 启动frida-server
/data/local/tmp/frida-server-10.7.7-android-arm64
如果需要frida-server在后台运行,使用下面命令最后加个 & 符合:/data/local/tmp/frida-server-10.7.7-android-arm64 &
- 在pc端执行命令
frida-ps -U
,看能否正常显示手机进程.-Ua
查看运行的进程,ios可以查看应用包名。
2. 在虚拟机使用Frida
- 远程连接虚拟机:
adb connect 127.0.0.1:21503
端口在安装软件安装目录的\MemuHyperv VMs\MEmu\MEmu.memu-prev文件中查询 - 将frida-server拷贝到虚拟机中,授权并启动.
- 本地转发端口
adb forward tcp:27042 tcp:27042 和 adb forward tcp:27043 tcp:27043
frida-ps -aR
测试是否成功.
3. 模拟器 - 网易mumu
adb shell
进入shell- 安装
frida-server-12.8.6-android-x86
(不支持64位) - 在pc端执行命令
frida-ps -U
,看能否正常显示手机进程(虚拟机相当于直接使用usb链接了)
0x02 操作笔记
1. 注入OR生成新进程
- 注入进程:
frida -U com.xxx.xxx
(注入到运行中的进程) - 生成进程:
frida -U -f com.xxx.xxx
(注入到Zygote,生成新进程) - 使用
--no-pause
命令,配合-f
命令使用,不暂停在app启动时,如果没有使用no pause命令,需要进入CLI后用%resume
恢复执行 - 注入JS:
frida -U com.xxx.xxx -l xxx.js
- 在新进程中注入:
frida -U -f com.xxx.xxx -l xxx.js --no-pause
- F参数省去输入包名:
frida -U -F -l xxx.js --no-pause
,-F
会直接注入当前APP
2. Hook Java
- Hook代码必须包装在
Java.perform(function(){...})
中 - 使用
setImmediate(function() {...}
包装Java.perform,可以让已注入的代码实时更新(修改js代码,无需重启即可立即生效) - 如果目标function没有重载方法,可以直接
implementation
Hook目标函数,如果目标function有多个重载方法,需要在implementation前使用overload(xx)
。 注: Hook重载方法时,可以先不写overload,让注入报错,然后直接复制系统提示的重载方法,就省去自己写方法签名。 $init
表示构造方法,比如Hook testClz类的构造方法testClz.$init.implementation
;Java.choose(className, callbacks)
Hook没有被调用的方法、字段,示列如下:
Java.choose("com.xx.MainActivity" , {
//每次扫描到一个实例对象,就调用一次,即,该类有多少个实例,该回调就会被触发多少次
onMatch : function(instance){
console.log("Found instance: "+instance);
console.log("Result of secret func: " + instance.secret());
},
//当所有的对象都扫描完毕之后进行回调
onComplete:function(){}
});
function callSecretFun() {...}
定义导出函数,使app内的方法可以在py里面直接调用;
function callSecretFun() { //定义导出函数
Java.perform(function () { //找到隐藏函数并且调用
Java.choose("com.xx.MainActivity", {
onMatch: function (instance) {
console.log("Found instance: " + instance);
console.log("Result of secret func: " + instance.secret());
},
onComplete: function () { }
});
});
}
rpc.exports = {
callsecretfunction: callSecretFun //把callSecretFun函数导出为callsecretfunction符号,导出名不可以有大写字母或者下划线
};
py里面直接调用script.exports.callsecretfunction()
常用命令
- frida-ls-devices 列出电脑连接的设备
- frida -D devices id 连接指定设备id的设备(电脑usb连接多台设备时)
回调对象
如下示例,长下面这个样子:
{
"onMatch":function(arg1, ...){ ... },
"onComplete":function(){ ... },
}
ByteString.of是用来把byte[]数组转成hex字符串的函数, Android系统自带ByteString类:
var ByteString = Java.use("com.android.okhttp.okio.ByteString");
ByteString.of(result).hex()
打印[object]
Java.openClassFile("/data/local/tmp/r0gson.dex").load();
const gson = Java.use('com.r0ysue.gson.Gson');
console.log(gson.$new().toJson(xxx));
3.JS code
xxx.js 定位并打印对应参数
console.log("Script loaded successfully 1");
Java.perform(function x() {
// hook initEx 并打印
var test1 = Java.use('com.xxx')//定位到要hook的类名
test1.initEx.implementation = function (arg1,arg2) {//equals就是我们要hook的函数,参数个数要对应上。
console.log("initEx param1 : " + arg1);
console.log("initEx param2 : " + arg2);//打印出来真实的参数
var ret = this.equals(arg1);
return ret;//返回值 返回值最好和hook的函数逻辑一致,不然会导致程序无法继续运行。如果只是单纯要打印就无所谓
}
// hook 打印资源类容
var test1 = Java.use('com.xxx')//定位到要hook的类名
// 有的函数涉及到重载, 如果不知道怎么写,可以先不用overload,从frida的报错信息补充完整即可。
test1.a.overload('android.content.Context').implementation = function (arg1,arg2,arg3,arg4,arg5,arg6) {
console.log("initExRaw param1 : " + arg1.getResources().getString(0x7f0f140b)); // 原混淆代码为 R.getString("name"), 可以直接用资源文件里面的二进制值
console.log("initExRaw param1 : " + arg1.getResources());
console.log("initExRaw param2 : " + arg2);//打印出来真实的解锁码
}
});
console.log("Script loaded successfully 2");
4. 最终执行效果