您的位置:首页 > 其它

ptrace应用系列-基础知识

2014-09-14 22:58 369 查看
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,实现向内核中动态的扩展进程数据段的大小。包括余下的函数调用都是很吻合的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: