-
-
Android inline hook
-
发表于: 2021-10-13 20:49 6652
-
原理
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 讲解