-
-
Android inline hook
-
发表于: 2021-10-13 20:49 6879
-
原理
Inline Hook 是修改原方法头几个指令,将其跳转到新方法去执行,以 ARM 指令为例,正常函数调用如图所示

调用 FuncA 方法实际是根据 FuncA_ptr 找到 FuncA 指令块执行的。
Inline Hook 要做的是修改正常调用流程,主要是通过修改 PC 寄存器值完成跳转
1 2 | LDR PC, [PC, #-4]addr |
Arm 处理器采用3级流水线来增加处理器指令流的速度,也就是说程序计数器 R15(PC) 总是指向“正在取指”的指令,而不是指向“正在执行”的,即 PC 总是指向当前正在执行的指令地址再加 2 条指令的地址。比如当前指令地址是 0×8000, 那么当前 pc 的值,在 thumb 下面是 0×8000 + 2 * 2, 在 arm 下面是 0×8000 + 4 * 2。
所以 ARM 执行到 LDR PC, [PC, #-4] 指令是,[PC, #-4] 代表的位置为当前指令的下一条指令,即 addr 代表的值。将 addr 写入 PC 寄存器后代表程序跳到 addr 处执行,如此就完成了函数的替换。替换后只是完成了第一步,因为必须提供方式调用原方法。
第二步是提供调用原函数功能,做法是开辟一块空间,记为 trampoline,将原方法的前两条指令放到 trampoline 中,然后在 trampoline 的第三条指令中加入
1 2 | LDR PC, [PC, #-4]addr2 |
其中 addr2 为原方法的第三条指令地址,这样调用 trampoline 就相当于调用原方法了。大体过程如下图:

第 1 步
修改 FuncA 函数前两个汇编指令,程序中再调用 FuncA_ptr 时,会经过 FuncA 前两条指令跳到 new-add 地址(即,方法 FuncNew)处执行,红线以下的会直接忽略,这样就做到了虽然调用的是 FuncA 函数,其实内部执行的确实 FuncNew 函数。
第 2 步
增加 Proto 方法,作用是执行原 FuncA 方法,方法 FuncNew 中调用 Proto 方法,就完成了整个 hook 过程,即,在 FuncNew 函数中既能获取到原函数 FuncA 的参数,也能获取到原函数 FuncA 的返回值。
以上分析属于原理分析,规避了很多问题,比如
- thumb 指令该如何处理
- arm64 指令该如何处理
- 原函数前两个指令中有跳转指令该如何处理
- 多线程中如何处理
- PC 相关指令的修正
- 其他未知情况
(inline hook 框架就是帮助处理以上问题的)
参考
Android-Arm-Inline-Hook:arm 架构下的 inline hook 讲解
Android-Arm-Inline-Hook Github 地址
Hookzz: arm64 架构下的 inline hook 讲解