一、Frida是什么
Dynamic instrumentation toolkit for developers, reverse-engineers, and security researchers.
开发人员,逆向工程和安全研究人员的动态代码插桩工具。
Frida主要用于动态分析和修改运行时的程序行为(支持 Android、iOS、Windows、macOS、Linux 等平台)。它的核心功能是通过注入 JavaScript 或 Python 脚本,实时 Hook 目标应用的函数、修改内存数据或拦截通信,广泛应用于逆向工程、安全测试、漏洞挖掘等领域。
从这个描述来看,Frida主要是逆向和漏洞挖掘用的,和我们应用开发和性能优化有什么关系?事实上,这是一个极其强大,甚至某些程度上可以说无所不能的工具,为什么这么说,我们慢慢来分析。
二、Frida能做什么(基础)?
Frida可以以极低的成本,实现对Java代码的动态Hook,以及实现对Native层代码的动态Hook,举例如下:
2.1 Hook Java代码
Java
Java.perform(function () {
const LottieAnimationView = Java.use('com.airbnb.lottie.LottieAnimationView');
// Hook onDraw 方法
LottieAnimationView.onDraw.implementation = function (canvas) {
// 调用原始方法
return this.onDraw(canvas);
};
console.log("[+] LottieAnimationView.onDraw() Hook 已安装");
});
上面的代码可以实现对LottieAnimView的监听,在LottieAnimView的onDraw方法回调的时候,我们可以得到回调,可以做进一步的处理,例如判断两帧之间的时间间隔。
2.2 Hook Native代码
Java
const openAddr = Module.findExportByName("libc.so", "open");
Interceptor.attach(openAddr, {
onEnter: function (args) {
const pathPtr = args[0];
const path = pathPtr.readUtf8String();
console.log(`[libc] open() 路径: ${path}`);
},
onLeave: function (retval) {
console.log(`[libc] open() 返回值: fd=${retval}`);
}
});
这段代码会hook libc的open方法,在打开某个文件的时候打印文件路径。
三、环境搭建与使用
3.1 环境搭建
Frida的环境搭建非常简单(需要Root手机)
3.1.1 Window端
安装python环境
pip install frida-tools
pip install frida
执行frida --version,得到正常输出即为安装成功 执行
adb forward tcp:27042 tcp:27042
3.1.2 Android端
- 1、github仓库的release发行版中,找到与本地frida版本一致的tag,查找此文件:

