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

缓冲区溢出漏洞利用问题:无法覆盖$ebp与$eip寄存器

问题分析与解决方案

首先,你遇到的核心误解是目标缓冲区搞错了——你以为是在覆盖readString函数栈上的buf[44]对应的栈帧,但实际上,你的输入最终仅被复制到全局变量Name[44]里,根本没触碰到栈上的ebp和eip寄存器对应的内存区域,这就是为什么输入再多字符也不会覆盖栈帧、不会触发段错误的原因。

让我们先拆解readString的逻辑:

  1. 函数栈上创建局部缓冲区buf[44],读入用户输入到这里(这里其实存在栈溢出风险,但你输入的超长字符只会覆盖buf后续的栈内存);
  2. 后续仅将buf的前44字节复制给s(也就是全局的Name数组),超出44字节的输入内容根本不会被写到Name里,只会留在readString的栈帧中。

1. 为什么输入71个A没覆盖ebp/eip?

假设是32位系统(栈向下增长),readString的栈帧布局大致是:

  • buf[44]:占据44字节,栈地址较高;
  • 局部变量i:4字节(int类型);
  • 保存的旧ebp:4字节;
  • 返回地址(对应eip的内容):4字节。

要覆盖返回地址,理论上需要输入至少44(buf) +4(i) +4(ebp) =52字节,第53-56字节才会覆盖返回地址。但你输入71个A却没触发崩溃,可能的原因有:

  • 输入时的换行符提前终止了fgetc的读取,实际读入的字符数不足71;
  • 系统开启了栈保护(比如GS Canary),溢出被检测后直接终止程序,而非触发段错误;
  • 如果你调试的是64位程序,栈帧布局偏移更大,需要更多字符才能覆盖rip(对应32位的eip)。

2. 实现目标的两种方法

你的最终目标是让程序无需输入"Alex"就能输出等级'6',这里有两种思路:

方法一:直接覆盖全局变量grade(最简单)

全局变量在内存中按声明顺序排列:

char grade = '3';
char Name[44];

也就是说,Name[44]的内存位置刚好就是grade的地址!输入第45个字符时,就会直接覆盖grade的值。

比如输入AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6(44个A加一个'6'),程序会输出:

Thank you, AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6.
I recommend that you get a grade of 6 on this assignment.

完全不需要触发栈溢出,就能达成目标。

方法二:栈溢出覆盖eip(代码执行方向)

如果你一定要通过覆盖eip实现,需要针对readString的栈帧做溢出:

  1. 确定返回地址偏移:在gdb中调试,设置断点在readString内,通过p &buf查看缓冲区地址,p $ebp+4查看返回地址位置,计算两者的差值就是需要输入的前置字符数;
  2. 构造payload:因为main中给Name所在页设置了可执行权限,可以将修改grade的shellcode写入Name,然后让返回地址指向Name的起始地址,实现自定义代码执行。

总结

你之前的问题源于混淆了栈上局部缓冲区buf和全局缓冲区Name的区别,输入的超长字符仅溢出了栈帧但未被复制到Name,导致你无法通过观察Name周边内存看到溢出效果。而直接利用全局变量的内存布局,仅需45个字符就能快速达成修改grade的目标。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 07:02:49