Background reading for a first Ghidra session
What to know before you open Ghidra: enough x86-64 to read function prologues, what decompilation actually produces, and the rename-retype-comment loop that makes a binary readable.
Open Ghidra against a stripped binary and you'll see something like FUN_004012a0 calling FUN_004013e0, both stuffed with local_18, param_1, and casts to undefined4. It's not noise — every one of those names is meaningful — but the meaning only lands if you've done two things first. Read enough x86-64 to recognise a function prologue, and understood what a decompiler can and cannot do.
Half an hour of background reading is the difference between a frustrating first session and a productive one.
Assembly familiarity, the floor
You don't need to write x86-64. You need to read it well enough to follow control flow when the decompiler is confused.
The minimum: recognise push rbp; mov rbp, rsp; sub rsp, 0x20 as a function prologue. Read the System V calling convention well enough to know that rdi, rsi, rdx, rcx, r8, r9 carry the first six integer arguments and rax holds the return value. Recognise call, ret, jmp, je, jne. Understand that lea rax, [rbp-0x10] computes an address rather than dereferencing one.
That's roughly two evenings of reading and a few hours of practice. Anything past this — SIMD, complicated addressing modes, the floating-point ABI — you can pick up on demand.
What "decompilation" means
This part trips up most beginners. Ghidra's decompiler does not recover source. It synthesises C-like pseudocode that has the same observable behaviour as the assembly. That means:
- Variable names are gone. The decompiler invents
local_18from a stack offset. Your job is to rename it. - Types are guessed. A function returning
intlooks identical to one returninglongif the high half ofraxis dead. Ghidra picks something — oftenundefined4— and you correct it. - Inlined functions don't come back. If the compiler turned a
strlencall into a SIMD loop, the decompiler shows you the SIMD loop, notstrlen. - Control structures may look weird. The decompiler reconstructs
for/while/iffrom basic blocks, but unusual code (jump tables,setjmp, computed gotos) can produce pseudocode that's technically right and humanly cryptic.
The pseudocode is a working hypothesis, not ground truth. When something looks wrong, the assembly is the source of truth.
How the IR fits in
Under the hood Ghidra translates each instruction to its own intermediate representation called P-code. Every machine operation maps to a small set of P-code ops with explicit inputs and outputs. The decompiler then runs analysis passes over P-code — dead-code elimination, constant propagation, type recovery, structuring — before printing pseudocode.
You almost never need to read P-code directly. What matters is understanding why this design exists: it lets the same decompiler frontend handle x86, ARM, MIPS, RISC-V, even ROM cartridge formats. When Ghidra gets something subtly wrong, it's usually because a P-code pass couldn't prove a property it needed.
The actual workflow
The iterative loop is the whole game. You do not analyse a binary in one pass; you nudge Ghidra's understanding forward, one rename at a time.
Start at a recognisable string or imported function — an error message, a printf call, a syscall wrapper. From there, walk outward. Rename param_1 to request, retype undefined4 to int32_t or whatever the pattern of usage demands, then write a one-line comment summarising what you've worked out. Move on. After an hour you'll have a few dozen named functions and a sense of what the program does.
Don't try to perfect a function on your first visit. You'll often work out what FUN_00401500 does only after understanding two of its callers.
Pick the right binary to start on
Beginners often pick something they're curious about — a game, a piece of malware, a closed-source utility. Bad idea for the first hour. You want something you've already written. Compile a small C program with -O0 -g, then strip the symbols, then open it in Ghidra. You already know what it does; the exercise is mapping the decompiler's output back onto code you wrote.
Once you've done that, drop to -O2, recompile, and watch what the optimiser did. The same code becomes nearly unrecognisable — loops unroll, arithmetic gets rewritten, dead branches vanish. That gap between the source you wrote and the assembly you see is what reverse engineering is.
After three or four practice binaries you'll be ready for something you didn't write. A small command-line utility from a CTF challenge is a good next target.
Where this fits in
Module 68 (Binary Analysis with Ghidra) walks through exactly this loop on real binaries, including how to handle stripped C++, how to read Ghidra's auto-analysis output skeptically, and how to write small Ghidra scripts when the GUI gets in the way.