Skip to content

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: G jumps 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_main

Double-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 \
    -deleteProject

Same 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 Strings or the window) is usually the real front door — the anchor loop in the next lesson starts there, not at entry.