如何通过ptrace为被拦截的子进程系统调用正确设置errno?
问题核心原因
你当前的操作流程漏了「系统调用退出时重新获取寄存器上下文」的步骤,且修改逻辑不符合Linux系统调用的错误返回规则,才会导致errno不生效。
底层机制说明
Linux下x86_64架构的系统调用出错时,内核会在RAX寄存器中存储负的错误码,用户态C库收到返回值后,会判断如果RAX值在[-4095, -1]区间,就会将errno设置为-RAX,同时给上层应用返回-1。你需要按照这个规则修改寄存器值才能正确触发errno设置。
你代码的两个核心问题
- 系统调用执行完成触发退出停止后,你没有重新调用
PTRACE_GETREGS获取最新的寄存器上下文,直接使用了系统调用入口时保存的旧寄存器变量,修改操作未作用到实际生效的上下文上。 - 仅用
PTRACE_POKEUSER修改单寄存器后没有验证是否写入成功,更稳妥的方式是全量写回寄存器结构体。
修正后的代码流程
if (is_syscall_blocked(regs.orig_rax)) { regs.orig_rax = -1; // 替换为无效系统调用号 if (ptrace(PTRACE_SETREGS, pid, 0, ®s) == -1) FATAL("%s", strerror(errno)); } // 运行系统调用,等待系统调用退出事件 if (ptrace(PTRACE_SYSCALL, pid, 0, 0) == -1) FATAL("%s", strerror(errno)); if (waitpid(pid, 0, 0) == -1) FATAL("%s", strerror(errno)); // 关键:重新获取系统调用退出后的寄存器上下文 if (ptrace(PTRACE_GETREGS, pid, 0, ®s) == -1) FATAL("%s", strerror(errno)); // 判断是否是我们之前阻断的系统调用 if (regs.orig_rax == -1) { // 将RAX设置为负的错误码,这里对应EPERM regs.rax = -EPERM; // 写回修改后的寄存器 if (ptrace(PTRACE_SETREGS, pid, 0, ®s) == -1) FATAL("%s", strerror(errno)); }
注意事项
- 不要直接给RAX设置正数错误码,必须传负值,否则C库会认为系统调用执行成功,不会修改errno。
- 确保你区分开了系统调用入口和出口的两次ptrace停止事件,修改操作必须在出口停止阶段执行,入口阶段修改RAX会被内核覆盖。
内容的提问来源于stack exchange,提问作者h3xcode
相关产品推荐
相关产品推荐

