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

为何打印变量x的内存地址会影响缓冲区溢出结果?

问题分析:打印变量地址改变缓冲区溢出效果的原因

这个问题的核心是编译器的栈布局优化与变量位置调整——当你使用&x取变量地址时,编译器会改变x在栈上的存储位置,让它不再和字符串数组str相邻,自然就不会被溢出覆盖了。

1. 对比两种场景的栈布局差异

我们直接从汇编代码里拆解变量的存储位置:

无printf("%p: ", &x)的场景(代码1)

从汇编的赋值指令可以明确看到:

  • 字符串数组str存储在25(%esp)起始的位置(movl $1953719636, 25(%esp)对应"Testt"的前4字节ASCII值)
  • 字符变量x存储在31(%esp)的位置(movb $88, 31(%esp),88是'X'的ASCII码)

str数组实际占6字节("Testt" + 终止符\0),刚好占据25(%esp)到30(%esp)的空间,和x所在的31(%esp)直接相邻。当你执行strcpy(str, "Hello world")(字符串含终止符共12字节)时,溢出的字节会直接覆盖到x的存储位置,所以x被改成了'w'。

有printf("%p: ", &x)的场景(代码2)

汇编里的赋值指令显示变量位置发生了明显变化:

  • str数组存储在26(%esp)起始的位置
  • 字符变量x存储在25(%esp)的位置

此时x位于str的栈地址前方(栈空间是从高地址向低地址增长,但数组内部是从低到高存储),而strcpy的溢出是向高地址方向写入,只会覆盖str后方的栈空间,完全碰不到在它前面的x,所以x的值保持为'X'不变。

2. 为什么取&x会触发布局变化?

编译器在处理局部变量的栈布局时,会根据变量的使用场景做优化:

  • 当你没有取x的地址时,编译器认为x是一个可被灵活放置的局部变量,为了栈对齐或空间利用效率,会把它放在str的后方,刚好形成相邻布局。
  • 当你使用&x获取地址时,x必须拥有一个稳定的内存地址(要传递给printf作为参数),编译器会调整它的位置——可能是为了规避溢出风险,也可能是为了满足16字节栈对齐规则(汇编里的andl $-16, %esp就是强制栈对齐到16字节边界),最终把x放到了str的前方,躲开了溢出范围。

3. 从运行结果验证结论

两段代码的运行输出也能直接对应布局变化:

  • 代码1中str的地址是0061FF29,x在它后方的相邻地址,溢出后被覆盖;
  • 代码2中str的地址是0061FF2A,x的地址是0061FF29,明确在str的地址前方,溢出写不到该位置。

这个现象也侧面说明:局部变量的栈布局是编译器自主决定的,没有固定顺序,依赖栈布局的缓冲区溢出行为完全不可靠——这也是现代编译器默认开启栈保护(Stack Smashing Protector)的原因之一。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 09:55:22