为何向容量128的缓冲区发送127个字符会触发Segmentation fault?
缓冲区容量128,输入127字符却触发段错误的原因
问题代码
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #define BANNER "Welcome to " LEVELNAME ", brought to you by https://exploit.education" char *gets(char *); void start_level() { char buffer[128]; gets(buffer); } int main(int argc, char **argv) { printf("%s\n", BANNER); start_level(); }
测试现象
输入126个字符时程序正常退出:
user@phoenix-amd64:/opt/phoenix/amd64$ python -c 'print("A"*126)' | ./stack-five Welcome to phoenix/stack-five, brought to you by https://exploit.education user@phoenix-amd64:
输入127个字符时触发段错误:
user@phoenix-amd64:/opt/phoenix/amd64$ python -c 'print("A"*127)' | ./stack-five Welcome to phoenix/stack-five, brought to you by https://exploit.education Segmentation fault
疑问
缓冲区容量为128,输入127个字符加上NULL终止符应该刚好填满缓冲区,为何会触发崩溃?
原因分析
核心问题出在自定义gets函数的行为以及Pythonprint的输出特性:
Python
print的额外输出字符:
Python的print函数默认会在输出末尾添加一个换行符(\n)。所以print("A"*127)实际输出的是127个A加上1个换行符,总共有128个字符。自定义
gets与标准库行为不同:
代码中显式声明了char *gets(char *);,说明这不是标准库的gets函数。标准gets会丢弃换行符,仅将读取的字符存入缓冲区后追加NULL终止符;但这个练习中的自定义gets会将换行符也存入缓冲区,之后再追加NULL终止符。溢出破坏栈帧触发崩溃:
- 输入126个
A时,实际输入是126个A+换行符,共127个字符,加上NULL终止符后总长度为128,刚好填满buffer[128],不会溢出到栈帧的其他区域(如RBP栈基址、返回地址),程序正常返回。 - 输入127个
A时,实际输入是127个A+换行符,共128个字符,加上NULL终止符后总长度为129,超出缓冲区1字节。这多出来的1字节会覆盖栈帧中的RBP(栈基址)的低字节,导致start_level函数返回时,栈帧恢复逻辑出错,最终触发段错误。
- 输入126个
64位系统中,栈帧从高到低的布局为:返回地址 → RBP栈基址 → 缓冲区。当RBP被覆盖后,执行leave指令恢复栈帧时会使用错误的RBP值,导致rsp指向非法区域,ret指令读取错误的返回地址,直接触发段错误。
内容的提问来源于stack exchange,提问作者E235
相关产品推荐
相关产品推荐

