ptrace应用系列-基础知识

来源:互联网 发布:阿里云国际版 200m 编辑:程序博客网 时间:2024/05/21 09:11

http://blog.csdn.net/estate66/article/details/6019435

Linux提供了ptrace系统函数,使得父进程得以控制和监视其它进程。当使用ptrace跟踪子进程后,所有发给子进程的信号(除了SIGKILL)外,都会被父进程截获,而此时子进程阻塞,并且被标记为TASK_TRACED。父进程收到信号后,可以查看和修改子进程的内核映像和寄存器。父进程完成所要做的工作之后可以选择让子进程继续执行还是终止。

     在i386体系中,应用程序的系统调用基本过程:将系统调用号放在eax寄存器,其他的函数形参依次放在ebx,ecx,edx,esi,edi中,

注意: stdin =0为标注输入流。stdout=1标注输出流(默认为屏幕)       stderr=2标注错误输出流(默认为屏幕)

write系统调用   write(2,“hello”,5)  

汇编以后:

movl  $4,%eax

movl   $2,%ebx

movl   $hello,%ecx

movl  $5,%edx

int  $0x80

其中4是write的系统调用号。在/usr/include/asm/unistd.h中声明和定义。

下面的这个程序是利用ptrace跟踪系统调用和查看寄存器的值。

 

      1 #include      2 #include      3 #include      4 #include      5 #include      6 #include      7      8 int main()      9 {     10         pid_t  child;     11         long orig_eax,eax;      
     12         long params[3];     13         int status;     14         int insyscall=0;     15         struct user_regs_struct regs;     16         child=fork();     17         if(-1==child)  printf("fork error/n");     18         else if(0==child)     19         {     20           ptrace(PTRACE_TRACEME,child,NULL,NULL);     21           execl("/bin/pwd","pwd",NULL);     22         }     23         else     24         {     25                   wait(&status);     26                   if(WIFEXITED(status))  return 0;     27                   orig_eax=ptrace(PTRACE_PEEKUSER,child,4*ORIG_EAX,NULL);     28                   printf("process executed syscall id=%ld/n",orig_eax);     29                   ptrace(PTRACE_SYSCALL,child,NULL,NULL);     30            while(1)     31                 {     32                   wait(&status);     33                   if(WIFEXITED(status))  break;     34                   orig_eax=ptrace(PTRACE_PEEKUSER,child,4*ORIG_EAX,NULL);     35                   if(0==insyscall)     36                           {     37                              insyscall=1;     38                              orig_eax=ptrace(PTRACE_PEEKUSER,child,4*ORIG_EAX,NULL);     39                              printf("process executed syscall id=%ld/n",orig_eax);     40                              ptrace(PTRACE_GETREGS,child,NULL,®s);     41                              printf("write called with ebx=%ld,ecx=%ld,edx=%ld/n",regs.ebx,regs.ecx,regs.edx);     42                           }     43                   else     44                           {     45                              eax=ptrace(PTRACE_PEEKUSER,child,4*EAX,NULL);     46                              printf("syscall with return value = %ld/n",eax);     47                              insyscall=0;     48                           }     49     50                 ptrace(PTRACE_SYSCALL,child,NULL,NULL);     51                 }     52         }     53 }     54

在头文件/usr/include/asm/usr.h中,有关于struct user_regs_struct regs的原型声明如下:

struct user_regs_struct {
        long ebx, ecx, edx, esi, edi, ebp, eax;
        unsigned short ds, __ds, es, __es;
        unsigned short fs, __fs, gs, __gs;
        long orig_eax, eip;
        unsigned short cs, __cs;
        long eflags, esp;
        unsigned short ss, __ss;
};

          在该函数中,insyscall作为系统调用的标志。父进程fork了一个子进程,在子进程中用PTRACE_TRACEME作为第一个参数调用了ptrace函数,告知内核跟踪自己。在执行完execl函数后,父进程使用wait函数等待来自内核的通知。一旦得到这个通知,就开始跟踪子进程。

WIFEXITED(status)这个宏用来指出子进程是否正常退出,如果是,则返回非零值。

PTARCE_PEEKUSR作为ptrace的第一个参数,其含义为:ptrace(PTARCE_PEEKUSR,child,4*ORIG_EAX,NULL)从由child子进程的用户区addr=4*ORIG_EAX处读出数据并返回该数据。addr必须是字对齐的。

PTRACE_SYSCALL的含义是让子进程重新执行,但在下一次子进程进入系统调用和退出系统调用时停止。

这个程序编译输出的部分结果如下:

/home/estate66/ptrace
process executed syscall id=11
process executed syscall id=122
write called with ebx=-1074571892,ecx=-1074571492,edx=11595732
syscall with return value = 0
process executed syscall id=45
write called with ebx=0,ecx=11595732,edx=-1074571776
syscall with return value = 166211584
process executed syscall id=33
write called with ebx=11579169,ecx=4,edx=11595732
syscall with return value = -2
process executed syscall id=5
write called with ebx=11582367,ecx=0,edx=0
syscall with return value = 3
process executed syscall id=197
write called with ebx=3,ecx=-1074574172,edx=11595732
syscall with return value = 0

process executed syscall id=90
write called with ebx=-1074574204,ecx=2,edx=11595732
syscall with return value = -1208287232
process executed syscall id=6
write called with ebx=3,ecx=2,edx=11595732
syscall with return value = 0
process executed syscall id=5
write called with ebx=-1208195482,ecx=0,edx=0

      第一行是我的当前工作目录,即在终端输入pwd看到的结果,后面的是显示该命令的系统调用过程。我们在终端输入strace  pwd得到如下:

execve("/bin/pwd", ["pwd"], [/* 35 vars */]) = 0
uname({sys="Linux", node="localhost.localdomain", ...}) = 0
brk(0)                                  = 0x97ad000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY)      = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=103838, ...}) = 0
old_mmap(NULL, 103838, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb7f86000
close(3)                                = 0
open("/lib/tls/libc.so.6", O_RDONLY)    = 3
read(3, "/177ELF/1/1/1/0/0/0/0/0/0/0/0/0/3/0/3/0/1/0/0/0/320n/262/0004/0/0/0"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=1529136, ...}) = 0
old_mmap(0xb12000, 1227964, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xb12000
old_mmap(0xc38000, 16384, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x125000) = 0xc38000
old_mmap(0xc3c000, 7356, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xc3c000
close(3)                                = 0
old_mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7f85000
mprotect(0xc38000, 8192, PROT_READ)     = 0
mprotect(0xb0e000, 4096, PROT_READ)     = 0
set_thread_area({entry_number:-1 -> 6, base_addr:0xb7f85aa0, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0
munmap(0xb7f86000, 103838)              = 0

       通过在系统调用头文件中对比发现:/usr/include/asm/unistd.h部分内容摘录如下:

#define __NR_execve              11                   

#define __NR_access              33

#define __NR_brk                 45

#define __NR_open                 5

#define __NR_mmap                90

#define __NR_fstat64            197

#define __NR_uname              122

#define __NR_close                6

      上面只列出了部分的内容。  通过比对我们可以发现,ptrace返回的系统调用号和strace跟踪的shell命令pwd执行过程中所进行的系统调用是一致的。

execl函数调用号是11,执行该函数后就开始新的进程,因此该函数没有返回值。uname函数调用号是122,返回值为0,brk函数调用号      45,实现向内核中动态的扩展进程数据段的大小。包括余下的函数调用都是很吻合的。

0 0
原创粉丝点击