第一篇 linux ptrace I
在之前的文章中我们用ptrace函数实现了查看系统调用参数的功能。在这篇文章中,我们会用ptrace函数实现设置断点,跟代码注入功能。
参考资料
Playing with ptrace, Part I
Playing with ptrace, Part II
英文好的推荐直接看老外的文章。但是其代码是运行在x86系统上的,本文中将其移植到了x86_64系统。
进程附加
在之前的文章中,我们都是trace自己程序fork出来的子进程,现在我们来看一下如何trace一个正在运行的进程。
trace一个正在运行的进程称为进程附加(attach)。使用的是ptrace函数的PTRACE_ATTACH参数。当一个进程成功附加到一个正在运行的进程时,此进程会成为被附加进程的父进程,同时向被附加的进程发送一个SIGSTOP信号,让其停止,这时我们就可以对其进行操纵。当我们完成对tracee的操作后就可以使用ptrace的PTRACE_DETACH参数停止附加。
我们用一个循环来模拟一个正在运行的进程,下边称此程序为hello
int main() { int i; for(i = 0;i < 10; ++i) { printf("My counter: %d ", i); sleep(2); } return 0; }
本文之后所有的程序都以此程序当作被附加的进程。在其运行之后我们可以使用 ps -h 命令查看其进程号(pid),以便我们通过进程号对其附加。
接下来看一个简单的进程附加的例子
#include <sys/types.h> #include <sys/reg.h> #include <sys/user.h> #include <sys/wait.h> #include <sys/ptrace.h> #include <stdio.h> #include <unistd.h> #include <stdlib.h> int main(int argc, char *argv[]) { pid_t traced_process; struct user_regs_struct regs; long ins; if(argc != 2) { puts("no pid input"); exit(1); } traced_process = atoi(argv[1]); printf("try to trace pid :%u ",traced_process); if(ptrace(PTRACE_ATTACH,traced_process,NULL,NULL)==-1) { perror("trace error:"); } wait(NULL); if(ptrace(PTRACE_GETREGS,traced_process,NULL,®s)==-1) { perror("trace error:"); } ins = ptrace(PTRACE_PEEKTEXT,traced_process,regs.rip,NULL); if(ins == -1) { perror("trace error:"); } printf("EIP:%llx Instruction executed: %lx ",regs.rip,ins); if(ptrace(PTRACE_DETACH,traced_process,NULL,NULL)==-1) { perror("trace error:"); } return 0; }
上边的程序对hello进行了附加,等其停下来以后,读取hello要运行的下一条指令的内容(地址存在rip中)。读取之后停止附加,让hello继续运行。
设置断点
下面要实现的是许多调试器都拥有的设置断点功能。
设置断点的原理:假如要在A地址处设置断点,可以把A地址处的指令替换为一条trap指令,就是说当tracee运行完这条被替换的指令后会自动停止,然后告知tracer自己已停止。
代码如下:
#include <sys/ptrace.h> #include <sys/reg.h> #include <sys/user.h> #include <sys/wait.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #define LONG_SIZE 8 void getdata(pid_t child, long addr,char *str,int len) { char *laddr = str; int i = 0,j = len/LONG_SIZE; union u{ long val; char chars[LONG_SIZE]; } word; while(i<j) { word.val = ptrace(PTRACE_PEEKDATA,child,addr + i*LONG_SIZE,NULL); if(word.val == -1) perror("trace error"); memcpy(laddr,word.chars,LONG_SIZE); ++i; laddr += LONG_SIZE; } j = len %LONG_SIZE; if(j!=0) { word.val == ptrace(PTRACE_PEEKDATA,child,addr + i*LONG_SIZE,NULL); if(word.val == -1) perror("trace error"); } str[len] = '