Memory-corruption exploitation is fundamentally a knowledge problem: the attacker who overwrites a return address must know what value to write — where the shellcode, the libc function, the ROP gadgets live. For decades those addresses were the same on every machine running the same binary; exploits were literally portable constants (the Morris worm hardcoded a stack address in 1988). ASLR is the countermeasure that made addresses a secret: randomize the layout per-execution, and the hardcoded exploit dies. How it’s implemented — and how it’s beaten — is a compact education in both memory layout and security economics.

What Actually Gets Randomized

Run cat /proc/self/maps twice; on any modern Linux (kernel.randomize_va_space = 2) the addresses differ each run:

  • Stack: random top-of-stack offset.
  • mmap base: shared libraries, and everything mmap’d, slide together from a randomized base — libc’s address changes per run (though note: one base; the inter-library deltas mostly don’t change, which matters below).
  • Heap: brk start randomized relative to the executable.
  • The executable itself: only if compiled as PIE (position-independent), making the main binary relocatable like a shared library. This was the slow one — PIE costs were real on 32-bit x86 (a register sacrificed to GOT addressing; measurable slowdowns),