Как отсоединиться от процесса, чтобы его можно было отслеживать другим процессом? - PullRequest
0 голосов
/ 22 февраля 2019

Шаги программы:

  1. Создать дочерний процесс с помощью fork и вызвать в нем execv
  2. Ptrace присоединиться к дочернему процессу
  3. Сделать что-то с ptrace
  4. Отделение от дочернего элемента
  5. Выполнение gdb -p child_pid

Но при запуске GDB пишет, что дочерний процесс уже отслежен.Как отсоединиться от отслеживаемого процесса, чтобы его можно было отслеживать другим процессом?

Код, выполняющий действия выше

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/signal.h>
#include <sys/user.h>
#include <sys/ptrace.h>

#define Error(msg) do { perror(msg); exit(0); } while(0)
#define PTRACE_E(req, pid, addr, data) \
    do { \
        if(ptrace(req, pid, addr, data) < 0) { \
            perror(#req); \
            exit(0); \
        } \
    } while(0)
#define BUF_SIZE 16

int main(int argc, char **argv) {
    pid_t pid;
    struct user_regs_struct regs;
    int status;
    char buf[BUF_SIZE];

    if (argc < 2) {
        fprintf(stderr, "Usage: %s <executable> [ARGS]\n", argv[0]);
        exit(0);
    }

    pid = fork();
    if(pid < 0) {
        Error("fork");
    } else if(pid == 0) {
        if(execv(argv[1], &argv[1]) < 0)
            Error("execv");
    }

    PTRACE_E(PTRACE_ATTACH, pid, NULL, NULL);

    while(wait(&status) && !WIFEXITED(status)) {
        PTRACE_E(PTRACE_GETREGS, pid, NULL, &regs);

        if(regs.orig_eax == 26 && regs.ebx == PTRACE_TRACEME) {
            regs.eax = 0;

            PTRACE_E(PTRACE_SETREGS, pid, NULL, &regs);
            break;
        }

        PTRACE_E(PTRACE_SYSCALL, pid, NULL, NULL);
    }

    ptrace(PTRACE_DETACH, pid, NULL, NULL);

    snprintf(buf, BUF_SIZE, "%d", pid);
    execl("/usr/bin/gdb", "/usr/bin/gdb", "-p", buf, NULL);
}

1 Ответ

0 голосов
/ 27 февраля 2019

Здесь важно отметить, что запрос PTRACE_SYSCALL сделает целевой процесс остановленным при входе в или выходе из системного вызова.Страница руководства говорит, что

Syscall-enter-stop и syscall-exit-stop трассировщик неотличимы друг от друга.Трассировщик должен отслеживать последовательность ptrace-stop, чтобы не интерпретировать syscall-enter-stop как syscall-exit-stop или наоборот.

Если вы используете ptrace для изменения целиРегистрируя значения, вы изменяете аргументы системного вызова, которые увидит ядро, или возвращаемое значение, которое увидит пользовательский процесс, в зависимости от того, делаете ли вы это с помощью syscall-enter-stop или syscall-exit-stop.

Ваш код здесь запускается по syscall-enter-stop:

if (regs.orig_eax == 26 && regs.ebx == PTRACE_TRACEME) {
    regs.eax = 0;
    PTRACE_E(PTRACE_SETREGS, pid, NULL, &regs);
    break;
}

Он изменяет значение eax (которое равно -38 при входе в системный вызов) на 0. Поскольку вы намеревались изменить возвратКод из запроса PTRACE_TRACEME цели от -1 до 0, вам нужно сделать PTRACE_SYSCALL еще раз, чтобы цель остановилась на syscall-exit-stop, прежде чем запускать вышеуказанный код.

В настоящее время,ваш код выходит из цикла после запроса PTRACE_SETREGS, а затем выполняет

ptrace(PTRACE_DETACH, pid, NULL, NULL);

, который отсоединится от цели и продолжит ее.Цель (теперь ex-) завершает свой запрос PTRACE_TRACEME, который успешно выполняется, и его родитель снова становится трассировщиком.

execl("/usr/bin/gdb", "/usr/bin/gdb", "-p", buf, NULL);

GDB выдаст предупреждающее сообщение, поскольку неожиданно уже является трассировщиком дляцель.

...