CSAPP实验:汇编层面缓冲区溢出为何跳过4字节?
关于CSAPP练习3.46栈溢出跳过4字节的疑问解答
背景信息
我正在阅读《深入理解计算机系统》(CSAPP),完成练习3.46时,需使用GDB测试一段存在漏洞的C代码以探究其内部机制。给出的漏洞代码及自行添加的main函数如下:
/** vulnerable code from the task **/ char *get_line() { char buf[4]; char *result; gets(buf); result = malloc(strlen(buf)); strcpy(result, buf); return result; } /** main function that I added **/ int main(void) { get_line(); return 0; }
编译时使用-fno-stack-protector和-g选项关闭栈保护并保留调试信息,同时通过命令sudo sysctl -w kernel.randomize_va_space=0关闭地址空间随机化以便观察。
已获取main和get_line的反汇编结果,在gets(buf)处设置断点,记录了调用gets前的栈状态,以及输入超长字符串0123456789012345678901234触发溢出后的栈状态。
疑问
为何溢出不从$rsp开始,而是跳过了4字节?
解答
这是由函数调用的栈帧布局和编译器的栈对齐规则导致的(以下基于常见的x86-64 SysV ABI环境分析,32位环境逻辑类似):
- 首先,
get_line函数的局部变量包含char buf[4](占4字节)和char *result(x86-64下占8字节)。根据SysV ABI规范,函数栈帧需要满足16字节对齐要求。当进入get_line函数时,栈指针%rsp会被调整以满足对齐要求,编译器会在buf和%rsp指向的栈顶之间插入4字节的填充空间,保证整体栈帧的对齐。 - 当
gets写入超长数据时,会先填满buf的4字节,接着覆盖这段预留的4字节填充空间,之后才会开始覆盖result变量、返回地址等栈上的关键数据,这就造成了“溢出跳过4字节”的视觉效果。 - 如果是32位编译环境,原因类似:编译器为了局部变量的布局或栈帧结构(比如保存基址指针
%ebp),在buf和栈顶之间预留了4字节间隔,溢出时需要先填满这段间隔才会触及栈上的其他内容。
核心原因是编译器的栈对齐规则或栈帧布局策略,在buf数组与栈顶(%rsp指向位置)之间插入了4字节填充,因此溢出时会先消耗这段填充,再继续覆盖栈上其他区域。
内容的提问来源于stack exchange,提问作者Leyiang
相关产品推荐
相关产品推荐

