【问题标题】:Stack differences between a signal handler being called directly and by raise()?直接调用和 raise() 调用的信号处理程序之间的堆栈差异?
【发布时间】:2017-01-27 23:18:04
【问题描述】:

我正在尝试在段错误处理程序中修改堆栈上的返回地址,以便它跳过错误指令。但是,如果我不直接调用信号处理程序,则每当我尝试修改返回地址时都会出现段错误。

当程序发生段错误时,gdb 不适合调试,但是当我执行info frame 时,我看到,在段错误之后,有“帧级别 2”而不是“帧级别 0”?我不知道 GDB 从哪里得到这些信息,因为当我尝试x/12xw $ebp 或任何字数时,我看不到main() 的返回地址...

在 CentOS linux 上使用 -m32 -z execstack -fno-stack-protector 编译

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

void segment_fault_handler(int signum)
{
    char* ret = (char*)(&signum)-4;
    *(ret) += 8;
}

int main()
{
    int phail = 0;

    signal(SIGSEGV, segment_fault_handler);

    segment_fault_handler(7); //Only by using this can I skip the next instruction

    phail = *( (int *) 0);

    printf("Win!\n");

    return 0;
} 

我加8的原因是因为main()中的phail指令是8个字节:

0x080484e2 <+37>:   movl   $0x7,(%esp)
0x080484e9 <+44>:   call   0x8048480 <segment_fault_handler>
0x080484ee <+49>:   mov    $0x0,%eax
0x080484f3 <+54>:   mov    (%eax),%eax
0x080484f5 <+56>:   mov    %eax,0x1c(%esp)
0x080484f9 <+60>:   movl   $0x80485b4,(%esp)
0x08048500 <+67>:   call   0x8048350 <puts@plt>

我需要稍微增加偏移量吗?在处理段错误情况时,我访问堆栈的方法(我认为对应于 EBP+4)是否需要更改?

【问题讨论】:

  • 您需要修改传递给处理程序的ucontext_t结构中保存的PC,不是信号处理函数的立即返回地址。 (并且不要直接调用它。)stackoverflow.com/questions/5119288/… 的示例程序展示了如何访问ucontext_t 结构。

标签: c gcc segmentation-fault stack-overflow disassembly


【解决方案1】:

使用sigaction而不是signal注册信号处理程序,并使用SA_SIGINFO标志获取描述信号原因的siginfo_t。这将允许您处理由不同于显式raised 或由killsigqueue 等发送的故障引起的SIGSEGV。这也为您提供了检查当时状态所必需的ucontext_t并在返回之前选择更改它。

顺便说一句,总的来说signal 无论如何都不是一个好主意,因为它是否设置了SA_RESTART 标志是未指定的——而且你几乎总是想要SA_RESTART。养成使用sigaction 并将signal 视为已弃用的习惯。

【讨论】:

    猜你喜欢
    • 2010-11-28
    • 1970-01-01
    • 1970-01-01
    • 2013-05-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-27
    相关资源
    最近更新 更多