You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

覆盖返回地址触发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确认正确索引

  1. 启动GDB:gdb ./program
  2. 在赋值语句处设置断点:b main:12(替换为buffer[(int8_t) argv[1][0]] = runme;所在行号)
  3. 运行程序:r test
  4. 查看buffer的起始地址:p &buffer
  5. 查看返回地址的位置:x/8x $rbp+8
  6. 计算正确索引:(返回地址 - buffer起始地址) / 8,将结果转为十六进制字符,用echo -ne传入即可。

3. 验证正确调用

例如,若计算出正确索引为9(十六进制\x09),执行:

./program "$(echo -ne '\x09')"

此时程序会正常执行runme函数,输出Hello, world!。

内容的提问来源于stack exchange,提问作者Josep

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 14:40:39