LAB-18Medium
Call Stack Reconstruction
Reconstruct the stack frame of a simple function from its prologue and epilogue.
15 min5 prerequisites5 tools
Disassembly
00: push rbp
01: mov rbp, rsp
02: sub rsp, 0x10
03: mov rax, 0x41
04: mov rbx, 0x42
05: mov rsp, rbp
06: pop rbp
07: ret Hints
Hint 1
The prologue is push rbp / mov rbp, rsp / sub rsp, N.
Hint 2
0x10 in decimal is 16.
Hint 3
RBP points at the saved caller's frame pointer; RIP is restored by ret.
Submit Findings
Solution
Call Stack Reconstruction — Solution
The function follows the standard frame layout:
- push rbp — saves the caller's frame pointer (the value RBP held before this function).
- mov rbp, rsp — RBP becomes this function's frame pointer.
- sub rsp, 0x10 — reserves 16 bytes (0x10) for locals.
- Body uses [rbp-...] for locals and [rbp+...] for args/saved values.
- mov rsp, rbp / pop rbp — restores RSP then the caller's RBP.
- ret — pops the return address (pushed by CALL) into RIP.
Answers: 1) caller's frame pointer 2) 16 3) RBP 4) return address.
Prerequisites
- a4-stack-functions
- a1-registers-movement
- x64 calling convention
- Stack frame layout (prologue/epilogue)
- Shadow space / home space
Methodology
Investigation Methodology
1. Standard Prologue (Frame Setup)
push rbp ; Save caller's RBP (8 bytes)
mov rbp, rsp ; RBP = current RSP (frame base)
sub rsp, N ; Reserve N bytes for locals
2. Stack Layout After Prologue
Higher addresses
+------------------+ <-- RSP (after sub)
| Local variables | (N bytes)
| ... |
+------------------+ <-- RBP (frame pointer)
| Saved RBP | (8 bytes) ← old RBP
+------------------+ <-- RBP + 8
| Return address | (8 bytes) ← pushed by CALL
+------------------+ <-- RBP + 16
| Home space | (32 bytes = 4x8) for RCX,RDX,R8,R9
+------------------+ <-- RBP + 48
| Arguments 5+ | (on stack, if >4 args)
Lower addresses
3. Frame Pointer (RBP) vs Stack Pointer (RSP)
- RBP: Fixed reference point for this frame
- RSP: Moves with pushes/pops/allocations
- Locals at
[rbp - offset], Args at[rbp + offset]
4. Standard Epilogue (Frame Teardown)
mov rsp, rbp ; RSP = RBP (deallocate locals)
pop rbp ; Restore caller's RBP
ret ; Pop return address → RIP
5. Shadow Space / Home Space (Win64)
- Caller allocates 32 bytes (4 slots) before call
- Callee may use it to spill register args
- Not part of callee's
sub rsp, N
6. Frame Pointer Omission (FPO) / Optimization
- Compiler may omit
push rbp/mov rbp, rsp - RBP used as general register
- Locals addressed relative to RSP
- Debug info (PDB) or heuristics needed to reconstruct
Common Pitfalls
- Confusing saved RBP (at [RBP]) with return address (at [RBP+8])
- Forgetting the 32-byte shadow space (home space) in Win64
- Assuming RBP is always frame pointer — FPO breaks this
- Misreading `sub rsp, 0x10` as 10 decimal (it's 16)
- Not accounting for stack alignment (16-byte boundary before call)
Tools
- Ghidra (Function Graph, Stack view)
- Binary Ninja (Stack layout view)
- IDA Pro (Frame Pointers, Stack view)
- x64dbg (Registers + Stack view)
- WinDbg (kv - stack trace with frame info)
References
- Lesson A4: Stack & Functions
- Microsoft x64 Calling Convention
- Agner Fog: Calling Conventions
- Ghidra Stack Frame Analysis
Extensions & Challenges
- Analyze a function with FPO (no RBP frame) — reconstruct from RSP
- Trace a function with 6+ arguments — find args on stack
- Research: How does `__fastcall` / vectorcall differ?
- Practice: Dump stack in x64dbg at function entry, map each slot