Frida 入门系列(二):使用 Frida Hook Native 函数(C/C++层)
引言
在上一篇文章中,我们介绍了如何使用 Frida Hook Android 应用中的 Java 函数。然而,在实际逆向分析中,很多关键逻辑是通过 Native 层(C/C++) 实现的,例如加密算法、网络通信、反调试机制等。
本文将带你了解如何使用 Frida 对 Native 函数进行 Hook,掌握基本的 Native Hook 技巧,并提供完整的示例代码和操作步骤。同时,我们将重点介绍两种常见场景:
如何 Hook 导出函数
如何 Hook 未导出函数
什么是 Native Hook?
Native Hook 指的是对运行在 C/C++ 层的函数进行动态拦截与修改。与 Java 层不同,Native 层的函数通常位于 .so 动态链接库中,且不暴露给 Java 虚拟机直接调用,因此 Hook 难度更高。
Frida 提供了强大的 Native Hook 支持,包括:
查找函数地址
注册 Hook 回调
修改寄存器/内存值
打印调用堆栈
返回自定义值
环境准备
1. 安装 Frida 工具链(回顾)
请确保你已按照上一篇文章完成以下配置:
1 | pip install frida-tools |
或 Node.js 版本:
1 | npm install -g frida |
上传并启动 frida-server 到设备上(armeabi-v7a / arm64-v8a)
1 | adb push frida-server /data/local/tmp/ |
示例场景
假设我们的目标 App 中存在一个 Native 函数:
1 | extern "C" JNIEXPORT jboolean JNICALL |
我们需要 Hook verify() 函数,查看其输入参数并篡改返回值。
编写 Frida Native Hook 脚本
下面是完整的 Frida Hook Native 函数脚本示例:
1 | function hook_native_verify() { |
如何运行脚本
方法一:命令行执行
1 | frida -U -n com.example.app -l hook_native.js |
方法二:Python 控制脚本
1 | import frida |
技术点解析
1. Module.findBaseAddress("libname.so")
获取指定 .so 文件的加载基地址。这是定位函数地址的基础。
2. baseAddr.add(offset)
结合函数偏移地址,计算出函数的完整地址。
偏移地址可以通过 IDA Pro、Ghidra、objdump 等工具获得。
3. Interceptor.attach(address, callbacks)
注册对某个地址的 Hook 回调,支持 onEnter 和 onLeave 两个阶段。
onEnter: 函数即将被执行时触发onLeave: 函数即将返回时触发
4. args[n]
表示函数传入的第 n 个参数,是一个 NativePointer 类型对象,可读取内存内容:
readCString()/readUtf8String():读取字符串toInt32():转为整数add(offset):指针偏移
5. retval.replace(value)
强制修改函数返回值,实现绕过校验、激活功能等目的。
进阶技巧:Hook 导出函数 vs 未导出函数
在 Native Hook 中,根据函数是否被导出,我们可以分为两类处理方式:
✅ Hook 导出函数(Exported Function)
如果目标函数是导出函数(出现在 .plt 表中),可以直接通过名字查找:
示例:Hook JNI 函数 Java_a_b_k0801_MainActivity_stringFromJNI
1 | function hook_exported_function() { |
✅ Hook 未导出函数(Unexported Function)
未导出函数不会出现在 .plt 表中,需要通过静态分析找到其偏移地址后再进行 Hook。
示例:Hook 地址为 0x1E250 的未导出函数
1 | function hook_unexported_function() { |
进阶技巧汇总
✅ 枚举所有模块
列出当前进程加载的所有模块:
1 | Process.enumerateModules({ |
✅ 内存 dump
打印指定地址附近的机器码:
1 | console.log(hexdump(addr, { offset: 0, length: 32, header: true, ansi: true })); |
✅ 使用 Memory.scan 查找特征码
当无法确定函数地址时,可以使用字节码特征匹配:
1 | Memory.scan(baseAddr, size, "FF 00 ?? 11 ?? ?? 00 00", { |
常见问题与解决方案
❓ Hook 地址无效?
确保
.so加载成功检查偏移是否正确(注意 PIE 偏移)
使用
Memory.scan()查找特定字节码特征
❓ 参数类型不确定?
可尝试打印多个寄存器或指针偏移
使用
Memory.read*()直接读取内存
❓ Hook 失败?
确保设备 root 并运行
frida-serverFrida 无法 Hook 未加载的函数,可以延迟 Hook 或监听模块加载事件
法律风险提示
本文内容仅用于合法授权的安全测试和研究,请勿用于非法用途。任何未经授权的系统入侵、数据篡改行为均属违法行为。
结语
通过本文,你已经掌握了 Frida 在 Native 层进行 Hook 的基础方法,包括查找函数地址、Hook 调用流程、修改参数和返回值等。这些技术对于分析 APK 中隐藏的加密算法、绕过反调试机制、提取密钥等任务至关重要。
下一期我们将介绍 Frida 高级技巧:Hook C++ STL 函数、处理 C++ 异常、Hook JNI 接口函数等,敬请期待!