ADR指令对substate HOOK的影响

0x00 前置信息

在用substrate对某个函数进行HOOK的时候发现会奔溃,并且崩溃在了一个很奇怪的地址上。在排除了参数列表和调用约定的问题之后,本文着手分析产生该奔溃的原因。在分析之前,首先会简要说明Inline HOOK的实现原理,接着借助崩溃信息和动态调试进行分析,最后总结崩溃的一般性原因。

0x01 Inline HOOK 的原理

Inline HOOK是指通过修改函数前N个汇编指令跳转到HOOK函数从而实现API HOOK的一种方式。具体修改的汇编指令与函数编译后的指令集相关,本文讨论范围限定在thumb指令集。substratethumb模式下进行Inline HOOK时,会修改函数前12个字节的指令,修改后的指令形式如下:

0000: BX PC
0002: ...
0004: LDR PC, [PC, #-4]
0008: the address to jump(hook function)

首先通过BX PC指令跳转到0x0004的位置(thumb模式下PC寄存器的地址是当前运行指令地址加4),并且把指令集从thumb切换到arm。在0x0004处执行LDR PC, [PC, #-4]指令,即把0x0008处的地址保存到PC寄存器中,0x0008处保存的就是HOOK函数的地址,此时就实现控制流的跳转。

第二步就是如何从HOOK函数中跳回原始函数,在HOOK函数中调用原始函数时,首先会跳入到一块新分配的内存区域代码段,此代码段保存了原始函数的前12个字节的指令,执行完后,再通过跳转指令跳回到原始函数12个字节之后的位置。这样,就完成了全部Inline HOOK的过程。

0x02 崩溃分析

奔溃日志如下:

I/DEBUG: signal 11 (SIGSEGV), code 2 (SEGV_ACCERR), fault addr 74daa000
I/DEBUG:     r0 00000000  r1 74f14fb8  r2 75cbebe8  r3 75cbebe8
I/DEBUG:     r4 74da8001  r5 00000000  r6 00001be0  r7 be8da0c8
I/DEBUG:     r8 758f40ec  r9 00005f52  sl 00000c30  fp 4199d0d8
I/DEBUG:     ip 74f14fb8  sp be8da090  lr 41589293  pc 74daa000  cpsr 40070030

这个崩溃地址0x74daa000不在我们要HOOK函数所在动态库的地址范围内,另外可知substrateHOOK时新开辟的内存段起始地址是0x74da9000,也就是崩在内存段起始地址+0x1000处,不过知道这些并不能判断出到底为什么奔溃的,下面根据动态调试的信息开始分析原因。

首先看一下我们要HOOK的原始函数前若干字节的汇编指令:

.text:0000BF78 07 B5                       PUSH            {R0-R2,LR}
.text:0000BF7A 08 A1                       ADR             R1, sub_BF9C
.text:0000BF7C 09 46                       MOV             R1, R1
.text:0000BF7E A1 F1 05 01                 SUB.W           R1, R1, #5
.text:0000BF82 00 46                       MOV             R0, R0
.text:0000BF84 08 46                       MOV             R0, R1
.text:0000BF86 12 46                       MOV             R2, R2
.text:0000BF88 00 F1 2C 00                 ADD.W           R0, R0, #0x2C
.text:0000BF8C 03 90                       STR             R0, [SP,#0x10+var_4] 
.text:0000BF8E 07 BD                       POP             {R0-R2,PC} 

函数开始保存寄存器环境之后,用ADR指令获取一个地址,然后通过STRR0中存储的地址保存到栈中保存LR寄存器所在的位置,最后通过POP PC实现跳转到0xBFC2(0xBF9C - 5 + 0x2C)的位置。这里实际就是用代码混淆技术实现了一个跳转。

HOOK后,原函数的汇编指令如下所示:

xxx.so:75A7DF78 DCB 0x78 ; x
xxx.so:75A7DF79 DCB 0x47 ; G
xxx.so:75A7DF7A DCB 0xC0 ; 
xxx.so:75A7DF7B DCB 0x46 ; F
xxx.so:75A7DF7C DCB    4
xxx.so:75A7DF7D DCB 0xF0 ; 
xxx.so:75A7DF7E DCB 0x1F
xxx.so:75A7DF7F DCB 0xE5 ; 
xxx.so:75A7DF80 DCB 0x5D ; ]
xxx.so:75A7DF81 DCB 0x92 ; 
xxx.so:75A7DF82 DCB 0x58 ; X
xxx.so:75A7DF83 DCB 0x41 ; A
xxx.so:75A7DF84 ; ---------------------------------------------------------------------------
xxx.so:75A7DF84 CODE16
xxx.so:75A7DF84
xxx.so:75A7DF84 loc_75A7DF84                            ; CODE XREF: debug107:loc_74DA9010•j
xxx.so:75A7DF84                                         ; DATA XREF: debug107:loc_74DA9010•o ...
xxx.so:75A7DF84 MOV             R0, R1
xxx.so:75A7DF86 MOV             R2, R2
xxx.so:75A7DF88 ADD.W           R0, R0, #0x2C
xxx.so:75A7DF8C STR             R0, [SP,#0xC]
xxx.so:75A7DF8E POP             {R0-R2,PC}
xxx.so:75A7DF8E ; ---------------------------------------------------------------------------

修改后的开始12个字节的指令含义IDA分析不清楚,可根据第一节描述的指令形式去理解,可知从第12字节之后位置的指令没有变化。

如下是substrate开辟的内存空间,即调用void MSHookFunction(Type_ *symbol, Type_ *replace, Type_ **result)第三个参数返回给用户的地址,从0x74DA9000开始的前12个字节即是原函数的前12个字节,这里关注一下0x74DA9002处的ADR R1, unk_74DA9024,执行后R1的值变成0x74DA9024

debug107:74DA9000 AREA debug107, CODE, ALIGN=0
debug107:74DA9000 ; ORG 0x74DA9000
debug107:74DA9000 CODE16
debug107:74DA9000 PUSH            {R0-R2,LR}
debug107:74DA9002 ADR             R1, unk_74DA9024
debug107:74DA9004 MOV             R1, R1
debug107:74DA9006 SUB.W           R1, R1, #5
debug107:74DA900A MOV             R0, R0
debug107:74DA900C BX              PC
debug107:74DA900C ; ---------------------------------------------------------------------------
debug107:74DA900E DCB 0xC0 ; 
debug107:74DA900F DCB 0x46 ; F
debug107:74DA9010 ; ---------------------------------------------------------------------------
debug107:74DA9010 CODE32
debug107:74DA9010
debug107:74DA9010 loc_74DA9010                            ; CODE XREF: debug107:74DA900C•j
debug107:74DA9010 LDR             PC, =(loc_75A7DF84+1)
debug107:74DA9010 ; ---------------------------------------------------------------------------
debug107:74DA9014 off_74DA9014 DCD loc_75A7DF84+1         ; DATA XREF: debug107:loc_74DA9010•r
debug107:74DA9018 DCB    0
debug107:74DA9019 DCB    0
debug107:74DA901A DCB    0
debug107:74DA901B DCB    0
debug107:74DA901C DCB    0
debug107:74DA901D DCB    0
debug107:74DA901E DCB    0
debug107:74DA901F DCB    0
debug107:74DA9020 DCB    0
debug107:74DA9021 DCB    0
debug107:74DA9022 DCB    0
debug107:74DA9023 DCB    0
debug107:74DA9024 unk_74DA9024 DCB    0                   ; DATA XREF: debug107:74DA9002•o

当从0x74DA9010跳回原函数时,原函数的指令如下所示:

xxx.so:75A7DF84 MOV             R0, R1
xxx.so:75A7DF86 MOV             R2, R2
xxx.so:75A7DF88 ADD.W           R0, R0, #0x2C
xxx.so:75A7DF8C STR             R0, [SP,#0xC]
xxx.so:75A7DF8E POP             {R0-R2,PC}
xxx.so:75A7DF8E ; ----------------------------------------------------------------------

注意此刻R1的地址已经是0x74DA9024了,经过2次运算后值变成了0x74da903b并保存在栈中,那么在执行POP {R0-R2,PC}后,PC中的指令就变成了0x74da903b,控制流又跳转到了0x74da903b,我发现从0x74da9018 - 0x74daa000这部分的内存值全是0,猜想应该是substrate开辟了0x1000字节大小的空间,以至于进一步奔溃在了0x94daa000。至此,根据我们动态调试分析的结果与崩溃日志一致。

综上,发现在HOOK完后,程序控制流在返回原函数之后跳转到了与原函数不一致的地址导致程序崩溃。究其原因,就是ADR指令取到了一个“错误”的地址,这里错误只是一个相对的概念。因为ADR是一个PC相关的指令,当运行环境变化时PC寄存器的值不一样了,导致ADR取到的地址发送变化。

0x03 总结

当做Inline HOOK时,如果前12个字节中出现PC相关的指令就需要做指令修正。如下是几个常见的PC相关指令:

1. ADR RD, <LABEL>
2. LDR RD, <LABEL>
3. MOV RD, PC

当然,这只是substrate的Bug,并不是说如果前12字节存在PC相关指令时就无法进行Inline HOOK,只是需要额外的指令修正逻辑。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值