在用户模式中,虽然只有一个函数可用,即ptrace(int _request, pid_t _pid, caddr_t _addr, int _data),但是这个函数能做所有的事情!如果你愿意,也可以花费几个小时来编写自己的小调试器,以解决特定的问题。
ptrace函数的_request参数是最重要的一个参数,因为它确定你将做什么。BSD和Linux的头文件使用不同的定义,这使得将ptrace应用从一个平台移植到另一个平台变得很复杂。默认地,我们使用BSD头文件中的定义。
r PT_TRACE_ME(PTRACE_TRACEME)将当前进程切换到停止状态。它通常总是与fork/exec一起使用,虽然也能遇到自我追踪的应用程序。对于每一个进程,PT_TRACE_ME只能被调用一次。追踪一个正被追踪的进程是会失败的(另一个较不重要的结果是进程不能追踪它自己。如果要这样做,应该首先从自身派生一个进程)。大量的反调试技术都是以这一事实为基础的。为了克服这类技术,必须使用绕过ptrace的调试器。一个信号被发送到正被调试的进程,并将该进程切换到停止状态,该进程可以使用从父进程上下文中调用的PT_CONTINUE和PT_STEP命令从停止状态退出。wait函数会延迟父进程的执行,直到被调试的进程切换为停止状态或者终止为止(终止时,返回值为1407)。其他的所有参数都被忽略。
r PT_ATTACH(PTRACE_ATTACH)将进程标志为pid的运行进程切换为停止状态,在这种情形下,调试器进程成为“父进程”。其他的所有参数都被忽略。进程必须具有与调试进程相同的用户标志(UID),并且不能是setuid/setduid进程(否则就要用root来调试)。
r PT_DETACH(PTRACE_DETACH)停止进程标志为pid进程(由PT_ATTACH和PT_TRACE_ME指定)的调试,并继续其常态运行。其他的所有参数都被忽略。
r PT_CONTINUE(PTRACE_CONT)继续进程标志为pid的被调试进程的执行,而不中断与调试器进程的通信。如果addr == 1(在Linux中为0),从上次停止的地址继续执行;否则,从指定的地址继续执行。参数_data指定发送到被调试进程的信号数量(零说明没有信号)。
r PT_STEP(PTRACE_SINGLESTEP)进行进程标志为pid的进程的单步执行,即执行下一条机器指令并切换为停止状态(在i386中,这是根据设置追踪标志来实现的,虽然有些“黑客”函数库使用硬件断点)。BSD要求将参数addr置为1,而Linux要求将该参数置为0。其他的所有参数都被忽略。
r PT_READ_I和PT_READ_D(PTRACE_PEEKTEXT和PTRACE_PEEKDATA)分别从代码区和正被调试进程的地址空间区读取机器字。在许多当代的平台中,这两个指令是等价的。ptrace函数接收目标地址addr,并返回读到的结果。
r PT_WRITE_I和PR_READ_D(PTRACE_POKETEXT和PTRACE_POKEDATA)将由_data传入的机器字写入addr所指定的地址。
r PT_GETREGS,PT_GETFPREGS和PT_GETDBREGS(PTRACE_GETREGS,PTRACE_ FPREGS和PT_GETFPXREGS)将一般用途寄存器、段寄存器和调试寄存器的值读入到地址由_addr指针所指定的调试器进程的内存区中。只有i386平台接收这些与系统相关的命令。寄存器结构的描述放在头文件machine/reg.h文件中。
r PT_SETREGS,PT_SETFPREGS和PT_SETDBREGS(PTRACE_SETREGS,PTRACE_ SETFPREGS和PT_SETFPXREGS)通过拷贝由_addr指针所指定的内存区域的内容来设置被调试进程的寄存器的值。
r PT_KILL(PTRACE_KILL)将sigkill发送到被调试进程,以终止其执行。