您可以使用 PTRACE_SYSCALL 请求:它重新启动子进程(就像 PTRACE_CONT 一样)但安排它在下一次进入或退出系统调用时停止。例如(假设为 x86 构建的内核):
#include <sys/ptrace.h>
#include <signal.h>
#include <linux/user.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc, char **argv)
{
int status = 0, pid, r;
struct user_regs_struct uregs;
if ((pid = fork()) == 0) {
printf("pid = %d, ppid = %d\n", getpid(), getppid());
ptrace(PTRACE_TRACEME, 0, 0, 0);
kill(getpid(), SIGINT);
r = getpid();
printf("%d\n", r);
} else {
wait(&status);
ptrace(PTRACE_SYSCALL, pid, 0, 0);
wait(&status);
ptrace(PTRACE_GETREGS, pid, 0, &uregs);
/* this prints the syscall number of getpid */
printf("syscall nr: %d\n", uregs.orig_eax);
/* 64 is syscall number of getppid */
uregs.orig_eax = 64;
ptrace(PTRACE_SETREGS, pid, 0, &uregs);
ptrace(PTRACE_CONT, pid, 0, 0);
wait(&status);
if(WIFEXITED(status))
printf("we're done\n");
}
}
孩子打印它的 PID 并向自己传递一个信号。由于之前调用ptrace()
this 意味着它将被停止。
父级等待这发生并使用 PTRACE_SYSCALL 重新启动子级,然后等待。接下来,子进程调用getpid
系统调用并再次停止。父进程使用 PTRACE_GETREGS 调用来查看子进程的寄存器,其中eax
保存系统调用号。父级将此更改为 的系统调用号getppid
,然后再次允许子级继续。因为系统调用号在调用系统调用之前已更改,所以子进程现在将调用getppid
而不是getpid
.
用于ptrace
此目的可能是可移植的,但我尚未对其进行测试。在 gdb 中,也可以使用catch syscall
命令。