改造 Cydia Substrate 框架用于函数内代码的HOOK

  上一次分析了Cydia Substrate so hook 框架的实现,实际使用中,发现这样的框架并不能满足我的一些需求,比如我要知道一个函数内部某处代码的运行时的寄存器值,用原始的框架就无法做到。

  想实现的功能是只要指定一个地址,就可以打印该处代码执行时的寄存器环境、HOOK的地址以及线程的TID,同时支持多个地址的添加。

  先实现一个通用的消息打印函数

  void printAllReg(int Reg_R0,int Reg_R1,int Reg_R2,int Reg_R3,int Stack_SP,int Stack_SP2)

  {

  int *pSP2 = &Stack_SP2;

  int Reg_R12 = *(pSP2+1);

  int Reg_R11 = *(pSP2+2);

  int Reg_R10 = *(pSP2+3);

  int Reg_R9 = *(pSP2+4);

  int Reg_R8 = *(pSP2+5);

  int Reg_R7 = *(pSP2+13);

  int Reg_R6 = *(pSP2+12);

  int Reg_R5 = *(pSP2+11);

  int Reg_R4 = *(pSP2+10);

  char *pFormat="Hookaddr:0x%08x tid=%x R0:0x%08x R1:0x%08x R2:0x%08x R3:0x%08x
R4:0x%08xR5:0x%08x R6:0x%08x R7:0x%08x R8:0x%08x R9:0x%08x R10:0x%08x R11:0x%08xR12:0x%08x ";

  LOGD(pFormat,(Stack_SP2-1-(int)pSSELibBase),gettid(),Reg_R0,Reg_R1,Reg_R2,Reg_R3,Reg_R4,Reg_R5,Reg_R6,Reg_R7,Reg_R8,Reg_R9,Reg_R10,Reg_R11,Reg_R12);

  }

  里面具体的参数意义后面再讲。

  printAllReg要由一个中转函数来调用,这个函数必须满足如下要求:

  1.调用前后不能改变R0-R12,LR寄存器的值

  2.调用完打印函数后,跳到old函数

  3.把监控地址入栈,这个要求比较关键,不然打印出来不知道是哪处代码在打印,也就没有意义了。

  根本满足这三点要求,C语言函数肯定是不行的,只有用arm汇编来实现了

  void HookAddr()

  {

  __asm__(

  "PUSH    {R0-R7}
"//保存可能会改变的寄存器

  "mov r4,r8 
"

  "PUSH  {R4}
"

  "mov r4,r9 
"

  "PUSH  {R4}
"

  "mov r4,r10 
"

  "PUSH  {R4}
"

  "mov r4,r11 
"

  "PUSH  {R4}
"

  "mov r4,r12 
"

  "PUSH  {R4}
"//将r8-r12入栈,直接push {R8}这样的指令是不支持的

  "ldr r7,=0x1a2b3001
"//0x1a2b3001是什么意思?

  "PUSH  {R7}
"

  "PUSH  {LR}
"//将返回地址入栈,printAllReg执行完后,lr寄存器会变掉

  "ldr r7, =printAllReg 
"//将两个地址入栈后,调用打印函数

  "BLX r7 
"

  "POP   {R7} 
"

  "mov lr,r7 
"//恢复lr

  "POP   {R7} 
"

  "POP   {R4} 
"

  "mov r12,r4 
"//r12经过rlx后,会变掉,这里专门恢复一下

  "POP    {R0-R3} 
" //平衡堆栈

  "POP    {R0-R7} 
" //恢复r0-r7

  "MOV R0,R0
"   //让下面的bx pc指令4字节对齐

  "BX PC
"//转到arm模式,以进行跳转

  "MOV R0,R0
"//占位

  "MOV R0,R0
"//占位,这里后面会被patch掉

  "MOV R0,R0
"//占位,这里后面会被patch掉

  "MOV R0,R0
"//占位,这里后面会被patch掉

  "BX  LR
"

  );

  这段函数经过编译后,用IDA反汇编的结果如下:

改造 Cydia Substrate 框架用于函数内代码的HOOK

  红框里的指令都是我们需要patch的指令

  0x1A2B3001为这值占用了四个字节,这个值是要入栈给printAllReg的,我们在这里写入HOOK的地址,就能被printAllReg函数所用了。

  再回头来看下printAllReg里int Reg_R12 =*(pSP2+1);这样指令的作用,从中转函数我们知道,调用printAllReg时,堆栈里值是这样排列的LR,HOOK Addr,R12,R11,R10,R9,R8,R7,R6,R5,R4,

  Stack_SP2指向的就是HookAddr,向后递推,就可以把R12-R4的地址读出来了,当然也可以把改下printAllReg的调用界面,把这些值全放参数里传过来。

  第一个红框,patch成跳转指令,0x16e0放把下四个字节内容加载到PC的指令,0x16e4放跳转地址

  使用如下函数进行patch

  经过这个函数patch后的中转函数,如下图所示

改造 Cydia Substrate 框架用于函数内代码的HOOK

  每个hook都会分配一段代码内存用于保存这样的中转函数,以后需要监控一个指定函数时,只需要调用setAddrHook,输入地址就可以了。限于Cydia Substrate框架的限制,输入的hook地址指向的前面12个字节最好不要被跳转所分割。

  也可以按此方法对HOOK函数的过滤函数进行通用化改造,实现输入函数地址即可打印函数调用信息的界面。

    文章来源:http://chenjava.blog.51cto.com/374566/1591851

 

上一篇:安卓防火墙 PS DroidWall

下一篇:Ossim系统常见测试方法