首页
社区
课程
招聘
[原创]Android inline hook之实现原理
发表于: 2013-11-1 21:41 35320

[原创]Android inline hook之实现原理

2013-11-1 21:41
35320
标 题: Android inline hook之实现原理
作 者: 蟑螂一号
时 间: 2013-11-01 19:40:00
原 文:http://www.sanwho.com/248.html
     在android中可以通过ptrace附加进程,然后向远程进程注入so库,从而达到监控以及远程进程关键函数挂钩。目前,android上的注入hook基本上都是基于修改got表,从而达到hook拦截效果。比如,android中binder通信封装在libbinder.so里面,libbinder.so中和binder驱动打交道是通过系统调用ioctl,因此拦截ioctl并解析其中的参数,就可以获取binder通信过程中的一些关键隐私信息。然而,ioctl是libc.so中导出的一个函数符号,真正实现ioctl函数功能是在libc.so中。以4.2源码为例,libc.so中ioctl实现分为两个部分:

1.__ioctl.S代码

#include <machine/asm.h>

#include <sys/linux-syscalls.h>

ENTRY(__ioctl)
.save {r4, r7}
stmfd sp!, {r4, r7}          @r4,r7寄存器入栈
ldr r7, =__NR_ioctl      @将ioctl系统调用号保存到r7寄存器
swi #0                                   @产生中断,切换到内核模式,执行ioctl
ldmfd sp!, {r4, r7}            @r4,r7出栈
movs r0, r0                        @更新cpsr
bxpl lr
b __set_syscall_errno
END(__ioctl)
2.ioctl代码
#include <stdarg.h>

extern int __ioctl(int, int, void *);

int ioctl(int fd, int request, ...)
{
    va_list ap;
    void * arg;

    va_start(ap, request);
    arg = va_arg(ap, void *);
    va_end(ap);

    return __ioctl(fd, request, arg);

}
从上面分析可以知道ioctl是通过调用汇编实现的__ioctl完成实际功能。

相对于修改got表,还可以通过内联hook实现函数hook功能。inline hook就是在函数实现的库中,通过篡改函数执行的汇编指令而到达目的。比如假设test函数,汇编代码如下:

stmfd sp!, {r4, r5, r6, r7}

ldmfd ip, {r4, r5, r6}

