覆盖返回地址触发Segmentation Fault,求问题排查与原因分析
问题分析与解决
代码本身的错误
首先,main函数的参数类型不符合C标准:
int main(char argc, char **argv)
argc应为int类型,虽然当前调用时argc=2不会触发问题,但这是错误写法,需修正为int main(int argc, char **argv)。
段错误的核心原因
1. 索引计算错误,覆盖了栈上非法位置
在x86_64架构的栈帧中,局部数组buffer[8]是8个8字节的函数指针,共占64字节,位于基址指针rbp的下方(栈向低地址增长)。程序的返回地址存储在rbp+8的位置(调用main时被压入栈)。
你传入的\x0b对应索引11,计算其指向的栈位置:
假设buffer起始地址为buf_addr,则buf_addr + 11*8 = buf_addr + 88。结合栈帧布局,该位置并非返回地址,而是栈帧中上级调用的其他数据区域,覆盖此处会直接破坏栈帧结构,导致函数返回时栈指针异常,触发段错误。
2. 栈金丝雀保护机制拦截
GCC默认开启-fstack-protector-strong栈金丝雀保护:在栈帧的buffer数组与rbp之间,会插入一个随机生成的金丝雀值。函数返回时,libc会检查该值是否被篡改,一旦发现异常就直接触发崩溃,不会执行你期望的runme函数。
解决步骤
1. 关闭栈保护(可选关闭PIE便于调试)
编译时添加参数关闭栈保护,若需要固定代码段地址方便调试,可同时关闭PIE:
gcc -fno-stack-protector -no-pie program.c -o program
2. 用GDB确认正确索引
- 启动GDB:
gdb ./program - 在赋值语句处设置断点:
b main:12(替换为buffer[(int8_t) argv[1][0]] = runme;所在行号) - 运行程序:
r test - 查看
buffer的起始地址:p &buffer - 查看返回地址的位置:
x/8x $rbp+8 - 计算正确索引:
(返回地址 - buffer起始地址) / 8,将结果转为十六进制字符,用echo -ne传入即可。
3. 验证正确调用
例如,若计算出正确索引为9(十六进制\x09),执行:
./program "$(echo -ne '\x09')"
此时程序会正常执行runme函数,输出Hello, world!。
内容的提问来源于stack exchange,提问作者Josep
相关产品推荐
相关产品推荐

