如何使用ptrace(2)来改变系统调用的行为?

3
有没有使用ptrace影响其他进程执行的指南或示例(尤其是ARM),或者相关库?例如,使它相信某些数据出现在文件描述符上(即使用某些结果释放select/poll并“回答”内核之前的读取系统调用)。期望涉及PTRACE_SYSEMU的内容。
这可以以可移植的方式完成吗?我想要像libc覆盖LD_PRELOAD技巧一样的东西,但可以在运行时附加。
这可以通过一些gdb命令完成吗?
理想情况下,如果有某个库,在实际调用之前或之后轻松、可移植地钩入系统调用并编辑它们(或模拟它们),就像使用LD_PRELOAD时那样
@link 有关使用PTRACE_SYSEMU的好指南吗?

你想查看这个文件,了解ptrace如何影响其他进程的执行:http://lxr.free-electrons.com/source/tools/testing/selftests/breakpoints/breakpoint_test.c - Pradeep Goswami
1个回答

1

您可以使用PTRACE_SYSCALL请求:它重新启动子进程(就像PTRACE_CONT一样),但安排它在下一次进入或退出系统调用时停止。例如(假设为x86构建的内核):

#include <sys/ptrace.h>
#include <signal.h>
#include <linux/user.h>
#include <sys/types.h>
#include <sys/wait.h>

int main(int argc, char **argv)
{
    int status = 0, pid, r;
    struct user_regs_struct uregs;

    if ((pid = fork()) == 0) {
        printf("pid = %d, ppid = %d\n", getpid(), getppid());
        ptrace(PTRACE_TRACEME, 0, 0, 0);
        kill(getpid(), SIGINT);
        r = getpid();
        printf("%d\n", r);
    } else {
        wait(&status);
        ptrace(PTRACE_SYSCALL, pid, 0, 0);
        wait(&status);
        ptrace(PTRACE_GETREGS, pid, 0, &uregs);

        /* this prints the syscall number of getpid */
        printf("syscall nr: %d\n", uregs.orig_eax);
        /* 64 is syscall number of getppid */
        uregs.orig_eax = 64;
        ptrace(PTRACE_SETREGS, pid, 0, &uregs);
        ptrace(PTRACE_CONT, pid, 0, 0);
        wait(&status);
        if(WIFEXITED(status))
            printf("we're done\n");
    }
}

子进程打印其PID并向自身发送一个信号。由于之前调用了ptrace(),这意味着它将被停止。

父进程等待此事件发生并使用PTRACE_SYSCALL重新启动子进程,然后等待。接下来,子进程调用getpid系统调用,并再次被停止。父进程使用PTRACE_GETREGS调用来窥视子进程的寄存器,其中eax保存系统调用号。父进程将其更改为getppid的系统调用号,然后再次允许子进程继续。因为在调用系统调用之前更改了系统调用号,所以子进程现在将调用getppid而不是getpid

使用ptrace进行此操作可能是可移植的,但我尚未测试过。在gdb中,您还可以使用catch syscall命令。


对于x86架构,还有injcode(和其他一些工具)。那么有没有类似的库或(例如)ARM示例呢?是否可以在比CPU寄存器调整更高级别的层次上完成? - Vi.
我不熟悉injcode,但从其描述来看,这似乎是可能的。通常,如果您想要干扰进程内部,ptrace(因此也包括调试器)可能是您可以获得的最高级别的抽象。 - Michael Foukarakis
使用gdb命令比ptrace更高级。也许有一些中间件(例如“libptrace”),可以以各种方式提供易于使用的API来跟踪其他进程;请注意:现有的“libptrace”似乎只支持x86架构... - Vi.
希望其他人能够回答那个问题。 - Michael Foukarakis
1
在我的情况下,父进程捕获了对write的调用。我认为glibc缓存了getpid()的结果。 - basin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接