我正在尝试修改段错误处理程序中堆栈上的返回地址,以便它跳过错误指令。但是,如果我不直接调用信号处理程序,每当我尝试修改返回地址时,我都会遇到段错误。
当程序出现段错误时,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)是否需要更改?
最佳答案
使用sigaction
而不是signal
注册信号处理程序,并使用SA_SIGINFO
标志来获取siginfo_t
描述信号产生的原因。这将允许您处理由错误引起的 SIGSEGV
,该错误不同于显式 raise
d 或由 kill
、sigqueue
发送的错误> 等。这还为您提供了检查故障时的状态并可选择在返回之前更改状态所需的 ucontext_t
。
顺便说一句,一般来说,无论如何使用 signal
都是一个坏主意,因为未指定是否设置了 SA_RESTART
标志 - 并且您几乎总是想要 SA_RESTART
.养成使用 sigaction
并将 signal
视为已弃用的习惯。
关于c - 直接调用信号处理程序和通过 raise() 调用的信号处理程序之间的堆栈差异?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41904590/