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

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