Ptrace()系统调用一般用于调试断点和跟踪系统调用。ptrace() "进程跟踪 "系统调用经常被用于调试目的。它是本地调试器保持跟踪的主要方式。跟踪可以暂停,寄存器和内存可以被检查和设置,系统调用可以被监控,甚至可以使用Ptrace系统调用拦截系统调用。跟踪器必须首先连接到跟踪器上。在一个多线程的进程中,每个线程可以分别连接到一个可能不同的追踪器上,或者不连接,因此也就无法调试。因此,"Tracee "总是指 "一个潜在的多线程进程,从未或可能是多线程进程。
所有提供给被追踪进程的信号,除了一个以外,都会使其停止,不管其注册的信号处理,并向追踪进程传递一个事件,这个事件可以用wait()系统函数来识别。SIGKILL信号是一个例外,因为它是即时传递的,并且完成了预期的行为。Ptrace系统调用从未有一个标准。它的接口在不同的操作系统中是可比的,特别是在基本功能方面,但在不同的系统中略有不同。
系统调用可以用Linux版的trace来追踪。PTRACE SYSCALL请求重启子进程的方式与PTRACE CONT相同,但它安排它在下一个系统调用入口或退出时停止。这就带来了很多新的机会。对于PTRACE PEEK请求,trace()将返回所需的数据;对于所有其他请求,它将返回0。所有失败的请求都返回-1,errno设置为最佳值。在PTRACE PEEK请求的情况下,-1可能是一个合法的返回值;程序要负责确定这是一个错误情况还是一个有效的返回值。本指南将通过一个例子向你解释C语言中trace()系统调用的功能。
了解C语言中trace()系统调用的例子
为了理解C语言中的trace()系统调用,我们使用Ubuntu 20.04 Linux系统来实现其例子。GCC编译器已经安装在我们的系统中用于执行代码。你可以在Ubuntu 20.04 Linux系统的终端外壳中使用下面引用的指令来安装它。
$ sudo apt install gcc
现在,让我们开始我们的例子。通过使用nano指令,在终端创建一个以.c为扩展名的任何你想要的文件。你也可以通过进入任何主目录或使用 "touch "指令直接创建该文件。使用nano指令的目的是为了在终端上直接打开GNU编辑器。现在在Ubuntu 20.04 Linux系统的终端外壳上执行下面的指令。
$ nano q.c

GNU nano 4.8将出现在你的屏幕上。现在写下下面所附图片中显示的代码。

在上面的代码中,我们利用了一些标准库。PTRACE TRACEME指定这个进程的父进程应该能够跟踪它。如果它的父代不期望跟踪它,进程就不应该提交这个请求。PID、addr和数据不被保留在考虑之内。追踪者是唯一使用PTRACE TRACEME调用的人;追踪者只使用其他请求。在上述情况下,父进程分叉一个子进程并监视它。子进程在调用exec函数之前运行以PTRACE TRACEME为第一参数的trace函数,该函数通知内核:然后子进程在调用execve()之后控制父进程。
父进程是用wait()函数来等待内核的提醒的,现在它被通知了,它可以观察子进程一直在做什么,比如检查寄存器值。内核保存了 "eax "寄存器的全部特征,每当系统调用发生时,它就掌握了系统调用的编号。PTRACE PEEKUSER 从跟踪者的用户部分读取一个字,其中包含进程的寄存器和其他数据(sys/user.h>)。作为ptrace()调用的结果,该字符串被返回。偏移量通常必须是字对齐的,尽管这可能因结构不同而不同。

如果被停止了,PTRACE CONT会恢复被追踪的进程。如果数据不为零,它被理解为要发送给跟踪器的信号数量;然后,不发送信号。例如,追踪者可以调节是否发送一个信号给被追踪者。编译和执行可以通过在Ubuntu 20.04 Linux系统的终端外壳中执行下面引用的指令来完成。
$ gcc q.c
$ ./a.out

成功的输出已经显示在上面的图片中。
结论
ptrace()系统调用在C语言编程中被广泛使用,但它可能会识别和改变一个正在运行的程序;trace函数可能看起来很奇怪。调试器和系统调用跟踪器通常采用这种技术。在用户端,它使程序员能够做更多有趣的事情。本文提供了对ptrace()系统调用的基本理解和实现。