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

C语言缓冲区溢出攻击疑问:gets与fgetc栈覆盖行为差异

解答:gets与fgetc栈溢出行为差异的原因

嘿,这个问题我之前做栈溢出练习时也碰到过,核心原因就是**gets()和fgetc()对输入换行符的处理逻辑完全不同**,再加上栈上局部变量的布局影响,才出现了你看到的差异。我给你拆解清楚:

1. gets()的行为:完全丢弃换行符,连续写入所有输入字符

gets()的设计就是读取输入直到遇到换行符(\n)或EOF,而且它会直接丢弃换行符,不会把它写入目标缓冲区,最后只会在读取的字符末尾自动加一个\0作为字符串结束符。

当你输入32个A然后按回车时:

  • gets()会把这32个A全部从buffer起始地址开始写入,不管buffer的12字节限制;
  • 最后添加的\0也会跟着覆盖栈上的后续空间;
  • 所以整个栈上被溢出覆盖的区域都是连续的0x41(A的ASCII码),直到覆盖返回地址,触发0x41414141的段错误,完全符合你的预期。

2. fgetc()循环的行为:会读取并写入换行符,加上栈布局的影响

fgetc()是逐个读取输入流的每一个字符——包括你按下回车产生的换行符(\n,ASCII码0x0a),除非遇到EOF才会停止。再结合栈上的局部变量布局,就会出现你看到的现象:

假设你的代码里,栈上除了buffer[12],还有其他局部变量(比如循环用的int idx或者char c),这些变量会存在于buffer的高地址侧(也就是buffer溢出后会先覆盖的区域)。

两种输入场景的具体表现:

  • 场景1:分两次输入(12个A+回车+20个A+回车)
    输入流包含12个A + \n + 20个A + \n:

    • 前12个A填满buffer[12];
    • 接下来的\n(0x0a)会直接覆盖栈上buffer旁边的局部变量,所以你调试时会看到buffer之后出现0x0000000a;
    • 后续的20个A会继续往更高的栈地址覆盖(跳过被换行符覆盖的局部变量区域),直到覆盖返回地址。
  • 场景2:一次性输入32个A+回车
    输入流是32个A + \n:

    • 前12个A填满buffer;
    • 第13到32个A会依次覆盖栈上的局部变量和保存的ebp;
    • 最后的\n会覆盖返回地址的一部分,这时候你也会在栈的某个位置看到0x0a,而不是全是0x41。

核心差异总结

方法对换行符的处理溢出后的栈内容特征
gets()函数丢弃换行符,不写入缓冲区连续的0x41覆盖栈空间
fgetc()循环读取并将换行符写入内存栈中会出现0x0a,打断连续的0x41

另外你提到的“跳过部分内存地址”,其实不是真的跳过,而是这些地址被栈上的局部变量占据,溢出时先覆盖了这些变量——如果是gets(),这些变量会被0x41覆盖,看起来是连续的;而fgetc()因为写入了换行符,这些变量的位置被0x0a填充,就显得像是“跳过”了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 10:11:05