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

GCC编译C语言缓冲区溢出测试代码未触发段错误原因咨询

问题

在Numberphile频道的缓冲区溢出相关视频中了解到缓冲区溢出的实现方式后,我尝试自行复现。编写的测试代码如下,除buffer数组大小外和视频示例完全一致,但输入长度大于buffer大小的字符串时,并没有像视频演示那样触发段错误,想知道具体原因。已知编译器是影响结果的关键因素,我当前使用的编译器为GCC。

#include <stdio.h>
#include <string.h>

int main(int argc, char** argv){
    char buffer[50];
    strcpy(buffer, argv[1]);
    
    return 0;
}

回答

复现失败的核心原因是现代GCC默认开启了多层栈安全防护,和视频里用的无任何防护的老旧32位编译环境行为差异极大,具体影响点如下:

  • 栈空间对齐预留:GCC为了适配SIMD指令的内存对齐要求,不会严格按你声明的char buffer[50]刚好分配50字节栈空间,会默认向上对齐到16字节整数倍,实际给buffer预留的栈空间有64字节。只要输入的字符串长度没超过预留空间+栈帧保存的寄存器值长度,根本碰不到栈上的返回地址,自然不会触发内存访问违规。
  • 栈金丝雀(Stack Canary)保护:GCC默认编译时会在栈帧返回地址前插一个随机生成的校验值,缓冲区溢出要覆盖返回地址必然先篡改这个校验值,函数返回前校验失败会直接终止程序,根本走不到跳转非法地址触发段错误的流程。
  • FORTIFY_SOURCE安全检查:默认编译优化等级下,GCC会联动glibc对strcpy这类危险字符串函数做边界检查,如果检测到拷贝长度超过编译期可确定的缓冲区大小,会直接终止程序,不会执行无边界的内存拷贝。
  • 架构差异:视频演示用的是32位系统,栈帧结构简单,返回地址只占4字节,溢出少量字节就能覆盖;现在主流是64位系统,栈帧有更多预留空间,需要输入更长的字符串才能碰到返回地址。

复现视频效果的编译方法

要得到和视频完全一致的运行结果,需要手动关闭GCC所有默认安全防护,指定32位编译模式,编译命令如下:

gcc -m32 -fno-stack-protector -D_FORTIFY_SOURCE=0 -O0 test.c -o vuln

各参数作用:

  • -m32:生成32位x86程序,栈帧布局和视频中的老旧环境一致
  • -fno-stack-protector:关闭栈金丝雀校验机制
  • -D_FORTIFY_SOURCE=0:关闭glibc对字符串操作函数的运行时边界检查,让strcpy执行无边界拷贝
  • -O0:关闭所有编译优化,避免编译器调整栈布局或者直接优化掉buffer变量

注意:就算关完所有保护,也不是输入超过50字节就立刻报错。32位栈帧里,buffer往上先存着4字节的旧ebp寄存器值,再往上才是4字节的返回地址,至少要输入58字节以上的字符串才能覆盖到返回地址;输入长度不够的话只会覆盖未使用的栈预留空间,程序会正常退出,没有任何异常提示。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.31 23:51:25