Frida简单说就是一个跨平台的hook框架,主要应用场景是app运行时,附加hook脚本,监视和修改进程中的代码, 从而实现对目标程序的深度分析和修改。
官方文档:https://frida.re/docs/examples/android/
一、Frida安装
1.1 mac安装frida及相关工具
pip3 install frida
pip3 install frida-tools
pip3 install objection
1.2 root手机安装frida-server
① 获取需要下载的frida-server版本和类型信息:
adb shell getprop ro.product.cpu.abi 查询cpu架构
frida --version 查询电脑端frida版本
②根据上面信息下载对应版本的frida-server
https://github.com/frida/frida/releases 例如:frida-server-16.4.2-android-arm64
adb push frida-server-16.4.2-android-arm64 /data/local/tmp
cd /data/local/tmp
chmod 777 frida-server-16.4.2-android-arm64
二、Frida使用
2.1 启动frida-server
adb root
cd /data/local/tmp
./frida-server-16.4.2-android-arm64
2.2 frida常用命令
frida-h 查看帮助
frida-ps -U 通过USB访问手机上的frida-server (测试frida-server启动是否成功)
2.3 frida附加脚本执行
- attach: 已存在目标进程,内部执行过程hook
frida -U -N <packageName> -l demo.js
- spawn: 未存在目标进程,在启动目标进程的过程hook
frida -U -f <packageName> -l demo.js
使用进程pid方式附加
frida -U -p <pid> -l demo.js
三、 hook java的常用实现
1)触发时机设置
setImmediate(main) //立即执行
setTimeout(main, 2000); // 延迟执行
2)代码运行在 Java 环境
Java.perform(function(){
//do something...
});
3)加载一个Java类
var StringClass = Java.use("java.lang.String");
var innerClass = Java.use("com.example.lesson5.MainActivity$InnerClass");
4)被动修改调用方法(依赖该方法在原有应用逻辑中被触发)
可修改参数、函数体实现、返回值
mainActivity.text.implementation = function(){
return "222"
}
5)重载的函数
mainActivity.text.overload('int','int').implementation = function(a, b){
console.log("调用了text重载方法 a=", a, ",b=", b)
return this.text(3,4)
}
6)实例化对象
var string = Java.use("java.lang.String").$new("abc")
7)获取调用栈
var throwInstance = Java.use("java.lang.Throwable").$new()
var thStr = Java.use("android.util.Log").getStackTraceString(throwInstance)
console.log("error=", thStr)
8)内存中找实例,主动调用类方法(不依赖该方法在原有应用逻辑中被触发)
Java.choose("com.stan.secure.MainActivity",{
onMatch:function(instance){
console.log("result=",instance.text())
},
onComplete:function(){
console.log("onComplete")
}
})
9)加载dex并使用其中的类
Java.openClassFile("/data/local/tmp/xxx.dex").load();
const a = Java.use("com.xxx.xxx.A");
四、hook native的常用实现
1)查看目标进程加载到内存中的模块(和cat /proc/<PID>/maps 效果一致)
Java.perform(() {
var modules = Process.enumerateModules()
for (var i in modules) {
var module = modules[i]
# 这里做了一些条件筛选:只列出应用内模块,这样可以观察目标应用实际加载了自身哪些动态库或者dex
if (module.path.includes('<packageName>')) {
console.log('path:', module.path, 'base:', module.base, 'size:', module.size)
}
}
})
- 查看so的导出符号
Java.perform(() {
var exportsMethods = Module.enumerateExports('libBugly.so')
for (var i in exportsMethods) {
var m = exportsMethods[i]
console.log('type:', m.type, 'name:', m.name)
}
})
- hook 具体函数
Java.perform(() {
var addr = Module.findExportByName('libBugly.so', 'JNI_OnLoad')
console.log('addr:', addr)
if (addr) {
Interceptor.attach(addr, {
onEnter(args) {
console.log('JNI_OnLoad onEnter JavaVM:', args[0])
},
onLeave(retval) {
console.log('JNI_OnLoad onLeave retval:', retval)
}
})
}
})