缓冲区溢出漏洞利用问题:无法覆盖$ebp与$eip寄存器
问题分析与解决方案
首先,你遇到的核心误解是目标缓冲区搞错了——你以为是在覆盖readString函数栈上的buf[44]对应的栈帧,但实际上,你的输入最终仅被复制到全局变量Name[44]里,根本没触碰到栈上的ebp和eip寄存器对应的内存区域,这就是为什么输入再多字符也不会覆盖栈帧、不会触发段错误的原因。
让我们先拆解readString的逻辑:
- 函数栈上创建局部缓冲区
buf[44],读入用户输入到这里(这里其实存在栈溢出风险,但你输入的超长字符只会覆盖buf后续的栈内存); - 后续仅将
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的栈帧做溢出:
- 确定返回地址偏移:在gdb中调试,设置断点在
readString内,通过p &buf查看缓冲区地址,p $ebp+4查看返回地址位置,计算两者的差值就是需要输入的前置字符数; - 构造payload:因为
main中给Name所在页设置了可执行权限,可以将修改grade的shellcode写入Name,然后让返回地址指向Name的起始地址,实现自定义代码执行。
总结
你之前的问题源于混淆了栈上局部缓冲区buf和全局缓冲区Name的区别,输入的超长字符仅溢出了栈帧但未被复制到Name,导致你无法通过观察Name周边内存看到溢出效果。而直接利用全局变量的内存布局,仅需45个字符就能快速达成修改grade的目标。
内容的提问来源于stack exchange,提问作者AlexP
相关产品推荐
相关产品推荐

