【问题标题】:How to detach from process, so that it can be traced by another process?如何从进程中分离,以便它可以被另一个进程跟踪?
【发布时间】:2019-02-22 01:32:11
【问题描述】:

程序步骤:

  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);
}

【问题讨论】:

  • ptrace(PTRACE_DETACH, pid, 0, sig) ?
  • @oakad 我用了ptrace_detach,但是不行,所以问了这个问题
  • @Cyber​​fined 你能把#includes 添加到示例的顶部吗?
  • @jamieguinan 是的,已经完成了。
  • 试图在这里重复你的测试用例。你的子命令是什么,它是如何设置 eax 和 ebx 的?

标签: c linux ptrace


【解决方案1】:

这里要注意的重要一点是,PTRACE_SYSCALL 请求将使目标进程在进入或退出系统调用时停止。 manual page

Syscall-enter-stop 和 syscall-exit-stop 无法区分 彼此由示踪剂。跟踪器需要跟踪 为了不误解 syscall-enter-stop 的 ptrace-stops 序列 作为 syscall-exit-stop 或反之亦然。

如果您使用 ptrace 更改目标的寄存器值,您将更改内核将看到的系统调用参数,或者用户进程将看到的返回值,这取决于您是否在 syscall-enter-停止或系统调用退出停止。

您的代码在 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);

它将与目标分离并继续它。 (现在是前)目标完成其 PTRACE_TRACEME 请求,该请求成功,使其父级再次成为跟踪器。

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

Gdb 将发出警告消息,因为它出乎意料地已经是目标的跟踪器。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-09-15
    • 2014-01-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-20
    • 2013-01-19
    • 2012-11-07
    相关资源
    最近更新 更多