Отслеживание системных вызовов процесса и всех разветвленных процессов

Я использую 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_TRACEFORK, PTRACE_O_TRACEVFORK а также PTRACE_O_TRACECLONE, но из man Документация, очень трудно понять, как именно это делается. Мне нужно несколько примеров по этому поводу.

Редактировать:

Я нашел похожий вопрос здесь: Как найти многопоточное приложение? Я попробовал это с помощью следующего кода. Этот код отслеживает системные вызовы запущенного процесса, и он должен также отслеживать разветвленные процессы. Он запускается после fork() в родительском процессе (ребенок вызывает PTRACE_TRACEME и exec()).

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 в этом.

Очевидно, когда я получаю событие форка / клона, новый процесс еще не существует, и я получаю сообщение об ошибке "Процесс не существует", если я пытаюсь его проследить. Если я усыпляю перед попыткой отследить новый процесс, я не получаю сообщение об ошибке. Теперь, когда программа достигает точки разветвления / клонирования, она начинает отслеживать новый процесс, но никогда не достигает точки возврата clone() syscall в родительском процессе, что означает, что дочерний процесс завершается успешно, но родительский объект зависает в своей точке ветвления.

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.

Код объясняет, как получить пид ребенка. Однако возможно, что дочерний элемент получит другой идентификатор пользователя из-за битов setuid или setgid, установленных в двоичных файлах, которые он вызывает. Таким образом, ответ заключается в том, что вы вызываете ptrace для дочернего PID и смотрите, получите ли вы доступ.

Вот соответствующий раздел:

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.
Другие вопросы по тегам