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会继续往更高的栈地址覆盖(跳过被换行符覆盖的局部变量区域),直到覆盖返回地址。
- 前12个
场景2:一次性输入32个A+回车
输入流是32个A + \n:- 前12个
A填满buffer; - 第13到32个
A会依次覆盖栈上的局部变量和保存的ebp; - 最后的
\n会覆盖返回地址的一部分,这时候你也会在栈的某个位置看到0x0a,而不是全是0x41。
- 前12个
核心差异总结
| 方法 | 对换行符的处理 | 溢出后的栈内容特征 |
|---|---|---|
gets()函数 | 丢弃换行符,不写入缓冲区 | 连续的0x41覆盖栈空间 |
fgetc()循环 | 读取并将换行符写入内存 | 栈中会出现0x0a,打断连续的0x41 |
另外你提到的“跳过部分内存地址”,其实不是真的跳过,而是这些地址被栈上的局部变量占据,溢出时先覆盖了这些变量——如果是gets(),这些变量会被0x41覆盖,看起来是连续的;而fgetc()因为写入了换行符,这些变量的位置被0x0a填充,就显得像是“跳过”了。
内容的提问来源于stack exchange,提问作者AlexP
相关产品推荐
相关产品推荐

