You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过ptrace为被拦截的子进程系统调用正确设置errno?

问题核心原因

你当前的操作流程漏了「系统调用退出时重新获取寄存器上下文」的步骤,且修改逻辑不符合Linux系统调用的错误返回规则,才会导致errno不生效。


底层机制说明

Linux下x86_64架构的系统调用出错时,内核会在RAX寄存器中存储负的错误码,用户态C库收到返回值后,会判断如果RAX值在[-4095, -1]区间,就会将errno设置为-RAX,同时给上层应用返回-1。你需要按照这个规则修改寄存器值才能正确触发errno设置。


你代码的两个核心问题

  1. 系统调用执行完成触发退出停止后,你没有重新调用PTRACE_GETREGS获取最新的寄存器上下文,直接使用了系统调用入口时保存的旧寄存器变量,修改操作未作用到实际生效的上下文上。
  2. 仅用PTRACE_POKEUSER修改单寄存器后没有验证是否写入成功,更稳妥的方式是全量写回寄存器结构体。

修正后的代码流程

if (is_syscall_blocked(regs.orig_rax)) {
  regs.orig_rax = -1; // 替换为无效系统调用号
  if (ptrace(PTRACE_SETREGS, pid, 0, &regs) == -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, &regs) == -1)
  FATAL("%s", strerror(errno));

// 判断是否是我们之前阻断的系统调用
if (regs.orig_rax == -1) {
  // 将RAX设置为负的错误码,这里对应EPERM
  regs.rax = -EPERM;
  // 写回修改后的寄存器
  if (ptrace(PTRACE_SETREGS, pid, 0, &regs) == -1)
    FATAL("%s", strerror(errno));
}

注意事项

  • 不要直接给RAX设置正数错误码,必须传负值,否则C库会认为系统调用执行成功,不会修改errno。
  • 确保你区分开了系统调用入口和出口的两次ptrace停止事件,修改操作必须在出口停止阶段执行,入口阶段修改RAX会被内核覆盖。

内容的提问来源于stack exchange,提问作者h3xcode

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.28 21:06:03