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

使用ptrace系统调用追踪时如何捕获SIGILL信号?

问题原因

你遇到的核心问题是:PTRACE_SYSCALL会让被追踪进程(tracee)在进入和退出系统调用时停止,而你的tracee在恢复执行后,首先触发的是之前raise(SIGSTOP)系统调用的退出停止点,而非无效指令的SIGILL信号。

当tracee调用raise(SIGSTOP)时,这个函数最终会触发kill系统调用。由于tracee已设置PTRACE_TRACEME,内核会在处理SIGSTOP信号时让tracee停止,此时你的tracer通过第一个waitpid捕获到SIGSTOP。但此时kill系统调用并未完全执行完毕——当你调用ptrace(PTRACE_SYSCALL, pid, 0, 0)让tracee恢复执行时,内核会先完成kill系统调用的剩余流程,然后在系统调用的退出点触发停止,此时因为你设置了PTRACE_O_TRACESYSGOOD,停止信号会被标记为SIGTRAP|0x80。

而此时tracee还未执行到那条无效汇编指令,自然不会触发SIGILL。如果使用PTRACE_CONT,tracee会直接跳过系统调用的停止点,继续执行后续代码,直到触发SIGILL并停止。

解决方案

要正确捕获SIGILL,你需要在处理完系统调用的停止点后,再次让tracee继续执行,此时它才会执行无效指令并触发SIGILL。修改你的tracer函数如下:

void tracer(pid_t pid) {
    int status;
    waitpid(pid, &status, 0);
    assert(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP);

    ptrace(PTRACE_SETOPTIONS, pid, 0, PTRACE_O_EXITKILL | PTRACE_O_TRACESYSGOOD);
    
    // 第一次PTRACE_SYSCALL:处理系统调用退出停止点
    ptrace(PTRACE_SYSCALL, pid, 0, 0);
    waitpid(pid, &status, 0);
    printf("stop sig (syscall exit) = %d\n", WSTOPSIG(status)); // 输出SIGTRAP|0x80
    
    // 第二次PTRACE_SYSCALL:让tracee继续执行,此时会触发SIGILL
    ptrace(PTRACE_SYSCALL, pid, 0, 0);
    waitpid(pid, &status, 0);
    printf("stop sig (SIGILL) = %d\n", WSTOPSIG(status)); // 输出SIGILL
}
额外说明

GDB能够区分这两种停止事件,是因为它会在每次捕获到停止后,检查停止的原因:

  • 如果是SIGTRAP|0x80,则判定为系统调用停止,继续执行等待下一个事件;
  • 如果是其他信号(如SIGILL),则判定为信号触发的停止,通知用户。

你需要在自己的tracer中实现类似的逻辑:循环处理停止事件,根据WSTOPSIG(status)的值判断是系统调用停止还是信号停止,再进行相应处理。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 01:24:55