πΊοΈ Process Memory Layout
When a program runs, the operating system gives it its own virtual memory space. This space is divided into regions with different purposes:
Main Regions
| Region | Content | Permissions |
|---|---|---|
.text |
Program code (Assembly instructions) | Read + Execute |
.data |
Initialized global variables | Read + Write |
.bss |
Uninitialized global variables | Read + Write |
| Heap | Dynamic allocations (malloc, new) | Read + Write |
| Stack | Local variables, return addresses | Read + Write |
π The Stack
A LIFO (Last In, First Out) data structure - the last item that enters is the first to leave.
Used to store local variables, function parameters, and return addresses.
What actually "enters" the Stack? Data - values, numbers, addresses:
| What enters | Example | Explanation |
|---|---|---|
| Register value | PUSH EAX |
The value inside EAX enters the Stack |
| Constant number | PUSH 5 |
The number 5 enters the Stack |
| Return address | CALL func |
Address of next instruction enters |
In short: "enters" = the value PUSH stores on Stack. "exits" = the value POP retrieves.
π― Two Important Concepts - Don't Confuse Them!
| Concept | Meaning |
|---|---|
| LIFO | Order of insertion and removal: last in = first out |
| Grows Downward | Direction of addresses: when adding data, the address number decreases |
Imagine a stack of plates:
βββββββββββ
β Plate 3 β β Last in, First out!
βββββββββββ€
β Plate 2 β
βββββββββββ€
β Plate 1 β β First in, Last out
βββββββββββ
The last plate you placed is the first one you'll remove. That's LIFO.
π What "Grows Downward" Means
In memory, high addresses are "at the top" and low addresses are "at the bottom". When you PUSH, ESP's address decreases (smaller number):
High addresses (top)
βββββββββββββββββββββββ 0xFFFF0010
β empty β
βββββββββββββββββββββββ€ 0xFFFF000C
β empty β
βββββββββββββββββββββββ€ 0xFFFF0008 β ESP was here BEFORE push
β OLD DATA β
βββββββββββββββββββββββ€ 0xFFFF0004 β ESP is here AFTER push
β NEW VALUE β (the pushed value)
βββββββββββββββββββββββ
Low addresses (bottom)
PUSH = Store value + ESP goes DOWN (address decreases)
POP = Retrieve value + ESP goes UP (address increases)
ESP always points to the LAST item that was pushed.
When you POP, you get that last item first.
The direction of addresses doesn't change the order of operations!
Stack Pointer - a register that always points to the top of the Stack (the last item that entered).
Base Pointer - a register that points to the base of the current Stack Frame. Used to access parameters and local variables.
Stack Operations
PUSH EAX ; Store EAX on Stack, ESP decreases by 4 POP EBX ; Load value from Stack into EBX, ESP increases by 4
π² Stack Frame
A region on the Stack that belongs to a specific function. Contains the local variables, parameters, and return address of that function.
When a function is called, a new Stack Frame is created:
push ebp ; Save old EBP on Stack mov ebp, esp ; EBP now points to new Frame base sub esp, 0x10 ; Allocate 16 bytes for local variables
mov esp, ebp ; Restore ESP to original state pop ebp ; Restore old EBP ret ; Return to caller function
π Important: Variable Names Don't Exist in Assembly!
In C code, there are variable names like a and b.
But in Assembly, there are only memory addresses - the names disappear after compilation!
C code: Assembly:
βββββββββββββββββββ βββββββββββββββββββββββββββ
β int a = 5; β β β mov dword [ebp-4], 5 β
β int b = 10; β β β mov dword [ebp-8], 10 β
β a = a + b; β β β mov eax, [ebp-4] β
β β β add eax, [ebp-8] β
β β β mov [ebp-4], eax β
βββββββββββββββββββ βββββββββββββββββββββββββββ
Named variables Just memory addresses!
| C Variable | Assembly Location | Explanation |
|---|---|---|
a |
[EBP-4] |
First local variable (4 bytes from EBP) |
b |
[EBP-8] |
Second local variable (8 bytes from EBP) |
param1 |
[EBP+8] |
First parameter (above EBP) |
When you see mov eax, [ebp-4], think:
"Loading some local variable into EAX"
Your job in RE is to figure out what that variable represents based on how it's used!
ποΈ The Heap
A memory region for dynamic allocations - when you don't know ahead of time how much memory you need.
In C: malloc(), free()
In C++: new, delete
| Stack | Heap |
|---|---|
| Automatic allocation | Manual allocation |
| Automatically freed at function end | Must be manually freed! |
| Fast | Slower |
| Limited size | Larger size available |
| Grows downward (addresses decrease) | Grows upward (addresses increase) |
π Virtual Memory
An abstract numbering system that the OS creates for each process. Each program "thinks" it has a huge memory space to itself, but the OS maps virtual addresses to real locations.
π The Difference Between Virtual and Physical
| Term | What it is |
|---|---|
| RAM | Physical Memory - the actual hardware (memory chips in the computer) |
| Virtual Memory | Abstraction - a numbering system the OS manages (doesn't "sit" anywhere) |
| Virtual Address | The address the program sees (e.g., 0x00401000) |
| Physical Address | The actual address in RAM |
π Where Does Data Actually Sit?
Program uses Virtual Address (e.g., 0x00401000)
β
CPU + Page Table translate
β
βββββββββββββββββββββββββββββ
β Option 1: RAM β β Fast! (physical memory chips)
β Option 2: Disk β β Slow! (pagefile.sys / swap)
βββββββββββββββββββββββββββββ
Virtual Memory doesn't "sit" anywhere - it's just a numbering system.
The actual data sits in RAM or on disk, and the OS decides where.
Advantages:
- Isolation: One process can't access another's memory
- Simplicity: Each process "thinks" it has all the memory
- Protection: Different regions with different permissions (R/W/X)
In RE, you always see Virtual Addresses.
You don't need to know the physical addresses - the OS handles the translation automatically.
π Chapter Summary
- Stack - local variables, grows downward, LIFO
- Heap - dynamic allocations, grows upward
- ESP - points to top of Stack
- EBP - points to base of Stack Frame
- Stack Frame - function's region (parameters + return address + variables)
- Virtual Memory - each process gets its own address space