Lesson 4 of 9 · 22m
A First Session in Ghidra
Ghidra is free, scriptable, and what most pros reach for first. The learning curve is the interface, not the analysis — a dozen habits carry a whole investigation.
Ghidra won the tooling war by being free, scriptable, and within a few percent of the five-figure alternative on real work. The learning curve is the interface, not the analysis. A working session has a rhythm — this is the one I teach new hires on day one.
Import and analyze
File → Import File, accept the detected format, run auto-analysis. The defaults are right for ordinary ELF and PE. Two options worth knowing: Decompiler Parameter ID (better argument recovery in pseudocode) and the Function ID analyzer for static binaries (next lesson). One thing to verify before spending an hour: the detected architecture. If Ghidra guessed ARM and file says MIPS, everything downstream is confident garbage.
The three views that matter
- Listing — the disassembly. Navigation lives here:
Gjumps to an address, double-click any label or function name to follow it, Alt+Left walks you back. - Decompiler — synchronized to the cursor. Click a line in the listing and the pseudocode for that region appears. You'll spend most of your time here — keep the listing open anyway.
- Symbol Tree → Functions — every function Ghidra found:
entry,FUN_00001139, and occasionally real names it recovered.
Finding main
In a dynamically-linked ELF, entry is _start — C runtime plumbing, not your code. It calls __libc_start_main, and the first argument is main:
lea rdi, [FUN_00001160] ; <- this is main, passed to libc
call __libc_start_mainDouble-click into FUN_00001160, read the decompilation, rename it main (L on the name). On Windows the same pattern applies: follow entry down through CRT initialization until you hit the first function doing real work.
Rename and retype relentlessly
L renames, Ctrl+L retypes. This is not cosmetics — it is the analysis. FUN_00001139 renamed to parse_header makes every call site self-documenting; a parameter retyped from undefined8 to char * cleans up every dereference of it at once. Name by hypothesis — maybe_check_license is a perfectly good name — and rename when the hypothesis dies.
Script the boring parts
Batch triage across fifty samples doesn't happen in the GUI. The headless analyzer ships with every install:
$ ./support/analyzeHeadless ~/ghidra-projects triage \
-import ./samples/*.elf \
-scriptPath ./scripts -postScript DumpFunctions.java \
-deleteProjectSame analysis, no window, your own script (here a twenty-line DumpFunctions.java that writes every function name and size to CSV) runs after each import. The Script Manager (Window → Script Manager) has hundreds of bundled examples to crib from.
Field notes
- Ghidra projects are directories on disk. Save early; an unsaved session plus a JVM crash is a painful combination.
- The decompiler and the listing will occasionally disagree. The listing is the program; the decompiler is a translation (lesson 6 covers when it misleads).
- Defined Strings (
Search → For Stringsor the window) is usually the real front door — the anchor loop in the next lesson starts there, not atentry.