What is a stack frame?
It is a data structure that contains information about a function call. It contains:
- The return address. When the function call completes it knows the location to return to.
- Parameters passed to the function.
- Local variables of the function.
- Saved registers.
Main Function Instructions
The debugger view instructions are shown below. We are currently in the Main Function. Don't worry about the OPCODES in the center, as they are not accurate. Our goal is to focus on understanding Stack Frames and the instructions.
Function Call Instructions
Here is the view of the function call instructions that will be executed when we step into the call.
Main Function Instructions
Now, let's head straight to the instructions for the Main Function. The prologue setup, as seen in the first two instructions above, is complete. Its purpose is to set up the stack frame and prepare the environment for the function's execution.
Why Is the Prologue Important?
The prologue ensures that:
- The function has a stable and isolated stack frame.
- Parameters and local variables can be easily accessed through a consistent reference point (EBP), even if the ESP changes.
Simplified Workflow:
1. Save the caller's stack frame (push ebp).
2. Establish a new stack frame for this function (mov ebp, esp).
3. Continue with function-specific operations.
After the function is done, the epilogue will reverse these steps, restoring the previous environment before returning to the caller.
As you can see below, after setting up the stack frame, we are now at the current instruction pointer (EIP).
Before stepping over the instruction, letβs examine the stack and the registers. Currently 0064FED8 is EBP and EBP. Remember! We have setup the stack frame by pushing EBP at the top, and ESP will always point at the top of the stack.
Have a look at the registers.
Before stepping into the Function Call, take note of the address after the Function Call: 0040153C.
When we step into the Function Call, the next instruction is the return address 0040153C that gets pushed at the top of the stack within the Function Call. We need to set up the current EBP using the same prologue as we did in the Main Function Call. This ensures that both ESP and EBP point to the top of the stack. From here, we can proceed to push local variables. By doing so, EBP becomes sandwiched between the arguments and the local variables. This structure allows us to always reference data relative to EBP, as ESP continues to change at the top of the stack.
| Memory Address | Content |
|---|---|
| EBP - 8 | Local Var2 |
| EBP - 4 | Local Var1 |
| EBP | Callee Function |
| EBP + 4 | Return Addr |
| EBP + 8 | ARG1 |
| EBP + 12 | ARG2 |
| EBP | Main Function |