Programs require memory to store data while they execute. Two of the most important memory regions are the Stack and the Heap. There are other regions of memory as well, but we will dive deeper into them later when we start working with malware. For now, let's focus on the basic primitives:
Process Memory
+------------------+
| Stack |
+------------------+
...
+------------------+
| Heap |
+------------------+
...
+-------------------+
| Other regions, |
| e.g. VirtualAlloc |
+-------------------+
Stack
- Automatic allocation
- Managed automatically by function calls
- Typically smaller
- Very fast
- Lifetime is tied to scope/function execution
Heap
- Dynamic allocation
- Managed explicitly by the program/runtime
- Typically much larger
- Slightly slower
- Lifetime is determined by the application
For example:
void Example()
{
int a = 10; // Stack
int* b = malloc(sizeof(int)); // Heap
}
When Example() returns:
a -> automatically disappears
b -> remains allocated until freed
That's the real distinction.
The Stack
The stack is used for temporary data associated with function execution.
Whenever a function is called, the program uses the stack to store call-related information such as:
- Local variables (which live only inside this function, not globally)
- Some function parameters, depending on architecture, calling convention and compiler behavior
- Return addresses
- Saved register values
On x64 systems, several early function arguments are commonly passed in registers first. The stack still remains central to function calls because it stores return addresses, local variables, saved state and any values that must be spilled or passed on the stack.
For example:
void Example()
{
int age = 28;
}
The variable age is typically stored on the stack.
One useful way to think about the stack is as a pile of plates:
Push Plate
▼
+-----+
| C |
+-----+
| B |
+-----+
| A |
+-----+
Pop Plate
▲
The last item added is the first item removed. This behavior is known as LIFO (Last In, First Out).
When a function finishes executing, its stack space is usually reclaimed automatically.
Because of its predictable layout and close relationship with function calls, the stack plays a central role in debugging, reverse engineering and many classic software vulnerabilities.
The Heap
The heap is used for data that must live longer than a single function call or whose size is not known in advance.
Unlike the stack, memory on the heap is typically allocated and released explicitly by the program.
For example:
int* numbers = malloc(100 * sizeof(int));
Here, memory is allocated from the heap rather than the stack.
Conceptually:
Heap
+---------+
| Object |
+---------+
Gap
+---------+
| Object |
+---------+
+---------+
| Object |
+---------+
Unlike the stack, heap memory is not necessarily arranged in a neat sequential manner. Objects may be created, resized and destroyed throughout the lifetime of the program.
The heap is commonly used for:
- Large data structures
- User-created objects
- Dynamic arrays
- Long-lived application data
- Buffers whose size is unknown at compile time
As software grows more complex, heap usage typically becomes far more significant than stack usage.