str r5, [r1, #-4]
str r6, [r1, #-8]

ldr r7, =__NR_xx

swi #0
movs r0, r0
beq 1f
如果需要inline hook该test函数的话,就需要分析该函数汇编码,找到切入点,篡改函数的指令,比如在该函数中将stmfd sp!, {r4, r5, r6, r7}指令篡改为ldr  xx。当然,inline hook并不是那么容易,在inline  hook过程中需要确定代码是thum指令还是arm指令,并且还得保证篡改了指令之后执行hook函数后堆栈是平衡的,这样才能完成hook之前函数的功能。下面以一个图说明inline hook。如下图所示:
                     
如图所示,黄色表示需要hook的函数,A1,A2,A3表示func函数的实现指令。中间蓝色表示我们自己实现的拦截func函数hook,红色表示存储func函数前几条指令A1,以及回到执行指令A2的跳转指令。因此,如果需要内联hook func函数,首先需要在远程进程中申请空间,该空间会存储func函数的前几条指令,以及跳转回func函数后续指令的跳转指令。其次,需要将func函数地址重定向到hook函数。

当应用执行func函数时候,由于之前被重定向到hook函数,因此会首先执行hook的代码,在hook函数中会处理传进来的参数,处理完成之后,跳转到右边红色部分A1出,此处A1执行相当于执行func函数的A1,A1执行完之后接着执行跳转指令跳转到func A2处执行。因此,一次中转之后,func函数被完整的执行。

修改got表和内联hook比较:

1.修改got表拦截比内联hook拦截容易,只需要知道elf文件中调用外部符号的地址。

2.内联hook实现拦截的信息比修改got表丰富。比如修改got表,拦截应用中的connect函数只能拦截应用通过java 层访问网络得请求链接,然而如果应用自己通过jni方式调用socket connect方法,将会不能拦截到。如果采用内联hook,很显然,connect函数最终是需要执行实现connect功能的指令,而指令实现库已经被hook,所以能达到拦截目的。
    以上纯属个人的理解,有不足之处,请各位大牛指点指点。

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

上传的附件:
收藏
免费 0
支持
分享
最新回复 (18)
雪    币: 123
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
实在看不懂!
2013-11-2 01:11
0
雪    币: 113
活跃值: (703)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
请问楼主,是不是一个进程自己的另一个子守护进程ptrace自己以后,其他人就无法ptrace了?

因为我用gdb动态调试so的时候,如果有这种情况就失败,提示权限不够已经被子进程ptrace,如果查看没有守护进程的时候,就可以成功
2013-11-2 10:52
0
雪    币: 67
活跃值: (53)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
4
[比如修改got表,拦截应用中的connect函数只能拦截应用通过java 层访问网络得请求链接,然而如果应用自己通过jni方式调用socket connect方法,将会不能拦截到。]
这句话似乎说的不是很明白。首先通过注入,修改GOT,拦截的都应该的Native的API调用吧。其次,分析函数调用链总可以找到最底层需要调用的API或系统调用,不存在可以通过JNI调用不被拦截的问题。
HOOK JAVA层API目前看反倒要到虚拟机层的API做文章,不知道我的理解对不对。
2013-11-4 10:20
0
雪    币: 259
活跃值: (3623)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
5
[QUOTE=yaojunhap;1236662][比如修改got表,拦截应用中的connect函数只能拦截应用通过java 层访问网络得请求链接,然而如果应用自己通过jni方式调用socket connect方法,将会不能拦截到。]
这句话似乎说的不是很明白。首先通过注入,修改GOT,拦截的都应该的Native的API调用吧。其次,分析函数调用链...[/QUOTE]

   如果修改got表,只是针对特定so进行修改,比如你要拦截系统libbinder.so的ioctl函数,那样拦截的ioctl方法只能是libbinder.so中的ioctl系统调用,如果一个应用在自己的应用中通过jni方式调用了ioctl函数,那么是不能拦截的,因为jni方式实现的方法所在so文件没有被修改。
2013-11-4 11:06
0
雪    币: 67
活跃值: (53)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
6
说岔了。首先注入是针对哪个进程的,如果那个进程没有被HOOK,的确不能修改它的GOT表或PLT表实现相关功能 。其次在这个进程内,你用JNI方式调用肯定被拦截,对被HOOK函数的调用。当然其他进程调用这个函数拦截不了。我的总结是这样的,你说的INLINE HOOK相当于是改.so的代码段,所以可以全局有效,但修改难度不知道。改GOT和PLT相当于改.so的数据段,和某个进程相关,方法是成熟的,但对其他进程应该无效。
2013-11-4 11:37
0
雪    币: 181
活跃值: (45)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
一个进程同时只能被 ptrace 一次~~~
2013-11-7 13:30
0
雪    币: 113
活跃值: (703)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
多谢,那LZ这种hook方法是不是也不能用了,能不能把ptrace解开。。
2013-11-8 10:47
0
雪    币: 181
活跃值: (45)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
已经被 PTRACE_ATTACH 后,其他进程就不能在ptrace了,PTRACE_DETACH后其他进程就可以ptrace了。

就是这样呵呵。
2013-11-8 11:04
0
雪    币: 181
活跃值: (45)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
已经被 PTRACE_ATTACH 后,其他进程就不能在ptrace了,PTRACE_DETACH后其他进程就可以ptrace了。

就是这样呵呵。
2013-11-8 11:06
0
雪    币: 257
活跃值: (44)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
11
LZ啊,我试了一下,将connect拦截,将IP更改为127.0.0.1,结果就是手机的wifi功能和上网都会受到影响(wifi有时无法开启,无法获取ip,网页打不开,等等),有没有更好的思路,即不让其他应用得到手机的IP,但又不影响手机的正常上网?
2013-11-11 12:04
0
雪    币: 259
活跃值: (3623)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
12
问一下你拦截的是哪个库哦,如果你修改libc.so当然影响当大了,如果你拦截java层native层的connect函数,不会出现那种情况,并且这样拦截只针对特定应用控制。
2013-11-11 12:44
0
雪    币: 257
活跃值: (44)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
13
没错我拦截的就是libc.so的connect,如果native层是要拦截哪个文件?
还有就是,libc, native, java三个层次的拦截,各有什么优缺点,哪个更不容易被发现呢?
2013-11-11 12:54
0
雪    币: 259
活跃值: (3623)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
14
    可以提供一种思路给你解决拦截libc.so中的connect函数:
    1.在connect通过 getuid判断应用是属于系统应用还是第三方安装应用。
    2.在connect函数中通过本地socket和你的控制应用通信,从控制应用中读取需要拦截应用的uid,然后直接在connect函数处理。安装应用的uid可以通过PackageManager获取。
2013-11-11 12:58
0
雪    币: 53
活跃值: (280)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
15
静态连接libc.so  表示无压力...
除非反编译找到相关系统调用函数入口地址
2013-11-14 13:14
0
雪    币: 259
活跃值: (3623)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
16
     你这个是引用外部符号了,inline hook是修改加载实现的libc.so库
2013-11-14 15:16
0
雪    币: 207
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
有没有考虑arm和thumb指令区别对待的问题啊?
2013-12-16 10:57
0
雪    币: 2323
活跃值: (4118)
能力值: ( LV12,RANK:530 )
在线值:
发帖
回帖
粉丝
18
过来,学习
2013-12-16 11:21
0
雪    币: 47
活跃值: (43)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
19
上面的各位,听了你们的想法,小弟受到了不少启发,刚开始弄hook,我现在打算hook got表,可是发现不同的程序加载到内存后的got表的位置是不一样的,怎么能得到got表的地址呢,上面的蟑螂兄所说的修改got表可以通过外部符号得到,能说具体点吗,我不明白
2013-12-23 19:23
0
游客
登录 | 注册 方可回帖
返回
//