简单使用入门
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| # 推送到服务器 adb root adb push frida-server-17.24.3-android-x86_64 /data/local/tmp adb shell
# 启动frida服务端 cd /data/local/tmp chmod 755 frida-server-17.24.3-android-x86_64 /data/local/tmp ./frida-server-17.24.3-android-x86_64 /data/local/tmp
# adb建立端口映射 # frida-server 启动后,默认监听127.0.0.1:27042
# 情况一:USB 直连(`frida -U`)—— 不需要端口映射 frida-ps -U 验证是否能列出进程
# 情况二:网络连接(`frida -H`)—— 需要端口映射或直连 # 建立转发:PC 的 27042 → 设备的 27042 adb forward tcp:27042 tcp:27042
adb -s 127.0.0.1:16416 forward --list 通过本地 27042 端口连接设备上的 Frida-server frida-ps -H 127.0.0.1:27042
|
基础语法
| API名称 |
描述 |
Java.use(className) |
获取指定的Java类并使其在JavaScript代码中可用。 |
Java.perform(callback) |
确保回调函数在Java的主线程上执行。 |
Java.choose(className, callbacks) |
枚举指定类的所有实例。 |
Java.cast(obj, cls) |
将一个Java对象转换成另一个Java类的实例。 |
Java.enumerateLoadedClasses(callbacks) |
枚举进程中已经加载的所有Java类。 |
Java.enumerateClassLoaders(callbacks) |
枚举进程中存在的所有Java类加载器。 |
Java.enumerateMethods(targetClassMethod) |
枚举指定类的所有方法。 |
日志输出
| 日志方法 |
描述 |
区别 |
| console.log() |
使用JavaScript直接进行日志打印 |
多用于在CLI模式中,console.log()直接输出到命令行界面,使用户可以实时查看。在RPC模式中,console.log()同样输出在命令行,但可能被Python脚本的输出内容掩盖。 |
| send() |
Frida的专有方法,用于发送数据或日志到外部Python脚本 |
多用于RPC模式中,它允许JavaScript脚本发送数据到Python脚本,Python脚本可以进一步处理或记录这些数据。 |
常用API
hook框架模板
1 2 3 4 5 6 7
| function main(){ Java.perform(function(){ hookTest1(); }); } setImmediate(main);
|
- Hook普通方法、打印参数和修改返回值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| function hookTest1(){ var utils = Java.use("类名"); utils.method1.implementation = function(a, b){ a = 123; b = 456; var retval = this.method1(a, b); console.log(a, b, retval); return retval; } }
|
- Hook重载参数
这里需要注意,hook重载函数的时候需要加overload指定,不知道也可以直接运行,frida的报错会给出提示。
1 2 3 4 5 6 7 8 9 10 11 12
|
function hookTest2(){ var utils = Java.use("com.zj.wuaipojie.Demo"); utils.Inner.overload('com.zj.wuaipojie.Demo$Animal','java.lang.String').implementation = function(a,b){ b = "aaaaaaaaaa"; this.Inner(a,b); console.log(b); } }
|
- Hook构造函数
1 2 3 4 5 6 7 8 9
| function hookTest3(){ var utils = Java.use("com.zj.wuaipojie.Demo"); utils.$init.overload('java.lang.String').implementation = function(str){ console.log(str); str = "52"; this.$init(str); } }
|
- Hook字段
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| function hookTest5(){ Java.perform(function(){ var utils = Java.use("com.zj.wuaipojie.Demo"); utils.staticField.value = "我是被修改的静态变量"; console.log(utils.staticField.value); Java.choose("com.zj.wuaipojie.Demo", { onMatch: function(obj){ obj._privateInt.value = "123456"; obj.privateInt.value = 9999; }, onComplete: function(){
} }); });
}
|
- Hook内部类
1 2 3 4 5 6 7 8 9 10 11
| function hookTest6(){ Java.perform(function(){ var innerClass = Java.use("com.zj.wuaipojie.Demo$innerClass"); console.log(innerClass); innerClass.$init.implementation = function(){ console.log("eeeeeeee"); }
}); }
|
- 枚举所有的类与类的所有方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| function hookTest7(){ Java.perform(function(){ Java.enumerateLoadedClasses({ onMatch: function(name,handle){ if(name.indexOf("com.zj.wuaipojie.Demo") !=-1){ console.log(name); var clazz =Java.use(name); console.log(clazz); var methods = clazz.class.getDeclaredMethods(); console.log(methods); } }, onComplete: function(){} }) }) }
|
- 枚举所有方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| function hookTest8(){ Java.perform(function(){ var Demo = Java.use("com.zj.wuaipojie.Demo"); var methods =Demo.class.getDeclaredMethods(); for(var j=0; j < methods.length; j++){ var methodName = methods[j].getName(); console.log(methodName); for(var k=0; k<Demo[methodName].overloads.length;k++){ Demo[methodName].overloads[k].implementation = function(){ for(var i=0;i<arguments.length;i++){ console.log(arguments[i]); } return this[methodName].apply(this,arguments); } } } }) }
|
- 主动调用
静态方法
1 2
| var ClassName=Java.use("com.zj.wuaipojie.Demo"); ClassName.privateFunc("传参");
|
非静态方法
1 2 3 4 5 6 7 8 9 10 11 12 13
| var ret = null; Java.perform(function () { Java.choose("com.zj.wuaipojie.Demo",{ onMatch:function(instance){ ret=instance.privateFunc("aaaaaaa"); }, onComplete:function(){ } }); })
|
FridaHook辅助算法分析
- 找到一些疑似关键函数,可以通过hook来确认app执行某个操作的时候,是否调用了它们
- 如果没有触发这些函数,考虑以下问题
- app在执行这个操作的时候,真的没有调用这个函数,换一个其他的关键函数
- 代码写错了,导致hook函数没执行,比如.implementation写成了.implementations,不报错,但是不执行。
- 一般可以通过主动调用上层函数,来触发这些hook函数
- 如果触发了这些函数,可以通过hook来打印执行过程中传入函数的参数和返回值
- frida -U -F -I HookDemo.js-U代表远程USB设备-F代表附加到最前的这个app-|后面指明需要加载的S脚本
- 写好的js脚本要注入手机端,并不是在Node.js中使用,所以只能用v8和fridaAPI支持的代码
Hook可以做什么
可以用来判断app执行某个操作的时候,是否经过我们的怀疑的这个函数
可以用来修改被hook函数的运行逻辑
可以用来在运行过程中,获取被hook的函数传入的具体的参数和返回值
可以用来主动调用app中的某些函数