Android Hook框架adbi源码浅析

【Android Hook框架adbi源码浅析】世事洞明皆学问,人情练达即文章。这篇文章主要讲述Android Hook框架adbi源码浅析相关的知识,希望能为你提供帮助。
二、libbase其实上面加载完SO库后,hook的功能我们完全可以自己在动态库中实现。而adbi作者为了方便我们使用,编写了一个通用的hook框架工具即libbase库。
libbase依然在解决两个问题:1.获取要hook的目标函数地址;2.给函数打二进制补丁即inline hook。
关于获取hook函数地址的方法这里不再赘述。直接看inline hook部分,这部分功能在basehook.c的hook()函数中实现,先看hook_t结构体:

struct  hook_t  {         unsigned  int  jump[3];   //跳转指令(ARM)         unsigned  int  store[3];   //原指令(ARM)         unsigned  char  jumpt[20];   //跳转指令(Thumb)         unsigned  char  storet[20];   //原指令(Thumb)         unsigned  int  orig;   //被hook函数地址         unsigned  int  patch;   //补丁地址         unsigned  char  thumb;   //补丁代码指令集,1为Thumb,2为ARM         unsigned  char  name[128];   //被hook函数名         void  *data; };

hook_t是一个标准inline hook结构体,保存了跳转指令/跳转地址/指令集/hook函数名等信息。因为ARM使用了ARM和Thumb两种指令集,所以代码中需进行区分:
if  (addr  %  4  ==  0)  {         /*  ARM指令集  */ }  else  {          /*  Thumb指令集  */ }

这样进行判断的依据,是编译器在使用Thumb指令集编译一个函数时,会自动将真正映射地址的最后一位置’1’赋给符号地址,这样可以实现无缝的Thumb指令集函数与Arm指令集代码混编。
接下来看一下ARM指令集分支的处理流程,这是该问题解决的核心部分:
if  (addr  %  4  ==  0)  {         log("ARM  using  0x%lx ",  (unsigned  long)hook_arm)         h-> thumb  =  0;         h-> patch  =  (unsigned  int)hook_arm;         h-> orig  =  addr;         h-> jump[0]  =  0xe59ff000;   //  LDR  pc,  [pc,  #0]         h-> jump[1]  =  h-> patch;         h-> jump[2]  =  h-> patch;         for  (i  =  0;   i  <   3;   i++)                                                                                                    h-> store[i]  =  ((int*)h-> orig)[i];         for  (i  =  0;   i  <   3;   i++)                                                                                                            ((int*)h-> orig)[i]  =  h-> jump[i]; }

首先填充hook_t结构体,第一个for循环保存原地址处3条指令共12字节。第二个for循环用新的跳转指令进行覆写,关键的三条指令分别保存在jump[0]-[2]中:
jump[0]赋值0xe59ff000,翻译成ARM汇编为ldr pc,[pc,#0],由于pc寄存器读出的值是当前指令地址加8,因此这条指令实际是将jump[2]的值加载到pc寄存器。
jump[2]保存的是hook函数地址。jump[1]仅用来4字节占位。Thumb分支原理与ARM分支一致,不再分析。
接下来我们注意到,函数最后调用了一处hook_cacheflush()函数:
hook_cacheflush((unsigned  int)h-> orig,  (unsigned  int)h-> orig+sizeof(h-> jumpt));

我们知道,现代处理器都有指令缓存,用来提高执行效率。前面我们修改的是内存中的指令,为防止缓存的存在,使我们修改的指令执行不到,需进行缓存的刷新:
void  inline  hook_cacheflush(unsigned  int  begin,  unsigned  int  end) {         const  int  syscall  =  0xf0002;         __asm  __volatile  (                 "mov          r0,  %0 "                 "mov          r1,  %1 "                 "mov          r7,  %2 "                 "mov          r2,  #0x0 "                 "svc          0x00000000 "                 :                 :      "r"  (begin),  "r"  (end),  "r"  (syscall)                 :      "r0",  "r1",  "r7"                 ); }

参考资料
[1].adbi源码 https://github.com/crmulliner/adbi
[2].minghuasweblog,ARM Cache Flush on mmap’d Buffers with __clear_cache(),March 29, 2013
 





    推荐阅读