这是一个针对 32 位 Windows 的基于栈的缓冲区溢出漏洞利用的简单自包含教程。 无需外部指南——你需要的一切都在这里。
vulnerable.c:一个包含不安全的 gets() 函数的简单程序。messageBox.asm:一个小型汇编载荷,会在溢出后执行。exploit.c:一个演示如何触发溢出的示例。本项目传达一个核心概念:不安全的输入读取可能让攻击者覆盖返回地址并重定向执行流程。
gets() 没有大小限制在 vulnerable.c 中,程序执行了以下操作:
char buffer[32];
gets(buffer);
程序为 buffer 分配了 32 字节,然后调用 gets() 读取输入。
问题在于:gets() 不检查缓冲区大小。
它会持续读取字符,直到遇到换行符。
如果用户输入 40 或 50 个字符,多余的字符就会溢出到 32 字节缓冲区之外。
当一个 C 函数执行时,栈(一块内存区域)存储以下内容:
buffer)EBP(调用函数的基址指针)图像如下所示:
较低地址(如图所示栈顶)
[ buffer (32 字节) ]
[ 保存的 EBP (4 字节) ]
[ 返回地址 (4 字节) ]
较高地址(栈底)
当 gets() 用过多的输入溢出 buffer 时,多余的字节会覆盖保存的 EBP,然后覆盖返回地址。
如果我们精心构造溢出,将特定地址放入返回地址字段,那么当函数尝试返回时,CPU 将跳转到该地址。
buffer[32]。gets(buffer) 读取一行用户输入。gets() 没有大小检查,因此它会将所有 50 字节写入缓冲区。buffer,覆盖保存的 EBP 和返回地址。这是通过缓冲区溢出实现代码执行的最简单形式。
messageBox.asm 是一小段代码,设计用于在溢出后执行。
它执行以下操作:
LoadLibraryA,参数为字符串 "USER32.DLL",确保库在内存中。ExitProcess 安全地终止程序。关键点:这是一段可执行代码,溢出将执行流重定向到它之后运行。 当消息框出现在屏幕上时,它证明了三件事:
在实际攻击中,这个载荷可以做任何事情:窃取数据、创建用户、下载恶意软件等。 消息框只是一种可见、安全的方式来演示任意代码执行已经发生。
这个特定的载荷为 MessageBoxA (0x751D8830) 和 ExitProcess (0x7437ADB0) 使用了硬编码的内存地址。
这些地址仅对特定系统有效。对于不同的 Windows 版本或系统,需要调整载荷。

现代 Windows 具有多种安全功能来阻止此漏洞利用:
为了学习目的,我们将它们全部禁用。
使用 MSVC(Microsoft Visual C++)并指定特定标志:
cl /c /GS- /W3 /Zl vulnerable.c
link /SUBSYSTEM:CONSOLE /DYNAMICBASE:NO /NXCOMPAT:NO vulnerable.obj /OUT:vulnerable.exe
各标志含义:
/GS- 禁用堆栈缓冲区溢出保护。/DYNAMICBASE:NO 禁用地址空间布局随机化(ASLR)。/NXCOMPAT:NO 禁用 DEP,允许在栈上执行代码。如果你已有可执行文件,可以使用 editbin 禁用保护:
editbin /NXCOMPAT:NO /DYNAMICBASE:NO vulnerable.exe
vulnerable.exe。这份 README 解释了:
gets() 是什么以及为什么它不安全(没有大小限制)。你现在已经理解了整个缓冲区溢出漏洞利用流程。 阅读代码文件并与本说明对比,以巩固你的理解。
此示例仅用于学习。 请勿将本技术用于不属于你或未获得明确测试权限的系统。 未经授权访问计算机系统是违法的。