Back

STACK FRAMES - x64dbg (!NOT COMPLETE!)


Click the flickering 𝕏 logo to view my profile.
PJPT Certification

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.

00401530 55 push ebp
00401531 89 E5 mov ebp, esp
00401533 6A 05 push 5
00401535 6A 03 push 3
00401537 E8 C9 call 74-stack-frame.402705
0040153C 83C4 add esp, 8

                       Function Call Instructions
Here is the view of the function call instructions that will be executed when we step into the call.

00402705 55 push ebp
00402706 89 E5 mov ebp, esp
00402708 83 EC 04 sub esp, 4
0040270B 8B 45 08 mov eax, dword ptr ss:[ebp+8]
0040270E 03 45 0C add eax, dword ptr ss:[ebp+C]
00402711 89 45 FC mov dword ptr ss:[ebp-4], eax
00402714 8B 45 mov eax, dword ptr ss:[ebp-4]
00402717 89 EC mov esp, ebp
00402719 5D pop ebp
0040271A C3 ret

                       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).

EIP β†’
00401533 6A 05 push 5
00401535 6A 03 push 3
00401537 E8 C9110000 call 74-stack-frame.402705
0040153C 83 C4 08 add esp, 8

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.

0064FED8 0064FF68
0064FEDC 00401386 return to 74-stack-frame.402705

Have a look at the registers.

EBP 0064FED8
ESP 0064FED8

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.

EIP β†’
00401537 E8 C9110000 call 74-stack-frame.402705
0040153C 83 C4 08 add esp, 8

Lower Memory Higher Memory ESP EBP
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