我试图在段错误处理程序中修改堆栈上的返回地址,以便它跳过错误指令。但是,每当我尝试修改返回地址时,如果我没有直接调用信号处理程序,就会出现段错误。
当程序执行段错误时,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)是否需要更改?
答案 0 :(得分:0)
使用sigaction
而非signal
注册信号处理程序,并使用SA_SIGINFO
标志获取描述信号原因的siginfo_t
。这样,您就可以处理因错误导致的SIGSEGV
与raise
或kill
,sigqueue
等发送的ucontext_t
不同。这也为您提供了{{1}必须检查故障时的状态,并在返回之前选择更改它。
signal
一般来说使用SA_RESTART
是一个坏主意,因为它未指定是否设置了SA_RESTART
标志 - 并且您几乎总是想要sigaction
。习惯使用signal
并将{{input}}
视为已弃用。