Memory Protections
Learning Objectives
- Explain what DEP/NX prevents and how it changes exploitation
- Explain how ASLR randomizes memory layout and what it defeats
- Explain how a stack canary detects (not prevents) an overflow
- Identify which protection a given crash or binary's behavior suggests is present
Three Mitigations, Three Different Jobs
Modern exploitation is hard not because any single defense is unbreakable, but because DEP, ASLR, and stack canaries each close a different door, and a working exploit has to deal with all three at once. None of them fixes the underlying bug (see Stack Overflows) — they make turning the bug into control much harder.
DEP / NX — Data Isn’t Executable
DEP (Data Execution Prevention, called NX — “No-eXecute” — at the hardware level) marks memory pages as either writable or executable, never both. The stack and heap are writable, so under DEP they’re not executable. Shellcode from e2, sitting in a stack buffer, simply can’t run — the CPU raises a fault the instant RIP tries to fetch from a non-executable page.
This is exactly why Return-Oriented Programming exists: if you can’t inject new executable code, you reuse code that’s already marked executable — the program’s own instructions — and just choose which pieces run.
ASLR — Moving the Target
Address Space Layout Randomization randomizes the base addresses of the stack, heap, shared libraries, and (with PIE binaries) the executable itself, freshly on every run. An offset that pointed exactly at system() or at your shellcode on one run points at nothing predictable on the next.
ASLR defeats hardcoded addresses specifically. A ROP chain or a jump target built from a fixed address baked into the exploit simply won’t line up once ASLR is on — the attacker needs to first leak an actual runtime address (from a crash, an information-disclosure bug, or a partial overwrite) before any address-dependent payload can work. Note what ASLR does not do: it doesn’t stop the overflow itself, and it doesn’t make code non-executable — it’s purely about not knowing where things are.
Stack Canaries — Detection, Not Prevention
A stack canary (or “stack cookie”) is a random value the compiler inserts between the local buffer and the saved RBP/return address, written at function entry and checked just before ret:
[ return address ]
[ saved RBP ]
[ canary ] <- checked before ret; overflow must pass through here
[ local buffer ]
A linear overflow — the strcpy-style pattern from e1 — has to overwrite the canary before it can reach the return address. If the value at ret time doesn’t match what was stored at entry, the program calls an abort handler instead of returning. Critically, the canary doesn’t stop the write from happening — the buffer still overflows, memory still gets corrupted — it only stops the corrupted return address from being used, and only if the overflow is sequential and doesn’t know or guess the canary’s value.
Reading the Signal
Given a crash or a binary, each protection leaves a distinct fingerprint:
| Observation | Suggests |
|---|---|
Crash is SIGSEGV at a garbage RIP value with no abort message first | Canary absent, or you already leaked/matched it |
Process calls __stack_chk_fail / aborts with a stack-smashing message | Canary present and it caught you |
| Shellcode placed correctly but execution faults immediately on entry to it | DEP/NX active — the page isn’t executable |
| Same exploit offset works reliably but the target address is wrong every run | ASLR active — need a leak, not a fixed address |
Real targets stack these together — a modern binary with a canary, NX, and ASLR all enabled forces an attacker to chain: leak an address to defeat ASLR, build a ROP chain to defeat NX, and avoid or leak the canary rather than blindly overwrite it.
Takeaway
DEP stops you from running injected code, ASLR stops you from knowing where anything is, and the canary just tells on you — none of them close the hole, they just raise the price of walking through it.