- 什么是 Hook? (核心思想)
- 为什么需要 Hook? (应用场景)
- Hook 的基本原理 (技术基石)
- Android Hook 技术体系 (从易到难)
- Java Hook (基于反射和动态代理)
- Native Hook (基于 Frida)
- Native Hook (基于 Inline Hook)
- 主流 Hook 框架简介
- Hook 技术的挑战与局限
什么是 Hook?(核心思想)
Hook 的中文意思是“钩子”,在计算机领域,Hook 技术是一种在程序运行时,动态地修改其执行流程的方法。

你可以把它想象成:
在一条正常的道路上(原始代码逻辑),你设置了一个收费站(Hook 点),所有经过的车辆(函数调用、方法调用)都必须在这里停下来,你可以选择:
- 检查车辆(读取参数、返回值)。
- 放行车辆(让它继续走原来的路)。
- 扣留车辆(阻止它继续执行)。
- 换一辆车(让它执行另一个函数的逻辑)。
这个“收费站”Hook 技术,它让我们能够“劫持”程序的正常执行,并注入我们自己的逻辑。
核心三要素:

- Hook 点:你想要拦截的位置,通常是一个函数、一个方法、一个类,甚至是某个系统 API。
- Hook 函数:当程序执行到 Hook 点时,实际被调用的函数,这个函数由我们自己编写,里面包含我们想要执行的逻辑。
- Hook 链:当 Hook 函数执行完毕后,如何处理原始逻辑,是直接返回,还是继续执行原始函数,或者执行另一个函数。
为什么需要 Hook?(应用场景)
Hook 技术非常强大,应用场景广泛:
-
安全与逆向工程:
- Crack/破解:绕过软件的注册验证、付费墙、逻辑判断等,Hook
boolean isPurchased()方法,让它永远返回true。 - 抓包:Hook App 网络请求相关的 API(如
OkHttp的execute方法),可以获取到所有明文的请求数据,无需 Root 证书。 - 分析 App 行为:Hook
Logcat输出函数、SharedPreferences读写方法,了解 App 的运行状态和数据存储。
- Crack/破解:绕过软件的注册验证、付费墙、逻辑判断等,Hook
-
功能增强与自动化测试:
- 插件化开发:在不重新安装主 App 的情况下,动态加载和运行新功能模块,需要 Hook AMS(Activity Manager Service)来欺骗系统,让系统认为插件中的 Activity 是主 App 的。
- 自动化测试:Hook UI 相关的代码,模拟用户点击、输入等操作,实现自动化 UI 测试。
- 热修复:在 App 运行时,将旧的、有问题的方法替换为新的、修复后的方法,这通常需要替换方法的实现。
-
系统级监控与调试:
(图片来源网络,侵删)- 性能分析:Hook
System.currentTimeMillis()或System.nanoTime(),可以精确测量代码块的执行时间。 - 日志增强:Hook
Log类,为所有日志添加统一的 TAG、时间戳、线程信息等。
- 性能分析:Hook
Hook 的基本原理
Hook 技术的核心原理可以总结为:“偷梁换柱”。
要替换一个函数,最直接的方法是修改它的入口地址,在计算机中,函数的入口地址通常存储在一个叫做函数指针或方法表的地方。
- 在 Native 层 (C/C++):函数指针就是一个内存地址,Hook 的本质就是修改这个地址,让它指向我们新写的函数,当程序试图调用原函数时,实际上会跳转到我们新函数的地址。
- 在 Java 层:Java 是动态语言,方法信息存储在方法区的运行时常量池中,Java 中的“函数指针”是
Method对象,Hook 的本质就是修改Method对象中的invokeNative指针,让它指向我们的新方法。
Hook 的通用步骤是:
- 定位 Hook 点:找到目标函数/方法在内存中的位置或其引用。
- 备份原始逻辑:保存原始函数的入口地址或
Method对象,以便后续可以调用它(即“恢复”或“传递”)。 - 编写 Hook 函数:创建一个新函数,实现你想要的功能。
- 替换:将 Hook 点的地址或引用,修改为你新编写的 Hook 函数的地址。
- 执行 Hook:将修改后的代码注入到目标进程中并执行替换操作。
Android Hook 技术体系
Android 是一个混合型系统,包含 Java/Kotlin 层和 C/C++ 的 Native 层,Hook 技术也分为两大类。
A. Java Hook (基于反射和动态代理)
这是最常见、最容易上手的 Hook 方式,主要作用于 Java 层。
核心技术:
-
Java 反射:
- 作用:在运行时获取任意类的 Class 对象,并操作其构造器、字段、方法。
- 在 Hook 中的应用:通过反射拿到
Method对象,然后获取其方法句柄,进而修改其实现,很多框架底层都依赖反射来找到目标。
-
动态代理:
- 作用:为某个接口创建一个代理对象,当调用接口方法时,会先进入代理对象的
invoke方法,由我们决定如何处理。 - 在 Hook 中的应用:特别适合 Hook 接口类型的方法,Hook
OnClickListener的onClick方法,我们可以创建一个代理OnClickListener,在onClick中先打印日志,然后再调用原始的onClick方法。
- 作用:为某个接口创建一个代理对象,当调用接口方法时,会先进入代理对象的
示例:Hook 一个普通方法
假设我们要 Hook android.widget.Toast.makeText() 方法,让它无论传入什么文本,都显示 "Hello Hook!"。
- Hook 点:
Toast.makeText()。 - Hook 函数:我们自己的
makeText()方法。 - 实现思路:
- 通过反射获取
Toast类中的makeText方法。 - 使用
setAccessible(true)确保可以访问。 - 使用
Method.setAccessible(false)和Method.invoke()来调用原始方法。 - 将
makeText方法的实现替换为我们的新逻辑。
- 通过反射获取
Java Hook 的局限性:
- 无法 Hook final 类和方法:
final关键字在编译时和运行时都阻止了重写和替换。 - 性能开销:反射和动态代理比直接调用慢。
- 无法 Hook Native 方法:Java 层的 Hook 无法触及 C/C++ 代码。
B. Native Hook (基于 Frida)
Java Hook 无法满足所有需求,特别是当目标逻辑在 Native 层(.so 文件)时,这时就需要 Native Hook。
Frida 是目前最主流、最强大的动态插桩工具,它简化了 Native Hook 的过程。
Frida 的工作原理: Frida 在目标进程中注入一个 JavaScript 脚本,这个脚本可以调用 Frida 提供的 API,来 Hook 目标进程中的函数,当被 Hook 的函数被调用时,Frida 会暂停该函数的执行,然后执行我们 JavaScript 脚本中定义的回调函数,在这个回调函数里,我们可以:
- 读取函数参数。
- 修改函数参数。
- 调用原始函数。
- 修改函数返回值。
- 甚至不调用原始函数,直接返回我们自己的值。
Frida 示例:Hook Native 函数
假设一个 Native 库 libnative.so 中有一个函数 int add(int a, int b),我们想让它返回 a * b 而不是 a + b。
// 1. 找到目标库
var soAddr = Module.findBaseAddress("libnative.so");
// 2. 定义要 Hook 的函数地址
var addAddr = soAddr.add(0x1234); // 假设 add 函数在 so 的偏移 0x1234 处
// 3. 定义回调函数
var addImplementation = new NativeCallback(function(a, b) {
console.log(`Original add called with a=${a}, b=${b}`);
// 不调用原始函数,直接返回 a * b
return a * b;
}, "int", ["int", "int"]);
// 4. Hook 目标函数
Interceptor.replace(addAddr, addImplementation);
console.log("Hooked add function!");
优点:
- 跨平台:支持 Windows, macOS, Linux, Android, iOS。
- 语言友好:使用 JavaScript,上手快,生态丰富。
- 功能强大:不仅能 Hook C 函数,还能 Hook Java/Kotlin 方法、ObjC 方法等。
- 无需源码:只要有可执行文件即可。
C. Native Hook (基于 Inline Hook)
Frida 很方便,但它依赖于一个运行时环境,有时不够“底层”,Inline Hook(内联 Hook)是一种更底层、更强大的 Native Hook 技术,通常由 C/C++ 实现,如 XHook, PLT Hook, Inline Hook 等。
Inline Hook 的核心原理 (以 ARM32 为例):
- 备份原始指令:将被 Hook 函数入口处的几条机器指令(通常是
BL或BLX跳转指令)读取出来,存入一个缓冲区。 - 构造跳转指令:构造一条跳转指令,指向我们自己的 Hook 函数地址。
- 修改内存权限:将目标函数入口处的内存页权限改为可写。
- “偷梁换柱”:将构造好的跳转指令写入到被 Hook 函数的入口处,覆盖掉原始指令。
- 执行 Hook:当程序调用该函数时,会跳转到我们的 Hook 函数。
在 Hook 函数中,我们通常会:
- 恢复原始指令:在自己的 Hook 函数开头,先写回之前备份的原始指令。
- 执行原始逻辑:通过一个
BLX跳转,跳转到被 Hook 函数的第二条指令处,继续执行原始逻辑。 - 执行 Hook 逻辑:在原始逻辑执行完毕后,再执行我们自己的代码。
优点:
- 性能高:直接修改机器码,几乎没有额外开销。
- 底层强大:可以 Hook 任何函数,不受限于 Frida 的实现。
- 隐蔽性强:比 Frida 注入更难被检测。
缺点:
- 复杂度高:需要处理不同 CPU 架构(ARM32, ARM64, x86)的指令差异,代码编写繁琐。
- 风险高:直接修改内存,容易导致程序崩溃。
- 平台相关:不同平台的实现方式不同。
主流 Hook 框架简介
- Xposed:Java Hook 的王者,它通过替换
Zygote进程的AndroidRuntime中的关键方法,在 App 启动时就为其加载一个XposedBridge.jar,从而实现了对几乎所有 Java 方法的 Hook,需要 Root。 - YAHFA (Yet Another Hook Framework for Android):Xposed 的一个替代品,更轻量级,与 ART 虚拟机兼容性更好。
- Frida:当前最流行的动态插桩工具,兼顾了 Java 和 Native Hook,是安全研究员和开发者的首选。
- Dobby:一个轻量级的、跨平台的 Native Hook 库,专注于 Inline Hook,性能优秀。
- Android-Inline-Hook-ART:一个专门针对 Android ART 虚拟机的 Inline Hook 框架,在 ART 环境下表现良好。
Hook 技术的挑战与局限
- 稳定性:Hook 操作涉及内存修改,容易导致目标 App 或系统崩溃,错误的 Hook 可能引发不可预知的问题。
- 兼容性:不同 Android 版本、不同 CPU 架构、不同厂商的 ROM,其底层实现可能不同,导致 Hook 失效,Inline Hook 在不同 ART 版本下指令长度可能不同。
- 反 Hook/反调试:目标 App 可能会检测自身是否被 Hook 或被调试,从而采取措施阻止 Hook(如
ptrace反调试、检测关键内存页是否被修改)。 - 性能开销:虽然 Inline Hook 性能很高,但 Java Hook 和 Frida 脚本执行都有一定的性能开销,不适合对性能要求极高的场景。
- 技术门槛:Inline Hook 等底层技术需要深厚的汇编、操作系统和编译原理知识。
| 特性 | Java Hook (Xposed等) | Native Hook (Frida) | Native Hook (Inline) |
|---|---|---|---|
| 作用层 | Java/Kotlin | Java/Kotlin + C/C++ | C/C++ |
| 核心技术 | 反射、动态代理 | 动态插桩、进程注入 | 机器码修改、跳转指令 |
| 易用性 | 高 | 中 | 低 |
| 性能 | 低 | 中 | 高 |
| 灵活性 | 受限于 final | 极高 | 极高 |
| 依赖 | Root (Xposed) | 无需 Root | 无需 Root |
| 主要场景 | 破解、插件化、热修复 | 安全研究、逆向、通用 Hook | 高性能监控、底层破解 |
如何选择?
- 如果目标是 Java 层,且需要快速实现:优先考虑 Xposed 或 YAHFA。
- 如果目标是 Native 层,或者需要跨平台、快速原型验证:Frida 是不二之选。
- 如果对性能有极致要求,或者需要 Hook Frida 无法触及的底层函数:选择 Inline Hook 框架(如 Dobby)。
Hook 技术是一把双刃剑,它既是黑客和逆向工程师的利器,也是开发者和测试工程师增强工具箱的强大武器,理解其原理和适用场景,能帮助我们更好地解决实际问题。
