在看雪上看到古河大牛的android-LibInject,平台是arm版的。linux
想测试x86平台上的android-Libinject,搜索有人已经完成(blog.csdn.net/jinzhuojun/article/details/9900105)。android
其中获取目标进程库函数地址的代码以下,特别的,x86平台的函数地址=实际函数地址+2。windows
这个+2的操做让人蛋疼,起初觉得是ELF在X86平台上的特性。安全
在GDB中调试,发现函数地址并不须要+2。函数
调试发现,是由于以下的函数ptrace_call设置eip的时候须要把eip加2。源码分析
做以下测试,测试
设置错误的eip,Regs->eip=0,目标进程段错误,崩溃eip=-2;spa
设置错误的eip,regs->eip=10,目标进程段错误,崩溃eip=8.net
测试代表: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的问题。