[转载] ptrace使用简介
转载原文信息:
作者:M_天河
链接:https://www.jianshu.com/p/b1f9d6911c90
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
ptrace 提供了一种机制使得父进程可以观察和控制子进程的执行过程,ptrace 还可以检查和修改子进程的可执行文件在内存中的image及子进程所使用的寄存器中的值。通常来说,主要用于实现对进程插入断点和跟踪子进程的系统调用。
用法示例
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <linux/user.h>
int main()
{ pid_t child;
long orig_eax;
child = fork();
if(child == 0) {
ptrace(PTRACE_TRACEME, 0, NULL, NULL);
execl("/bin/ls", "ls", NULL);
}
else {
wait(NULL);
orig_eax = ptrace(PTRACE_PEEKUSER,child, 4 * ORIG_EAX,NULL);
printf("The child made a ""system call %ld\n", orig_eax);
ptrace(PTRACE_CONT, child, NULL, NULL);
}
return 0;
}
上面代码被编译后除了执行ls命令外还会执行第17行代码的打印内容。
具体过程:父进程 fork() 出子进程,子进程中执行我们所想要 trace 的程序,在子进程调用 exec() 之前,子进程需要先调用一次 ptrace,以 PTRACE_TRACEME 为参数。这个调用是为了告诉内核,当前进程已经正在被 traced,当子进程执行 execve() 之后,子进程会进入暂停状态,把控制权转给它的父进程(SIG_CHLD信号), 而父进程在fork()之后,就调用 wait() 等子进程停下来,当 wait() 返回后,父进程就可以去查看子进程的寄存器或者对子进程做其它的事情了。
当系统调用发生时,内核会把当前的寄存器中的内容(即系统调用的编号)保存到子进程的用户态代码段中,我们可以像上面的例子那样通过调用Ptrace(传入PTRACE_PEEKUSER作为第一个参数)来读取这个寄存器的值,当我们做完这些检查数据的事情之后,通过调用ptrace(PTRACE_CONT),可以让子进程重新恢复运行。
ptrace共有四个参数:
long ptrace(enum __ptrace_request request,pid_t pid,void *addr,void *data);
其中第一个参数可以取如下内容:
PTRACE_TRACEME, 本进程被其父进程所跟踪。其父进程应该希望跟踪子进程
PTRACE_PEEKTEXT, 从内存地址中读取一个字节,内存地址由addr给出
PTRACE_PEEKDATA, 同上
PTRACE_PEEKUSER, 可以检查用户态内存区域(USER area),从USER区域中读取一个字节,偏移量为addr
PTRACE_POKETEXT, 往内存地址中写入一个字节。内存地址由addr给出
PTRACE_POKEDATA, 往内存地址中写入一个字节。内存地址由addr给出
PTRACE_POKEUSER, 往USER区域中写入一个字节,偏移量为addr
PTRACE_GETREGS, 读取寄存器
PTRACE_GETFPREGS, 读取浮点寄存器
PTRACE_SETREGS, 设置寄存器
PTRACE_SETFPREGS, 设置浮点寄存器
PTRACE_CONT, 重新运行
PTRACE_SYSCALL, 重新运行
PTRACE_SINGLESTEP, 设置单步执行标志
PTRACE_ATTACH,追踪指定pid的进程
PTRACE_DETACH, 结束追踪
具体用法:
- PTRACE_TRACEME
ptrace(PTRACE_TRACEME,0 ,0 ,0)
本进程被其父进程所跟踪。其父进程应该希望跟踪子进程。 - PTRACE_PEEKTEXT, PTRACE_PEEKDATA
ptrace(PTRACE_PEEKTEXT, pid, addr, data)
ptrace(PTRACE_PEEKDATA, pid, addr, data)
从内存地址中读取一个字节,pid表示被跟踪的子进程,内存地址由addr给出,data为用户变量地址用于返回读到的数据。在Linux(i386)中用户代码段与用户数据段重合所以读取代码段和数据段数据处理是一样的。 - PTRACE_POKETEXT, PTRACE_POKEDATA
ptrace(PTRACE_POKETEXT, pid, addr, data)
ptrace(PTRACE_POKEDATA, pid, addr, data)
往内存地址中写入一个字节。pid表示被跟踪的子进程,内存地址由addr给出,data为所要写入的数据。 - PTRACE_PEEKUSR
ptrace(PTRACE_PEEKUSR, pid, addr, data)
从USER区域中读取一个字节,pid表示被跟踪的子进程,USER区域地址由addr给出,data为用户变量地址用于返回读到的数据。USER结构为core文件的前面一部分,它描述了进程中止时的一些状态,如:寄存器值,代码、数据段大小,代码、数据段开始地址等。在Linux(i386)中通过PTRACE_PEEKUSER和PTRACE_POKEUSR可以访问USER结构的数据有寄存器和调试寄存器。 - PTRACE_POKEUSR
ptrace(PTRACE_POKEUSR, pid, addr, data)
往USER区域中写入一个字节,pid表示被跟踪的子进程,USER区域地址由addr给出,data为需写入的数据。 - PTRACE_CONT
ptrace(PTRACE_CONT, pid, 0, signal)
继续执行。pid表示被跟踪的子进程,signal为0则忽略引起调试进程中止的信号,若不为0则继续处理信号signal。 - PTRACE_SYSCALL
ptrace(PTRACE_SYS, pid, 0, signal)
继续执行。pid表示被跟踪的子进程,signal为0则忽略引起调试进程中止的信号,若不为0则继续处理信号signal。与PTRACE_CONT不同的是进行系统调用跟踪。在被跟踪进程继续运行直到调用系统调用开始或结束时,被跟踪进程被中止,并通知父进程。 - PTRACE_KILL
ptrace(PTRACE_KILL,pid)
杀掉子进程,使它退出。pid表示被跟踪的子进程。 - PTRACE_SINGLESTEP
ptrace(PTRACE_KILL, pid, 0, signle)
设置单步执行标志,单步执行一条指令。pid表示被跟踪的子进程。signal为0则忽略引起调试进程中止的信号,若不为0则继续处理信号signal。当被跟踪进程单步执行完一个指令后,被跟踪进程被中止,并通知父进程。 - PTRACE_ATTACH
ptrace(PTRACE_ATTACH,pid)
跟踪指定pid 进程。pid表示被跟踪进程。被跟踪进程将成为当前进程的子进程,并进入中止状态。 - PTRACE_DETACH
ptrace(PTRACE_DETACH,pid)
结束跟踪。 pid表示被跟踪的子进程。结束跟踪后被跟踪进程将继续执行。 - PTRACE_GETREGS
ptrace(PTRACE_GETREGS, pid, 0, data)
读取寄存器值,pid表示被跟踪的子进程,data为用户变量地址用于返回读到的数据。此功能将读取所有17个基本寄存器的值。 - PTRACE_SETREGS
ptrace(PTRACE_SETREGS, pid, 0, data)
设置寄存器值,pid表示被跟踪的子进程,data为用户数据地址。此功能将设置所有17个基本寄存器的值。 - PTRACE_GETFPREGS
ptrace(PTRACE_GETFPREGS, pid, 0, data)
读取浮点寄存器值,pid表示被跟踪的子进程,data为用户变量地址用于返回读到的数据。此功能将读取所有浮点协处理器387的所有寄存器的值。 - PTRACE_SETFPREGS
ptrace(PTRACE_SETREGS, pid, 0, data)
设置浮点寄存器值,pid表示被跟踪的子进程,data为用户数据地址。此功能将设置所有浮点协处理器387的所有寄存器的值。