欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

Android下通过root实现对system_server中binder的ioctl调用拦截

Android下通过root实现对system_server中binder的ioctl调用拦截

Android下通过root实现对system_server中binder的ioctl调用拦截

2012-10-18
关键字:Android, Hook, API, Binder, 注入, 拦截
(转载请注明出处)
======================================================================
〇、引言
======================================================================
    Linux下的远程注入与HOOK网上已有不少文章与代码实现,而对于Android平台,注入有不少,但HOOK却不多。经过了两个多礼拜的研究,我初步实现了在拥有root权限的Android 2.3平台上针对system_server中binder通讯的拦截,写下来分享一下。
======================================================================
一、动态链接机制
======================================================================
    首先回顾一下Linux平台上,一个模块甲需要调用另外一个模块乙中的函数时的动态链接机制:
    1、模块甲在编译期间,将要引用的模块乙的名字与函数名写入自身的符号表。
    2、运行期模块甲调用时,调用流程是从调用代码到PLT表到GOT表再跳入模块乙。
    而如何保证模块甲的代码能从其PLT/GOT跳到正确的模块乙入口,这就是链接器做的事情。
    标准Linux链接器是ld.so,支持懒绑定,也就是说,模块甲在编译期间生成的调用模块乙的原始代码,流程是从调用代码到PLT表到链接器。运行期第一次调模块乙时,首先进入链接器,链接器根据调用信息加载模块乙搜寻其符号并将找到的函数地址填入GOT表,之后的后续调用流程就直接走PLT/GOT表了。这种机制能减少加载时的开销,为Linux发行版等采用。
    Android虽然内核基于Linux,但其动态链接机制却不是ld.so而是自带的linker,不支持懒绑定。也就是说,上述模块甲乙如果在Android平台上,则是模块甲加载时,linker就会根据模块甲中的.rel.plt表和字符串表中的内容加载模块乙并搜索其所需函数地址并预先填入GOT表。之后调用流程每次都直接走PLT/GOT表,不再进linker,PLT表中也省去了跳至linker的代码,这种流程和“勤劳”绑定类似,倒是为拦截提供了一点方便。如果拦截懒绑定的入口时模块乙还没加载地址也没找到,拦截就没法进行了。
    要拦截模块甲对乙的调用,一般思路是通过ptrace远程注入并加载一新拦截模块至模块甲,并搜索模块甲的GOT表,找到对模块乙的调用地址,改成新模块内的某函数地址,然后新模块内的这个函数在进行了自己的处理后,再跳到模块乙中。
    Android和Linux的链接器不同导致了内存布局的差异,也导致了网上流行的Linux注入与HOOK的方法行不通。网上的方法是通过ptrace注入后,搜索dynamic的section中的PLTGOT区,去里头取link_map以遍历此进程所加载的模块来搜索需要hook的函数地址。但Android上,dynamic的section的PLTGOT区前几项都是空的,没有link_map这个数据结构,只能通过分析/proc/<pid>/maps来遍历模块。
======================================================================
二、Binder拦截选址
======================================================================
    Binder是Andorid上的轻量级跨进程通讯机制,由用户空间的libbinder.so和内核的binder驱动协作构成。一次完整的Binder调用的流程(拿对system_server中的Service的调用举例)是从用户进程到用户进程加载的libbinder.so到ioctl到binder驱动并阻塞,Service端在等待时通过libbinder.so收到驱动传上来的调用请求,把数据整好后通过libbinder.so再通过ioctl返回给驱动,之前用户端阻塞的ioctl收到应答而返回,回到libbinder.so再回到用户进程,从而完成了一次完整的调用请求。
    注意,这里用户进程空间所加载的libbinder.so和system_server端加载的libbinder.so在逻辑上不是同一个东西。正因为不是同一个东西,我们才能针对system_server进程中加载的libbinder.so动手,拦截其GOT表中对ioctl的调用,从而提前知道Service要返回的内容(如果想改,则需要分析Binder数据再改了)。这个ioctl就是拦截的选址所在。
======================================================================
三、具体实现
======================================================================
----------------------------------------------------------------------
3.1 实现思路
----------------------------------------------------------------------
    在尝试了各种思路并失败了很多次后,最终确定下来拦截system_server进程中的binder通讯的思路如下:
    1、以root身份运行注入程序,通过ptrace停止并附加system_server。
    2、远程注入shellcode,加载注入的共享库并解除附加,让其调用共享库中的一特定函数。
    3、此特定函数将库中待接替ioctl的新函数地址以及ioctl的真实地址写入Android的Property供外界使用。
    4、注入程序通过Android的Property获得ioctl的原始地址以及接替ioctl的新函数地址。
    5、注入程序再次通过ptrace附加system_server,定位libbinder.so中名为.got的Section,并搜索其项寻找ioctl的原始地址。
    6、找到GOT表中的原始地址后将其替换为接替ioctl的新函数地址。
    7、解除附加system_server让其重新运行,完成拦截。
    其中,1和2在网上有现成的实现,是一个叫LibInject的包,其中有inject.c/h以及Android.mk,还有个大牛给出的shellcode.s。不过这段shellcode加载共享库并调用后会立即dlclose卸载之,不符合我们常驻的需求,因此我又写了个新共享库让shellcode加载的共享库调用,多了一步。此库最终常驻system_server的内存。
----------------------------------------------------------------------
3.2 注入共享库中的新函数实现
----------------------------------------------------------------------
    在这个常驻system_server进程内的共享库里,只实现了简单几个函数,其中do_hook函数在注入后通过外界调用,它不做具体的hook动作,仅仅只是把所需的两个函数地址写入Android的Property供外界使用:

注意:上传附件及图片大小不得大于30M。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途。
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功