追踪进程及其所有衍生进程的系统调用

9
我将使用ptrace来跟踪进程的系统调用。在fork进程后,我使用PTRACE_TRACEME开始跟踪进程。代码如下所示:

while (true) {
    int status;
    int gotPid;
    gotPid = waitpid(pid, &status, 0);

    if (WIFEXITED(status) || WIFSIGNALED(status)) {
        break;
    }

    if (WIFSTOPPED(status)) {
        handleTrace();
    }
}

那么接下来就是 handleTrace 函数,它看起来像这样。
long syscall;
syscall = ptrace(PTRACE_PEEKUSER,
     pid, 8 * ORIG_RAX, NULL);

// do something with the syscall

// continue program
ptrace(PTRACE_SYSCALL, pid, NULL, NULL);

这很好,但如果程序分叉(或创建新线程),我还想跟踪所跟踪进程创建的子进程(以及由进程创建的线程)。我知道可以使用 PTRACE_O_TRACEFORKPTRACE_O_TRACEVFORKPTRACE_O_TRACECLONE 来完成,但从 man 文档中很难弄清楚具体如何实现。我需要一些示例代码。
编辑:
我在这里找到了一个类似的问题:如何追踪多线程应用程序? 我尝试了以下代码。该代码跟踪启动进程的系统调用,并应该跟踪分叉的进程。它在父进程的 fork() 之后运行(子进程调用 PTRACE_TRACEMEexec())。
编辑2:
我对代码进行了一些修改,取得了一些进展。
long orig_eax;
int status;
int numPrograms = 1;

while(1) {
    int pid;
    CHECK_ERROR_VALUE(pid = waitpid(-1, &status, __WALL));
    std::cout << pid << ": Got event." << std::endl;
    if(WIFEXITED(status) || WIFSIGNALED(status)) {
        std::cout << pid << ": Program exited." << std::endl;
        if (--numPrograms == 0) {
            break;
        }
        continue;
    }

    if (status >> 16 == PTRACE_EVENT_FORK || status >> 16 == PTRACE_EVENT_VFORK ||
            status >> 16 == PTRACE_EVENT_CLONE) {
        int newpid;
        CHECK_ERROR_VALUE(ptrace(PTRACE_GETEVENTMSG, child, NULL, (long) &newpid));
        std::cout << pid << ": Attached to offspring " << newpid << std::endl;
        boost::this_thread::sleep(boost::posix_time::millisec(100));
        CHECK_ERROR_VALUE(ptrace(PTRACE_SETOPTIONS,
                newpid, NULL, PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_TRACECLONE));
        CHECK_ERROR_VALUE(ptrace(PTRACE_SYSCALL, newpid, NULL, NULL));
        ++numPrograms;
    } else {
        CHECK_ERROR_VALUE(ptrace(PTRACE_SETOPTIONS,
                pid, NULL, PTRACE_O_TRACEFORK  | PTRACE_O_TRACEVFORK | PTRACE_O_TRACECLONE));
        CHECK_ERROR_VALUE(orig_eax = ptrace(PTRACE_PEEKUSER,
                pid, 8 * ORIG_RAX, NULL));
        std::cout << pid << ": Syscall called: " <<
                SyscallMap::instance().get().right.at(orig_eax) <<
                std::endl;
        CHECK_ERROR_VALUE(ptrace(PTRACE_SYSCALL,
                pid, NULL, NULL));
    }

}

CHECK_ERROR_VALUE 是一个宏,用于检查结果代码并抛出带有 errno 描述的异常。

显然,当我获得 fork/clone 事件时,新进程还不存在,如果尝试对其进行 ptrace,则会收到 "进程不存在" 错误消息。如果在尝试 ptrace 新进程之前加上 sleep,就不会收到错误消息。现在,当程序到达 fork/clone 点时,它开始跟踪新进程,但是父进程中的 clone() 系统调用的返回点永远无法到达,这意味着子进程成功完成,但父进程挂起在其 fork 点处。

1个回答

1

Strace可以做到这一点,它的README-linux-ptrace文件中有一些相关信息:

https://github.com/strace/strace/blob/master/README-linux-ptrace

它似乎在抱怨这个平台的内核错误,所以可能会因人而异。

该代码解释了如何获取子进程的pid。但是由于二进制文件调用时设置了setuid或setgid位,导致子进程可能会获得另一个用户ID。因此,答案是您需要对子进程PID调用ptrace,并查看是否有访问权限。

以下是相关部分:

PTRACE_EVENT 停止由跟踪器观察到,当 WIFSTOPPED(status) == trueWSTOPSIG(status) == SIGTRAP 时返回。状态字的高位字节中设置了一个附加位:值为 ((status >> 8) & 0xffff) 将是 (SIGTRAP | PTRACE_EVENT_foo << 8)。以下事件存在:

  • PTRACE_EVENT_VFORK - 在从 vfork/clone+CLONE_VFORK 返回之前停止。当 tracee 在此之后继续执行时,它将等待子进程退出/执行后才会继续执行(即在 vfork 上的通常行为)。
  • PTRACE_EVENT_FORK - 在从 fork/clone+SIGCHLD 返回之前停止
  • PTRACE_EVENT_CLONE - 在从 clone 返回之前停止
  • PTRACE_EVENT_VFORK_DONE - 在从 vfork/clone+CLONE_VFORK 返回之前停止,但在 vfork 子进程通过退出或执行解除阻塞此 tracee 之后。

对于上述四个停止:停止发生在父进程中,而不是新创建的线程中。可以使用 PTRACE_GETEVENTMSG 检索新线程的 tid。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接