The ptrace() system call provides a means by which one process (the”tracer”) may observe and control the execution of another process(the “tracee”), and examine and change the tracee’s memory andregisters. It is primarily used to implement breakpoint debugging and system call tracing.

帮助文档online.

ptrace

ptrace可以让一个进程监视和控制另一个进程的执行,并且修改被监视进程的内存、寄存器等,主要应用于断点调试和系统调用跟踪.

函数原型:

1
long ptrace(int request, pid_t pid, void * addr, void * data)

其中,request代表请求类型,pid代表被调试进程的pid.

ptrace建立跟踪关系的两种方式:

  1. fork:
    利用fork+execve执行被测试的程序,子进程在执行execve之前调用ptrace(PTRACE_TRACEME),建立了与父进程(debugger)的跟踪关系。
  2. attach: debugger可以调用ptrace(PTRACE_ATTACH,pid,…),建立自己与进程号为pid的进程间的跟踪关系。
    即利用PTRACE_ATTACH,使自己变成被调试程序的父进程(用ps可以看到)。用attach建立起来的跟踪关系,可以调用ptrace(PTRACE_DETACH,pid,…)来解除。注意attach进程时的权限问题,如一个非root权限的进程是不能attach到一个root进程上的。

有信号发生时,被调试进程就会暂停下来,并且通知调试进程.调试进程可以调用waitpid()获得被调试进程暂停位置的相关信息.
在被调试进程暂停的期间,调试进程可以各种ptrace request来对被调试进程进行操作,查看、修改、使之继续运行、忽略该信号甚至发送信号.

部分ptrace request

PTRACE_TRACEME

将当前进程切换到停止状态。它通常总是与 fork/exec 一起使用。对于每一个进程,PTRACE_TRACEME 只能被调用一次。

PTRACE_ATTACH

根据pid将被调试进程附加到调试进程上,PTRACE_ATTACH向被调试进程发送SIGSTOP信号使之停下.
但是在ptrace(PTRACE_ATTACH,pid,0,0)执行完毕时被调试进程可能还没有暂停,可以使用waitpid()等待其停下.

PTRACE_DETACH

将被调试进程与调试进程分离,使被调试进程正常运行.

PTRACE_SYSCALL

使被调试进程继续运行,但是在下一个系统调用的入口处或出口处停下,或者是执行完一条指令后停下.
例如,调试进程可以监视被调试进程系统调用入口处的参数,接着再使用SYSCALL,监视系统调用的返回值.

wait

wait函数会延迟父进程的执行,直到被调试的进程切换为停止状态或者终止为止.

反调试

ptrace被广泛用于反调试,因为一个进程只能被ptrace一次,如果事先调用了ptrace方法,那就可以防止别人调试我们的程序.

测试

测试程序:

1
2
3
4
5
6
7
8
9
10
11
12
#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

运行:

另开命令行调试:

检测

1
2
3
4
5
6
7
8
9
10
11
12
#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):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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.

Reference

Android ptrace简介