如何脱离进程,以便它可以被另一个进程跟踪?
How to detach from process, so that it can be traced by another process?
程序步骤:
- 通过fork创建child进程并在其中调用execv
- Ptrace 附加到 child 进程
- 用 ptrace 做点什么
- 从 child
分离
- 执行 gdb -p child_pid
但是当 gdb 启动时,它写道 child 进程已经被跟踪。
如何从被跟踪的进程中分离出来,以便它可以被另一个进程跟踪?
执行以上操作的代码
#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, ®s);
if(regs.orig_eax == 26 && regs.ebx == PTRACE_TRACEME) {
regs.eax = 0;
PTRACE_E(PTRACE_SETREGS, pid, NULL, ®s);
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_SYSCALL 请求将使目标进程在进入 或退出系统调用时停止。 manual page 表示
Syscall-enter-stop and syscall-exit-stop are indistinguishable from
each other by the tracer. The tracer needs to keep track of the
sequence of ptrace-stops in order to not misinterpret syscall-enter-stop
as syscall-exit-stop or vice versa.
如果您使用 ptrace 更改目标的寄存器值,您将更改内核将看到的系统调用参数,或用户进程将看到的 return 值,具体取决于您是否这样做它在 syscall-enter-stop 或 syscall-exit-stop。
您的代码是 运行 at syscall-enter-stop:
if (regs.orig_eax == 26 && regs.ebx == PTRACE_TRACEME) {
regs.eax = 0;
PTRACE_E(PTRACE_SETREGS, pid, NULL, ®s);
break;
}
它将 eax(进入系统调用时为 -38)更改为 0。由于您的意图是将目标 PTRACE_TRACEME 请求中的 return 代码从 -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 会给出警告信息,因为它出乎意料地已经是目标的跟踪器。
程序步骤:
- 通过fork创建child进程并在其中调用execv
- Ptrace 附加到 child 进程
- 用 ptrace 做点什么
- 从 child 分离
- 执行 gdb -p child_pid
但是当 gdb 启动时,它写道 child 进程已经被跟踪。 如何从被跟踪的进程中分离出来,以便它可以被另一个进程跟踪?
执行以上操作的代码
#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, ®s);
if(regs.orig_eax == 26 && regs.ebx == PTRACE_TRACEME) {
regs.eax = 0;
PTRACE_E(PTRACE_SETREGS, pid, NULL, ®s);
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_SYSCALL 请求将使目标进程在进入 或退出系统调用时停止。 manual page 表示
Syscall-enter-stop and syscall-exit-stop are indistinguishable from each other by the tracer. The tracer needs to keep track of the sequence of ptrace-stops in order to not misinterpret syscall-enter-stop as syscall-exit-stop or vice versa.
如果您使用 ptrace 更改目标的寄存器值,您将更改内核将看到的系统调用参数,或用户进程将看到的 return 值,具体取决于您是否这样做它在 syscall-enter-stop 或 syscall-exit-stop。
您的代码是 运行 at syscall-enter-stop:
if (regs.orig_eax == 26 && regs.ebx == PTRACE_TRACEME) {
regs.eax = 0;
PTRACE_E(PTRACE_SETREGS, pid, NULL, ®s);
break;
}
它将 eax(进入系统调用时为 -38)更改为 0。由于您的意图是将目标 PTRACE_TRACEME 请求中的 return 代码从 -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 会给出警告信息,因为它出乎意料地已经是目标的跟踪器。