Android Arm Inline Hook

什么是Inline Hook

Inline Hook即内部跳转Hook,通过替换函数开始处的指令为跳转指令,使得原函数跳转到自己的函数,通常还会保留原函数的调用接口。与GOT表Hook相比,Inline Hook具有更广泛的适用性,几乎可以Hook任何函数,不过其实现更为复杂,考虑的情况更多,并且无法对一些太短的函数Hook。
其基本原理请参阅网上其他资料。

需要解决的问题

1.Arm模式与Thumb模式的区别
2.跳转指令的构造
3.PC相关指令的修正
4.线程处理
5.其他一些细节

Arm模式与Thumb模式的区别

arm版本7及以上的体系中,其指令集分为ARM指令集和Thumb指令集。Arm指令为4字节对齐,每条指令长度均为32位;Thumb指令为2字节对齐,又分为Thumb16、Thumb32,其中Thumb16指令长度为16位,Thumb32指令长度为32位。

在对一个函数进行Inline Hook时,首先需要判断当前函数指令是Arm指令还是Thumb指令,指令使用目标地址值的bit[0]来确定目标地址的指令类型。bit[0]的值为1时,目标程序为Thumb指令;bit[0]值为0时,目标程序为ARM指令。其相关实现代码为以下宏:

// 设置bit[0]的值为1
#define SET_BIT0(addr)      (addr | 1)
// 设置bit[0]的值为0
#define CLEAR_BIT0(addr)    (addr & 0xFFFFFFFE)
// 测试bit[0]的值,若为1则返回真,若为0则返回假
#define TEST_BIT0(addr)     (addr & 1)

跳转指令的构造

跳转指令主要分为以下两种:

1.B系列指令:B、BL、BX、BLX
2.直接写PC寄存器

Arm的B系列指令跳转范围只有4M,Thumb的B系列指令跳转范围只有256字节,然而大多数情况下跳转范围都会大于4M,故我们采用LDR PC, [PC, ?]构造跳转指令。另外Thumb16指令中并没有合适的跳转指令,如果单独使用Thumb16指令构造跳转指令,需要使用更多的指令完成,并且在后续对PC相关指令的修正也更加繁琐,故综合考虑下,决定放弃对ARMv5的支持。

另外,Arm处理器采用3级流水线来增加处理器指令流的速度,也就是说程序计数器R15(PC)总是指向“正在取指”的指令,而不是指向“正在执行”的,即PC总是指向当前正在执行的指令地址再加2条指令的地址。比如当前指令地址是0×8000, 那么当前pc的值,在thumb下面是0×8000 + 2 2, 在arm下面是0×8000 + 4 2。
对于Arm指令集,跳转指令为:

LDR PC, [PC, #-4]
addr

LDR PC, [PC, #-4]对应的机器码为:0xE51FF004,addr为要跳转的地址。该跳转指令范围为32位,对于32位系统来说即为全地址跳转。
对于Thumb32指令集,跳转指令为:

LDR.W PC, [PC, #0]
addr

LDR.W PC, [PC, #0]对应的机器码为:0x00F0DFF8,addr为要跳转的地址。同样支持任意地址跳转。
其相关实现代码为:

// Arm Mode
if (TEST_BIT0(item->target_addr)) {
    int i;
    i = 0;
    if (CLEAR_BIT0(item->target_addr) % 4 != 0) {
        ((uint16_t *) CLEAR_BIT0(item->target_addr))[i++] = 0xBF00;  // NOP
    }
    ((uint16_t *) CLEAR_BIT0(item->target_addr))[i++] = 0xF8DF;
    ((uint16_t *) CLEAR_BIT0(item->target_addr))[i++] = 0xF000; // LDR.W PC, [PC]
    ((uint16_t *) CLEAR_BIT0(item->target_addr))[i++] = item->new_addr & 0xFFFF;
    ((uint16_t *) CLEAR_BIT0(item->target_addr))[i++] = item->new_addr >> 16;
}
// Thumb Mode
else {
    ((uint32_t *) (item->target_addr))[0] = 0xe51ff004; // LDR PC, [PC, #-4]
    ((uint32_t *) (item->target_addr))[1] = item->new_addr;
}

首先通过TEST_BIT0宏判断目标函数的指令集类型,其中若为Thumb指令集,多了下面一个额外处理:

if (CLEAR_BIT0(item->target_addr) % 4 != 0) {
    ((uint16_t *) CLEAR_BIT0(item->target_addr))[i++] = 0xBF00;  // NOP
}

对bit[0]的值清零,若其值4字节不对齐,则添加一个2字节的NOP指令,使得后续的指令4字节对齐。这是因为在Thumb32指令中,若该指令对PC寄存器的值进行了修改,则该指令必须是4字节对齐的,否则为非法指令。

ARM HOOK 图解
原理图

HOOK流程图

这里写图片描述
PC相关指令的修正

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值