
Windows x86 PoC: Stack‑based buffer overflow with custom shellcode on legacy 32-bit Windows.
This is a simple, self-contained tutorial on stack-based buffer overflow exploits for 32-bit Windows. No external guides required—everything you need is here.
vulnerable.c: A simple program with an unsafe gets() function.messageBox.asm: A small assembly payload that would execute after the overflow.exploit.c: An example of how the overflow might be triggered.This project teaches one core idea: unsafe input reading can let an attacker overwrite the return address and redirect execution.
gets() has no size limitIn vulnerable.c, the program does this:
char buffer[32];
gets(buffer);
The program reserves 32 bytes for buffer, then calls gets() to read input.
The problem: gets() does not check the buffer size.
It keeps reading characters until it encounters a newline.
If the user types 40 or 50 characters, the extra characters overflow past the 32-byte buffer.
When a C function runs, the stack (a region of memory) stores:
buffer)EBP (base pointer from the calling function)Picture it like this:
Lower addresses (top of stack as drawn)
[ buffer (32 bytes) ]
[ saved EBP (4 bytes) ]
[ return address (4 bytes) ]
Higher addresses (bottom)
When gets() overflows buffer with too much input, the extra bytes overwrite the saved EBP and then the return address.
If we carefully craft the overflow to place a specific address in the return address field, the CPU will jump to that address when the function tries to return.
buffer[32] on the stack.gets(buffer) to read a line of user input.gets() has no size check, so it writes all 50 bytes into the buffer.buffer and overwrite the saved EBP and return address.This is the simplest form of code execution through a buffer overflow.
messageBox.asm is a small piece of code designed to run after the overflow.
It does the following:
LoadLibraryA with the string "USER32.DLL" to ensure the library is in memory.ExitProcess to safely terminate the program.The key point: this is executable code that runs after the overflow redirects execution to it. When the message box appears on screen, it proves three things:
In a real attack, this payload could do anything: steal data, create a user, download malware, etc. The message box is just a visible, safe way to demonstrate that arbitrary code execution happened.
This specific payload uses hardcoded memory addresses for MessageBoxA (0x751D8830) and ExitProcess (0x7437ADB0).
These addresses are specific to one system. The payload would need to be adjusted for a different Windows version or system.

Modern Windows has multiple security features that prevent this exploit:
For this learning exercise, we disable all of them.
Use MSVC (Microsoft Visual C++) with specific flags:
cl /c /GS- /W3 /Zl vulnerable.c
link /SUBSYSTEM:CONSOLE /DYNAMICBASE:NO /NXCOMPAT:NO vulnerable.obj /OUT:vulnerable.exe
Flag meanings:
/GS- disables stack buffer overrun protection./DYNAMICBASE:NO disables Address Space Layout Randomization (ASLR)./NXCOMPAT:NO disables DEP, allowing code on the stack to execute.If you already have the executable, you can disable protections with editbin:
editbin /NXCOMPAT:NO /DYNAMICBASE:NO vulnerable.exe
vulnerable.exe.This README explains:
gets() is and why it is unsafe (no size limit).You now understand the entire buffer overflow exploit flow. Read the code files and compare them to this explanation to solidify your understanding.
This example is for learning only. Do not use this technique against systems you do not own or have explicit permission to test. Unauthorized access to computer systems is illegal.