跟踪进程和所有分叉进程的系统调用

时间:2012-11-23 15:42:28

标签: c++ c linux fork ptrace

我使用ptrace来跟踪进程的系统调用。在分叉过程之后,我使用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文档中,很难弄清楚它是如何完成的。我需要一些这方面的例子。

修改

我在这里找到了一个类似的问题:How to ptrace a multi-threaded application?我用以下代码尝试了它。此代码跟踪已启动进程的系统调用,并且它也应该跟踪分叉进程。它在父进程中fork()之后运行(子进程调用PTRACE_TRACEMEexec()。)

EDIT2:

我对代码做了一些修改,取得了一些进展。

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的事件时,新进程还不存在,并且我得到了一个&#34;进程不存在&#34;如果我尝试ptrace它错误消息。如果我在尝试跟踪新进程之前暂时休眠,我就不会收到错误消息。现在当程序到达fork / clone时,它开始跟踪新进程,但它永远不会到达父进程中clone()系统调用的返回点,这意味着子进程成功完成,但是父母在它的叉点处挂起。

1 个答案:

答案 0 :(得分:1)

Strace这样做,它的README.linux文件包含有关该主题的一些信息:

http://strace.git.sourceforge.net/git/gitweb.cgi?p=strace/strace;a=blob;f=README-linux-ptrace;h=97e2c019a075f216f125f8b9aa6da68fa2abf230;hb=refs/heads/master

它似乎在抱怨这个平台中的内核错误,所以YMMV。

该代码解释了如何获得孩子的pid。但是,由于在其调用的二进制文件上设置的setuid或setgid位,子进程可能会获得另一个用户标识。所以答案是你在子PID上调用ptrace并查看你是否可以访问。

以下是相关部分:

PTRACE_EVENT stops are observed by tracer as waitpid returning with
WIFSTOPPED(status) == true, WSTOPSIG(status) == SIGTRAP. Additional bit
is set in a higher byte of status word: value ((status >> 8) & 0xffff)
will be (SIGTRAP | PTRACE_EVENT_foo << 8). The following events exist:

PTRACE_EVENT_VFORK - stop before return from vfork/clone+CLONE_VFORK.
When tracee is continued after this, it will wait for child to
exit/exec before continuing its execution (IOW: usual behavior on
vfork).

PTRACE_EVENT_FORK - stop before return from fork/clone+SIGCHLD

PTRACE_EVENT_CLONE - stop before return from clone

PTRACE_EVENT_VFORK_DONE - stop before return from
vfork/clone+CLONE_VFORK, but after vfork child unblocked this tracee by
exiting or exec'ing.

For all four stops described above: stop occurs in parent, not in newly
created thread. PTRACE_GETEVENTMSG can be used to retrieve new thread's
tid.