ptrace和debugger原理
ptrace
ptrace可以让一个进程监视和控制另一个进程的执行,并且修改被监视进程的内存、寄存器等,主要应用于断点调试和系统调用跟踪.
函数原型:
long ptrace(int request, pid_t pid, void * addr, void * data)
其中,request代表请求类型,pid代表被调试进程的pid.
部分ptrace request和wait
PTRACE_TRACEME
表示本进程将被其父进程跟踪,交付给这个进程的所有信号(除SIGKILL之外),都将使其停止,父进程将通过wait()获知这一情况。
它通常总是与 fork/exec 一起使用。对于每一个进程,PTRACE_TRACEME 只能被调用一次。
PTRACE_ATTACH
根据pid将调试进程附加到被调试进程上,PTRACE_ATTACH向被调试进程发送SIGSTOP信号使之停下.
但是在ptrace(PTRACE_ATTACH,pid,0,0)执行完毕时被调试进程可能还没有暂停,可以使用waitpid()等待其停下.
PTRACE_DETACH
将被调试进程与调试进程分离,使被调试进程正常运行.
PTRACE_SYSCALL
使被调试进程继续运行,但是在下一个系统调用的入口处或出口处停下,或者是执行完一条指令后停下.
例如,调试进程可以监视被调试进程系统调用入口处的参数,接着再使用SYSCALL,监视系统调用的返回值.
wait()
wait函数会延迟父进程的执行,直到被调试的进程切换为停止状态或者终止为止.
调试器建立调试关系的两种方式:
用gdb调试程序,可以直接gdb ./test,也可以gdb (test的进程号)。这对应着使用ptrace建立跟踪关系的两种方式:
fork:利用fork+execve执行被测试的程序,子进程在执行execve之前调用ptrace(PTRACE_TRACEME),建立了与父进程(debugger)的跟踪关系。
attach: debugger可以调用ptrace(PTRACE_ATTACH,pid,…),建立自己与进程号为pid的进程间的跟踪关系。即利用PTRACE_ATTACH,使自己变成被调试程序的父进程(用ps可以看到)。用attach建立起来的跟踪关系,可以调用ptrace(PTRACE_DETACH,pid,…)来解除。注意attach进程时的权限问题,如一个非root权限的进程是不能attach到一个root进程上的。
反调试
ptrace被广泛用于反调试,因为一个进程只能被ptrace一次,如果事先调用了ptrace方法,那就可以防止别人调试我们的程序.
测试程序:
#include<stdio.h>
#include<stdlib.h>
#include<sys/ptrace.h>
int main(int argc, char* argv[])
{
ptrace(PTRACE_TRACEME);
while(1){
printf("Hello Ptrace!\n");
sleep(1);
}
return 0;
}
编译:gcc -g -o hello-ptrace hello-ptrace.c
运行并用gdb调试,发现已经被抢占,不能attach
已经由于PTRACE_TRACEME,被父进程trace
检测
#include<stdio.h>
#include<sys/ptrace.h>
int main(int argc, char* argv[])
{
if(-1 == ptrace(PTRACE_TRACEME))
{
printf("Debugger!\n");
return 1;
}
printf("Hello Ptrace!\n");
return 0;
}
反反调试
ptrace用户态源码(位于bionic/libc/bionic/ptrace.c):
long ptrace(int request, pid_t pid, void * addr, void * data)
{
switch (request) {
case PTRACE_PEEKUSR:
case PTRACE_PEEKTEXT:
case PTRACE_PEEKDATA:
{
long word;
long ret;
ret = __ptrace(request, pid, addr, &word);
if (ret == 0) {
return word;
} else {
// __ptrace will set errno for us
return -1;
}
}
default:
return __ptrace(request, pid, addr, data);
}
}
0表示成功,-1表示错误.
单个应用可在ptrace下断点.
定制ROM,可以将ptrace源代码修改为如果是自己的pid调用ptrace,返回-1;否则返回0.