c - x86, amd64 : Why SIGTRAP' ucontext instruction pointer does not point to related int3

标签 c linux x86 signals x86-64

正如标题所说 - ucontext_t 中的 rip 不指向引发 SIGTRAPint3。相反,它指向下一条指令。

这偏离了我(幼稚)的预期,即每条错误指令都将在从信号处理程序返回时重试(如果上下文未更改并且进程在处理程序中时未明确终止)。

另一方面,当获取 SIGILL 时 - 上下文指向错误指令。同样在 ARM 和 Aarch64 上 - SIGTRAP 上下文也指向相关的 bkpt #0/bpt #0

测试程序:

/* sigtest.c */ 

#define _GNU_SOURCE

#include <stdio.h>
#include <signal.h>
#include <ucontext.h>

extern void do_int3(void);
extern void do_ud2(void);

void sighandler(int signo, siginfo_t* info, void* context) {
    struct ucontext_t* uctx = context;
    /* Yes, printf() here is bad. I promise to never ever do this in real programs */
    printf("Got signal %d:\n"
           "\tsi_addr: %p\n"
           "\tcontext RIP: %p\n",
           signo,
           info->si_addr,
           (void*)uctx->uc_mcontext.gregs[REG_RIP]);
}

int main(int argc, char** argv) {
    struct sigaction sa = {};
    sa.sa_flags = SA_SIGINFO | SA_ONESHOT;
    sa.sa_sigaction = sighandler;
    sigemptyset(&sa.sa_mask);
    sigaction(SIGTRAP, &sa, NULL);
    sigaction(SIGILL, &sa, NULL);

    void (*fn)(void) = 0;

    if (argv[1][0] == 't') {
        fn = do_int3;
    }
    if (argv[1][0] == 'u') {
        fn = do_ud2;
    }

    printf("call function at %p\n", fn);
    fn();
    printf("call returned\n");
}
; do_int3.S 
    .text
    .globl  do_int3
    .type   do_int3, @function
do_int3:
    int3
    ret
    .size   do_int3, .-do_int3
; do_ud2.S 
    .text
    .globl  do_ud2
    .type   do_ud2, @function
do_ud2:
    ud2
    ret
    .size   do_ud2, .-do_ud2

编译运行:

$ cc sigtest.c do_int3.S do_ud2.S 
$ ./a.out t
call function at 0x5620b87c6908
Got signal 5:
    si_addr: (nil)
    context RIP: 0x5620b87c6909
call returned
$ ./a.out u
call function at 0x557d1bc1590a
Got signal 4:
    si_addr: 0x557d1bc1590a
    context RIP: 0x557d1bc1590a
Illegal instruction (core dumped)

很容易注意到,对于 SIGTRAPrip 值指向下一条指令,而不是指向 int3

为什么在SIGTRAP上下文中调整指令指针不重试相关的int3

最佳答案

Why is instruction pointer adjusted in SIGTRAP context to not retry related int3?

它不是由内核调整的;硬件推送的异常返回地址是int指令之后的地址,包括int3

请记住 int3the normal case of int n 仅略有不同例如 int 0x80int 被设计为类似于系统调用或远程调用,因此(异常)返回地址是int之后的地址。否则,除非内核在 iret 之前编辑了异常返回信息,否则 int 0x80 系统调用将永远重新运行。

那么为什么 Linux 的 int3/int 3 处理程序不将保存的 RIP 减 1?一方面,如果调试人员愿意,他们可以在软件中做到这一点,并且保持内核简单更好。 (对于确实想知道硬件推送的地址的软件来说,维护、效率和更少的分解。)

另一方面,1 字节的固定偏移量并不总是正确的:2 字节 CD 03 int 3 引发与 1 字节 CC int3 相同的异常.即使您想尝试,x86 机器代码也不会唯一地向后解码,因此混淆后的机器代码可能会给出一个地址,该地址并不是所执行指令的实际开始位置。 (尽管如果从那里解码,它将作为 int 3int3 运行)。例如如果向后看,2 字节 rep int3add al, 0xf3/int3 无法区分。

像 GDB 这样插入 int3 的软件会知道它插入了什么,并且需要知道目标机器的详细信息,因此可以处理偏移量。

关于c - x86, amd64 : Why SIGTRAP' ucontext instruction pointer does not point to related int3,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72973683/

相关文章:

c# - 为什么 32 位可执行文件不能在 WoW64 下运行?

C : error while writing file using UDP socket

python - 不阻塞地服务单个 HTTP 请求

linux - 如何将 Visual Studio Code 与 Haskell 和 Docker 结合使用

linux - 更改系统日志中的日期格式

assembly - 我如何在 NASM x86 中定义宏

c - 如何用内联汇编编译这个程序?

mysql - C语言每分钟计数

c - 字符串文字和 strcat

c - 阅读搜索算法完成的比较次数