Android Libinject X86平台EIP-2的分析

原创
2014/06/30 23:34
阅读数 132

在看雪上看到古河大牛的android-LibInject,平台是arm版的。

想测试x86平台上的android-Libinject,搜索有人已经完成(blog.csdn.net/jinzhuojun/article/details/9900105)。

 

其中获取目标进程库函数地址的代码如下,特别的,x86平台的函数地址=实际函数地址+2。

 

这个+2的操作让人蛋疼,起初以为是ELF在X86平台上的特性。

在GDB中调试,发现函数地址并不需要+2。

调试发现,是因为如下的函数ptrace_call设置eip的时候需要把eip加2。

 

作如下测试,

设置错误的eip,Regs->eip=0,目标进程段错误,崩溃eip=-2;

设置错误的eip,regs->eip=10,目标进程段错误,崩溃eip=8

 

测试表明:X86版本的ptrace_call,确实需要eip=eip+2。

根据windows的经验,怎么也没明白为啥eip-2。

设置目标进程eip=funX,结果目标进程从funX-2开始执行,蛋疼。

 

测试GDB调试,设置目标进程的EIP,目标进程从EIP开始执行,蛋更疼了。

据说GDB也是用的ptrace接口,为啥GDP设置EIP就从EIP开始执行,而ptrace_call设置EIP却从EIP-2的地址开始执行。

 

请教原文作者,答复“可能是ABI的原因,有的平台并不需要”。

请教其它大牛,估计是被太多邮件淹没了。

 

在看雪上看到有文章说,需要在ptrace_attach之后先执行ptrace_syscall

 

测试表明,这篇文章说的没问题。但作者也没有为什么必须要先ptrace_syscall。

对比不添加ptrace_syscall和ptrace_syscall,ptrace_getreg获得EIP都是相同的,说明ptrace _attach返回时,目标进程的EIP都是相同的。那添加的ptrace_syscall又做了哪些额外的工作,导致执行结果不一样。

 

不明就里,如鲠在喉!一统搜索,没有结果!只有去看linux内核相关的书籍了。

在阅读完《linxu内核源码分析》的ptrace和signal章节后,找到了最终的原因。

 

EIP-2内幕

我的目标程序源码如下:

 

 

我的注入程序代码如下:

 

注入程序在ptrace_attach目标程序的时候,目标程序因为执行sleep(1)处于休眠状态。

此时EIP为系统调用的用户态返回地址,如下的0xb7fe2424。无论是sysenter方式还是int 0x80方式,返回EIP都是该地址。

 

 

Ptrace_attach首先向目标进程发送SIGSTOP信号,该信号把本来处于休眠的进程唤醒。然后进入等待目标进程状态发生改变。

 

目标进程被唤醒后经内核调度开始执行,开始执行其实是从原来的休眠处开始的,显然继续执行将会结束sleep系统调用,并且返回结果不是sleep满足,而是sleep被中断了。

目标进程准备退出sleep系统调用,在返回到用户态EIP之前,内核检测自身是否存在信号量。显然,赤裸裸的躺着注入程序发送的SIGSTOP信号。于是,转入执行SIGSTOP信号处理,该信号处理把进程状态为STOP,挂起自身,激活注入进程的wait调用。

 

注入进程wait调用返回,ptrace_getregs获取的EIP是确确实实的目标进程将来返回到用户态执行的EIP。

 

注入进程ptrace_setreg设置的EIP也确确实实是目标进程将来要返回到用户态执行的EIP。

 

注入进程ptrace_continue给目标进程发送信号SIGCON,信号发送本身将唤醒挂起的目标进程。

 

目标进程因为收到的SIGCON信号,恢复执行。此时进程的SIGSTOP信号即将处理完毕,系统检测该到信号来自系统调用,并且该信号处理不是由用户程序处理,这就意味着该信号导致了本系统调用失败,需要自动重新执行该系统调用。自动重新本系统调用的方法:恢复用户态寄存器EAX为系统调用号,用户态EIP=EIP-2。而EIP-2恰好就是int 80系统调用指令。

 

目标进程处理完信号后,开始执行系统调用返回到用户态。此时用户态的EIP由于-2的原因,不再是原来的pop ebp指令,而是int 80指令。因此返回到用户态后,自动重新执行本系统调用。

 

因此,EIP-2的本质原因,在于ptrace_attach的时候目标进程因系统调用进入了休眠,而attach发送的信号导致了目标进程调用中断返回,系统为了弥补中断返回的系统调用,在信号处理中将EIP-2来迫使中断的系统调用返回后自动重启本系统调用。

 

为何ptrace_attach+ptrace_syscall就没有EIP-2的问题?

在ptrace_attach后加入ptrace_syscall后不会导致eip-2,原因在于ptrace_syscall仅仅是设置目标进程的标志位,没有发送任何信号,也没有中断目标进程的任何系统调用。目标进程是主动在系统调用之前检查该标志位,主动挂起自己。这种条件下,注入进程设置目标进程eip,目标进程的系统调用自然是返回到设置的eip。

 

Ptrace_attach一定会有问题吗?

NO。attach时刻目标进程如果不是陷入系统调用,就不会触发自动重启系统调用导致的EIP-2。

 

ptrace_attach+ptrace_syscall一定安全吗?

NO。在ptrace_syscall+ptrace_setreg+ptrace_continue后,目标进程开始进行系统调用,如果系统调用是可中断的阻塞调用,在阻塞等待过程中如果因为接受到其它信号,导致系统调用中断返回,那么系统会因为自动重启系统调用而设置eip-2,并且系统期待的eip-2处的代码为int 80。显然,注入进程设置的eip为某个函数,而eip-2就是个不伦不类的东西。

 

注:最后两个问题,没有实际测试,只是推断;SIGCON信号对于目标进程只是简单的忽略。

不知道ARM平台存不存在因为自动重启系统调用而导致的EIP-2的问题。


展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部