最近需要用到Android so hook,于是分析了一下比较流行的Cydia Substrate框架
CydiaSubstrate框架的核心函数是MSHOOKFunction,官方使用说明如下:
现在Android 默认编译出来的都是thumb指令集的,就分析一下这个模式下的HOOK吧。
在使用MSHOOKFunction HOOK前,先用IDA attach到进程先看下准备HOOK的函数,前面18个字节的二进制指令如下:
52ABF480 30 B5 PUSH {R4,R5,LR}
52ABF482 8D B0 SUB SP, SP, #0x34
52ABF484 05 1E SUBS R5, R0, #0
52ABF486 10 D1 BNE loc_52ABF4AA
52ABF488 0C 1E SUBS R4, R1, #0
52ABF48A 28 D0 BEQ loc_52ABF4DE
52ABF48C 15 49 LDR R1, =(aSign - 0x52ABF496)
52ABF48E 02 23 MOVS R3, #2
52ABF490 5B 42 NEGS R3, R3
HOOK后的指令如下:
52ABF480 78 47 BX PC
52ABF480 ;---------------------------------------------------------------------------
52ABF482 C0 DCB 0xC0 ;
52ABF483 46 DCB 0x46 ; F
52ABF484 ; ---------------------------------------------------------------------------
52ABF484 CODE32
52ABF484
52ABF484 loc_52ABF484 ; CODE XREF:_F14700dj
52ABF484 04 F0 1F E5 LDR PC, =(sub_58F7C000+1)
52ABF484 ; ---------------------------------------------------------------------------
52ABF488 01 C0 F7 58 off_52ABF488 DCDsub_58F7C000+1 ; DATA XREF:_F14700d:loc_52ABF484r
52ABF488 ; End of function _F14700d
52ABF488
52ABF48C ; ---------------------------------------------------------------------------
52ABF48C ; START OF FUNCTION CHUNK FORsub_58F7D000
52ABF48C CODE16
52ABF48C
52ABF48C loc_52ABF48C ;CODE XREF: sub_58F7D000:loc_58F7D010j
52ABF48C ; DATA XREF: sub_58F7D000:loc_58F7D010o ...
52ABF48C 15 49 LDR R1, =(aSign - 0x52ABF496)
52ABF48E 02 23 MOVS R3, #2
52ABF490 5B 42 NEGS R3, R3
HOOK前后对比,发现函数的前面12个字节的指令被替换了,这12个字节指令就是实现了一个跳转功能,跳转到过滤函数sub_58F7C000。仔细看一下,还是觉得有点奇怪的地方,LDR PC,=(sub_58F7C000+1)这个指令比较明显就是跳转的功能,那为前面的BX PC只是为了跳转到这条指令,为什么要这样用?而不是直接使用LDR PC,=(sub_58F7C000+1)进行跳转呢,BX PC是不是多此一举呢?
后来经过手动patch测试后发现,这条指令不可缺少的,因为LDR PC,=(sub_58F7C000+1)指令是arm指令集的,而我们的原始函数使用的是thumb指令集,直接在函数开头放上arm指令集的指令,CPU不会把它识别为arm指令,仍然把它做为thumb指令,这样就会执行出错了。这里的BX PC可进行指令集切换,这样就可以正常执行了。
另外这里的LDR PC,=(sub_58F7C000+1)指令使用sub_58F7C000+1做为跳转地址,而不是sub_58F7C000,也是因为指令集的原因,尾巴上带1的指令告诉CPU,sub_58F7C000函数是thumb指令集的,不带1的话,就被认为是arm指令集了
MSHookFunction会返回一个old地址,old地址指向的函数,需要实现原函数相同的功能,但是这里的前面12个字节已经被覆盖掉了,是不是old可以直接执行原来的12个字节后跳转回0x52ABF48C就可以了呢?
我们直接来看下old指向的地址(在IDA里转成CODE的时候有的时候会失败,是因为它默认把指令当成arm指令集来识别,按Alt+G,value设为1改成thumb模式再转就可以)
debug141:58F7D000 30 B5 PUSH {R4,R5,LR}
debug141:58F7D002 8D B0 SUB SP, SP, #0x34
debug141:58F7D004 05 1E SUBS R5, R0, #0
debug141:58F7D006 0D D1 BNE sub_58F7D024
debug141:58F7D008 0C 1E SUBS R4, R1, #0
debug141:58F7D00A 05 D0 BEQ sub_58F7D018
debug141:58F7D00C 78 47 BX PC
debug141:58F7D00C ;---------------------------------------------------------------------------
debug141:58F7D00E C0 DCB 0xC0 ;
debug141:58F7D00F 46 DCB 0x46 ; F
debug141:58F7D010 ;---------------------------------------------------------------------------
debug141:58F7D010 CODE32
debug141:58F7D010
debug141:58F7D010 loc_58F7D010 ; CODE XREF:sub_58F7D000+Cj
debug141:58F7D010 04 F0 1F E5 LDR PC, =(loc_52ABF48C+1)
debug141:58F7D010 ; End of function sub_58F7D000
debug141:58F7D010
debug141:58F7D010 ;---------------------------------------------------------------------------
debug141:58F7D014 8D F4 AB 52 off_58F7D014 DCDloc_52ABF48C+1 ; DATA XREF:sub_58F7D000:loc_58F7D010r
debug141:58F7D018 CODE16
debug141:58F7D018
debug141:58F7D018 ; =============== S U B R O U T IN E =======================================
debug141:58F7D018
debug141:58F7D018 ; Attributes: thunk
debug141:58F7D018
debug141:58F7D018 sub_58F7D018 ; CODE XREF: sub_58F7D000+Aj
debug141:58F7D018 78 47 BX PC
debug141:58F7D018 ;---------------------------------------------------------------------------
debug141:58F7D01A C0 DCB 0xC0 ;
debug141:58F7D01B 46 DCB 0x46 ; F
debug141:58F7D01C ;---------------------------------------------------------------------------
debug141:58F7D01C CODE32
debug141:58F7D01C
debug141:58F7D01C loc_58F7D01C ; CODE XREF:sub_58F7D018j
debug141:58F7D01C 04 F0 1F E5 LDR PC, =(loc_52ABF4DE+1)
debug141:58F7D01C ;---------------------------------------------------------------------------
debug141:58F7D020 DF F4 AB 52 off_58F7D020 DCD loc_52ABF4DE+1 ; DATA XREF:sub_58F7D018:loc_58F7D01Cr
debug141:58F7D020 ; End of function sub_58F7D018
debug141:58F7D020
debug141:58F7D024 CODE16
debug141:58F7D024
debug141:58F7D024 ; =============== S U B R O U T IN E =======================================
debug141:58F7D024
debug141:58F7D024 ; Attributes: thunk
debug141:58F7D024
debug141:58F7D024 sub_58F7D024 ; CODE XREF:sub_58F7D000+6j
debug141:58F7D024 78 47 BX PC
debug141:58F7D024 ;---------------------------------------------------------------------------
debug141:58F7D026 C0 DCB 0xC0 ;
debug141:58F7D027 46 DCB 0x46 ; F
debug141:58F7D028 ; ---------------------------------------------------------------------------
debug141:58F7D028 CODE32
debug141:58F7D028
debug141:58F7D028 loc_58F7D028 ; CODE XREF:sub_58F7D024j
debug141:58F7D028 04 F0 1F E5 LDR PC, =(sub_52ABF4AA+1)
debug141:58F7D028 ;---------------------------------------------------------------------------
debug141:58F7D02C AB F4 AB 52 off_58F7D02C DCDsub_52ABF4AA+1 ; DATA XREF:sub_58F7D024:loc_58F7D028r
debug141:58F7D02C ; End of function sub_58F7D024
这段内存是新分配的内存,整个指令比原来长很多,除了后面增加了很多指令,第4条指令和第6条指令也跟原来不一样了。
分析可知,第4和第6条指令都为条件短跳指令,这里要从0x58F7D006跳到0x52ABF4AA仅通过一个字节的偏移已经跳不回去了,所以需要把这里的短跳转换为长跳,这里先跳转到0x58F7D024近处的指令,再使用远跳指令跳到0x52ABF4AA除了跳转指令,BL,BLX这样的CALL指令,还有涉及到PC值的指令,也需要经过类似的转换才行。如果前面12个字节不需要转换的话,直接使用原来的12字节指令就可以了。
总结:
Cydia Substrate框架的
优点:
相比调用不停重写函数前面一些指令的框架,更为稳定,支持多线程并发运行。old函数处理较好,基本上需要替换的指令都能比较好的支持
缺点:
arm代码编译后比较紧凑,函数之间不留有空隙,thumb指令集在不影响其它寄存器的条件下,12个字节的跳转指令长度已经到达极限,太短的函数是无法做inline hook的。
即使长度够的话,也并不是完美的,举个会发生异常的例子:如果12个字节之后的指令有往最前面12字节内回跳的话,这样就会挂掉