Ubuntu 23.04中scanf触发Segmentation Fault的原因及学习路径
问题原因分析
你的代码存在明确的缓冲区溢出漏洞:char a[2]是仅能容纳2个字符(加上终止符\0的话是3字节)的数组,但scanf("%s", a)未限制输入长度,当输入AAchiching(共10个可见字符,加上自动添加的\0总长度为11字节)时,输入内容会超出a的缓冲区,覆盖栈上的其他数据。
两个Ubuntu版本出现差异的核心原因是系统组件版本及栈布局的变化:
- Glibc版本差异:Ubuntu 23.04使用的Glibc版本远高于18.04,新版本的
scanf实现对内存访问的检查更严格,溢出内容可能直接覆盖了栈帧的返回地址、栈基址等关键区域,导致函数执行时触发非法内存访问;而旧版本Glibc中,溢出内容仅覆盖了b数组,未触及栈帧的敏感部分,因此没有立即触发段错误。 - 栈布局差异:不同版本的GCC编译生成的栈帧结构、变量排列顺序存在差异。在Ubuntu 23.04的编译结果中,
a数组的位置更靠近栈帧的敏感区域,溢出后直接破坏了关键数据;而18.04的栈布局中,a和b的位置关系让溢出内容先覆盖b,未影响函数的正常执行流程。 - 内存防护默认配置:即使你添加了
-fno-stack-protector关闭栈保护,新版本系统在内存页权限、栈随机化等方面的默认配置更严格,溢出后更容易触发段错误。
深入学习渠道
- 官方文档
- 研读GCC官方手册,重点理解编译选项(如
-fno-pie、-O0、-fno-stack-protector)对程序内存布局的影响,以及栈帧的生成规则。 - 查阅Glibc的版本变更记录和stdio库文档,了解
scanf等函数在不同版本中的实现细节差异。
- 研读GCC官方手册,重点理解编译选项(如
- 专业书籍
- 《深入理解计算机系统》:系统讲解栈帧结构、缓冲区溢出、内存管理等底层原理,是理解这类问题的核心教材。
- 《漏洞战争:软件漏洞分析精要》:深入剖析缓冲区溢出的利用方式、不同系统的防护机制,适合进阶学习。
- 实践工具
- 使用
gdb调试,通过info frame、x/20x $rsp等命令对比两个系统中栈内存的布局差异,观察溢出前后的内存变化。 - 用
objdump -d chal查看编译后的汇编代码,分析不同版本中栈变量的排列和函数调用流程。 - 借助
readelf -l chal查看可执行文件的段权限,理解内存区域的访问控制规则。
- 使用
内容的提问来源于stack exchange,提问作者NobinPegasus
相关产品推荐
相关产品推荐

