如何使用ptrace(2)修改系统调用行为?getpid返回异常原因求解
问题原因解析
- 寄存器修改错误:x86_64架构Linux下,
orig_rax寄存器用于存储系统调用号,系统调用的返回值会存储在rax寄存器中。你将orig_rax修改为999,内核会识别为要执行编号为999的系统调用,而当前系统并不存在999号系统调用,会返回-ENOSYS错误,ENOSYS的数值恰好为38,因此子进程拿到的返回值就是-38。 - 拦截时机错误:你当前是在系统调用进入内核前的断点处停止,此时系统调用尚未执行,若要修改返回值,需要等系统调用执行完成、返回用户态前的断点处再修改返回值寄存器。
正确实现逻辑
要修改getpid的返回值为999,按如下步骤操作即可:
- 子进程触发系统调用进入断点后,首先确认当前系统调用号(
orig_rax等于SYS_getpid) - 继续运行进程,等待系统调用执行完成触发退出断点
- 读取子进程寄存器,直接修改
rax的值为999 - 恢复子进程运行即可
修正后代码
#include <stdio.h> #include <sys/ptrace.h> #include <sys/user.h> #include <unistd.h> #include <signal.h> #include <sys/wait.h> #include <syscall.h> #include <stdlib.h> #include <errno.h> #include <string.h> int main(int argc, char **argv) { int pid = fork(); if (pid == 0) { //child ptrace(PTRACE_TRACEME, 0, 0, 0); kill(getpid(), SIGSTOP); int r = getpid(); printf("child pid %d from child\n", r); } else { //parent printf("child pid %d from parent\n", pid); waitpid(pid, 0, 0); // 第一次SYSCALL停在系统调用入口 ptrace(PTRACE_SYSCALL, pid, 0, 0); waitpid(pid, 0, 0); struct user_regs_struct uregs; ptrace(PTRACE_GETREGS, pid, 0, &uregs); if (uregs.orig_rax == SYS_getpid) { puts("Yes! It is getpid."); // 继续到系统调用退出断点 ptrace(PTRACE_SYSCALL, pid, 0, 0); waitpid(pid, 0, 0); // 读取寄存器修改返回值 ptrace(PTRACE_GETREGS, pid, 0, &uregs); uregs.rax = 999; ptrace(PTRACE_SETREGS, pid, 0, &uregs); ptrace(PTRACE_CONT, pid, 0, 0); } else { puts("NO!!! It is not getpid."); } // 等待子进程结束避免僵尸进程 waitpid(pid, 0, 0); } return 0; }
运行上述代码后即可看到子进程输出的getpid返回值为999。
内容的提问来源于stack exchange,提问作者Anonymous
相关产品推荐
相关产品推荐