- 2、解压得到其中的文件,将其重命名为frida-server(这一步只是用于方便使用)
- 3、adb push frida-server data/local/tmp/frida-server
- 4、adb shell chmod +x data/local/tmp/frida-server
- 5、adb shell "/data/local/tmp/frida-server &"
3.2 基本用法
3.2.1 ps
arduino
//得到手机上所有进程以及进程名,-U的意思是使用USB设备,而不是本机
frida-ps -U
记住这里的进程名,后续会用到,这里三方应用一般是中文名,系统服务是英文,是app自己设置的,不同于我们一直使用的包名。
3.2.2 加载脚本
加载脚本有两种方式,一种是直接进程存在的情况,一种是不管进程在不在,都重新拉起。 进程存在:(这种情况使用进程名,一般是中文)
frida -U -n 抖音 test.js
重新拉起:(这种情况下,需要使用包名)
frida -U -f com.ss.android.ugc.aweme test.js
| 注入模式 | 描述 | 命令或参数|优点|主要用途|
| --- | --- | --- | --- | --- |
| Spawn模式 | 将启动App的权利交由Frida来控制,即使目标App已经启动,在使用Frida注入程序时还是会重新启动App | 在CLI模式中,Frida通过加上 -f 参数指定包名以spawn模式操作App | 适合于需要在App启动时即进行注入的场景,可以在App启动时即捕获其行为 | 当需要监控App从启动开始的所有行为时使用 |
| Attach模式 | 在目标App已经启动的情况下,Frida通过ptrace注入程序从而执行Hook的操作 |在CLI模式中,如果不添加 -f 参数,则默认会通过attach模式注入App |适合于已经运行的App,不会重新启动App,对用户体验影响较小|在App已经启动,或者我们只关心特定时刻或特定功能的行为时使用 |
3.2.3 基础使用
1、在Java环境中运行
Java
Java.perform(function () {
}
2、Hook Class
Java
const TargetClass = Java.use('com.example.app.MainActivity');
TargetClass.login.implementation = function (username, password) {
console.log(`用户名: ${username}, 密码: ${password}`);
return this.login(username, password); // 继续原逻辑
};
3、打印堆栈
Java
console.log(Java.use("android.util.Log").getStackTraceString(
Java.use("java.lang.Exception").$new()
));
4、构造函数
Java
Java.use("classname").$init(params)
5、内部类
Java
Java.use("classname$innerclassname")
6、取变量里面的值
Java
obj.name.value
其中name是变量名,后面需要跟一个value,才能拿到实际的值 7、枚举某个类的实例
Java
Java.choose(className, callbacks)
例如我们想拿到Activity的实例,做一些事情,就可以用这个方式来做。
3.2.4 demo
编写一个Js文件,hook某个Class的方法,打印一行日志,将其命名为lottie.js
Java
Java.perform(function () {
const LottieAnimationView = Java.use('com.airbnb.lottie.LottieAnimationView');
// Hook onDraw 方法
LottieAnimationView.onDraw.implementation = function (canvas) {
// 调用原始方法
return this.onDraw(canvas);
};
console.log("[+] LottieAnimationView.onDraw() Hook 已安装");
});
在windows目录下,执行上面的命令即可。
四、Frida的应用
4.1 安全
目前在一些公司里面,配置了安全团队,其中有一项就是扫描应用异常的安全行为。不知道大家有没有想过,他们是如何发现一些异常问题的?例如未同意隐私协议之前不准访问网络、一些敏感信息等?并且他们甚至可以拿到应用的堆栈。可能会有人觉得是他们开发了一些非常复杂的工具、或者系统给他们开了后门。
有一个例子是安全团队扫描到我们有异常的ContentObserver调用,这里由于安全的原因就不贴截图了。大致的堆栈是我们监听了ContentObserver,报的是对媒体库的监听,没有应用的堆栈。首先安全的这个堆栈是怎么拿到的?他们正是使用Frida来实现的,只是他们监控的是实际ContentObserver发生变化的onChange,但是这个对我们没有意义,我们需要的是实际注册的地方,我们可以Hook ContentObserver的registerContentObserver方法,来得到我们实际注册的URI,用于判断URI是否敏感,也就是应用是否有敏感行为。
代码如下:
Java
Java.perform(function() {
const ContentResolver = Java.use('android.content.ContentResolver');
ContentResolver.registerContentObserver.overload(
'android.net.Uri',
'boolean',
'android.database.ContentObserver'
).implementation = function(uri, notifyForDescendants, observer) {
console.log(' URI: ' + uri.toString());
return this.registerContentObserver(uri, notifyForDescendants, observer);
};
});
除了上面的应用之外,在安全领域,Frida被大量使用,检测网络、定位等功能,不一而足。
4.2 逆向
除了安全领域,Frida也广泛的应用于逆向领域,可以用于分析混淆后的代码等。比如我们想要分析某个应用在某个地方的执行结果,例如在混淆代码中,看到某个地方的代码,想要知道执行的结果,通过常规的方式很难达成,但是可以通过Frida来达成,直接Hook这个类的方法, 在收到调用之后,可以执行自己的逻辑,将内容打印出来,并执行原有的方法。
这个实际上也可以应用于我们来debug自己的Release包,有的时候我们会遇到一些非常棘手的问题,只在Release包复现,但是debug包无法复现,这个时候我们就可以通过Frida来编写一些脚本,hook运行时,通过添加日志的形式来进行debug,避免大量没有意义的编译与验证。
举个简单的例子,遇到一个Release包才能复现的问题,我们需要在一些关键方法加日志,判断每次方法调用是否是同个对象(需要考虑混淆),代码如下:
Java
Java.perform(function () {
const HandleBitmap = Java.use('com.xx.xx.xx.xx.xx.xx$handleBitmap$1');
const UpdateLocation = Java.use('com.xx.xx.xx.xx.xx.xx$updateLocation$1');
HandleBitmap.invoke.overloads[0].implementation = function () {
console.log("handle bitmap : " + this.this$0.value.hashCode());
return this.invoke();
};
UpdateLocation.invoke.overloads[0].implementation = function () {
console.log()
const view = this.this$0.value;
if (view.k.getSecond().intValue() != this.$mY.value) {
console.log("update location : " + this.this$0.value.hashCode());
}
return this.invoke();
};
});
这样我们在updateLocation的时候,可以知道每次调用的是否是同个对象,佐证一些猜想,从而实现我们定位Bug的目的,编写Js脚本的时间非常短,借助AIGC,可能在一两分钟内就可以完成编写与调试。
五、Frida的实现原理
5.1 核心架构
Frida 的架构分为三部分:
- 主机端(Host):运行 Python/JavaScript 脚本,控制分析逻辑。
- 设备端(Target):运行 frida-server 或 frida-gadget,负责注入目标进程。
- 通信桥梁:通过 TCP/WebSocket 在主机和设备间传递数据和指令
5.2 进程注入机制
5.2.1 注入方式
frida-server(需 Root)
以高权限运行在设备上,通过 ptrace 或 LD_PRELOAD 将 Frida 的运行时库注入目标进程。
流程:
主机通过 adb 连接 frida-server。 frida-server 调用 fork() 创建子进程,附加到目标进程(如 zygote 或 App 进程)。 注入 frida-agent.so(核心引擎)到目标进程内存。
frida-gadget(非 Root)
将 frida-gadget.so 打包进目标 APK(需重签名),随应用启动自动加载。
限制:只能注入当前应用,无法控制系统进程。
5.2.2 注入后的内存布局
目标进程的内存中会加载以下模块:
frida-agent.so:核心引擎,负责脚本解析、Hook 管理和通信。
JavaScript 运行时:基于 V8 引擎(或 Duktape 轻量版),执行用户脚本。
动态生成代码:用于 Hook 的跳板代码(Trampoline)和内存补丁。
5.3 动态插桩原理
5.3.1 Java 层 Hook
实现方式 :通过修改 Android ART/Dalvik 虚拟机的运行时结构。 方法替换 :替换 Method 对象的 entry_point 或 code_item,指向 Frida 生成的代理代码。 JNI 桥接:将 Java 方法调用转发到 JavaScript 回调。
5.3.2 Native 层 Hook
Inline Hook :修改函数入口的机器码,跳转到自定义代码。 ARM/Thumb 模式 :处理指令对齐和条件跳转。 指令修复 :保存被覆盖的原始指令,在回调中恢复执行。 PLT/GOT Hook:修改动态链接表的函数地址(适用于 libc.so 等)。
六、总结
Frida是非常强大的动态代码插桩工具,可以用其开发出强大的分析工具和性能优化工具,是我们进行优化的利器。反过来说,我们的应用很容易被Frida进行攻破,如果后续有更高的安全要求,这里是绕不过去的门槛。
参考
1、www.freebuf.com/articles/mo... 2、cloud.tencent.com/developer/a...