覆盖栈上saved eip返回地址不生效,触发SIGSEGV段错误的原因及解决方法
栈溢出执行shellcode崩溃问题排查
故障现象
你尝试覆盖栈上的返回地址saved eip接管C程序执行流程,相关调试信息如下:
(gdb) info frame Stack level 0, frame at 0xbffff550: eip = 0x8048831 in main (6.c:52); saved eip = 0xbffffdef source language c. Arglist at 0xbffff538, args: argc=6, argv=0xbffff5e4 Locals at 0xbffff538, Previous frame's sp is 0xbffff550 Saved registers: ebx at 0xbffff534, ebp at 0xbffff538, eip at 0xbffff54c
你准备的Hello World shellcode存放在环境变量SHELLCODE中,gdb查询地址如下:
(gdb) x/s *((char **)environ + 7) 0xbffffdef: "SHELLCODE=\353\023Y1\300\260\004\061\333C1Ҳ\017̀\260\001K̀\350\350\377\377\377Hello, world!\n\r"
程序执行后触发段错误:
Program received signal SIGSEGV, Segmentation fault. 0xbffffdfd in ?? ()
编译参数为-m32 -fno-stack-protector -z execstack -fno-PIE -no-pie -g,已关闭栈保护、开启栈执行权限、关闭PIE,配置无问题。
崩溃原因分析
- 核心问题是你覆盖返回地址用的
0xbffffdef指向的是环境变量字符串的起始位置,而非你编写的有效shellcode的起始。
你将shellcode存放在名为SHELLCODE的环境变量中时,系统会自动在你传入的shellcode内容前拼接SHELLCODE=前缀,这部分前缀是普通ASCII字符,对应的机器码并非合法的x86可执行指令,CPU执行到这部分非法指令时就会触发段错误。 - 从报错信息也可以验证这点:你跳转到
0xbffffdef执行后,运行了若干字节的非法指令,最终在偏移14字节的0xbffffdfd位置触发崩溃,完全符合前缀执行后的异常表现。
解决方法
- 修正跳转地址的偏移
SHELLCODE=的总长度为10字节,你只需要将覆盖saved eip的地址从0xbffffdef修改为0xbffffdef + 10 = 0xbffffdf9,即可直接跳转到你编写的有效shellcode起始位置执行。 - 降低偏移计算复杂度
可以将存放shellcode的环境变量名改为单字符,例如命名为A,这样前缀仅为A=共2字节,偏移计算更简单,出错概率更低。 - 增加nop滑行区提升容错率
在你编写的shellcode前填充数百个0x90(nop空指令),只要跳转地址落在任意一个nop的位置,CPU都会顺着滑行到后续的有效shellcode执行,能抵消地址计算的微小偏差。
内容的提问来源于stack exchange,提问作者Boris
相关产品推荐
相关产品推荐

