Wiki / Software

Ghidra

NSA's open-source reverse-engineering suite. Disassembles and decompiles firmware for almost any CPU into readable C-like pseudocode.

Ghidra is the NSA's open-source software reverse-engineering (SRE) suite: a multi-architecture disassembler and decompiler that turns raw firmware into readable C-like pseudocode.

What it is

Ghidra loads a binary, auto-analyses it, and produces cross-references, function graphs, a symbol tree, and a decompiler view side by side with the raw assembly. Its decisive strength for hardware work is breadth of processor support: ARM, MIPS, AVR, PowerPC, RISC-V, and crucially Xtensa (the ESP32 core) and SuperH, so it handles router, PLC, camera and ESP firmware that IDA needs paid plugins for. It is free, runs anywhere Java runs, and is scriptable in Java or Python.

Why it matters

Once binwalk has carved a firmware image, the interesting logic sits in stripped binaries: a CGI handler that builds a shell command from an HTTP parameter, a bootloader that checks a signature, a daemon that derives a device key. Ghidra's decompiler reconstructs that logic well enough to spot the classic bug classes, command injection, unchecked strcpy, a backdoor account, a weak key-derivation, without hand-reading thousands of instructions. For a raw MCU dump with no operating system, it is often the only way to understand what the chip actually does.

Typical workflow

# 1. Create a project, then import the dump
File > Import File > firmware.bin

# 2. Pick the language (processor:endian:size:variant), e.g.
#    ARM:LE:32:Cortex   MIPS:BE:32:default   Xtensa:LE:32:default
#    and set the load / base address for a raw (headerless) dump.

# 3. Run Auto Analysis (Analysis > Auto Analyze), then work the code:
#    - Symbol Tree + Defined Strings to find entry points and URLs/keys
#    - double-click into the Decompiler, rename vars as you understand them
#    - 'X' shows cross-references to a function or string

For batch work there is a headless runner:

analyzeHeadless ~/projects MyProj -import firmware.bin \
  -processor ARM:LE:32:Cortex -postScript export_strings.py

Tips

  • For a raw flash dump with no header, the base address is everything. Set it wrong and every absolute pointer, string reference and jump table resolves to garbage. Recover it from the vector table, the bootloader's link map, or by aligning string references until they land on real strings.
  • The Defined Strings window (Window > Defined Strings) is the fastest first pass: URLs, format strings, AT commands, error messages and key material jump out, and each one cross-references straight to the code that uses it.
  • Keep the decompiler and the listing open together and rename as you go. A function full of iVar3/DAT_0800abcd becomes legible once you name the three variables that matter.
  • The decompiler is a model, not ground truth. When it looks wrong, especially around calling conventions or the stack, drop to the listing and trust the assembly.

Pitfalls

  • Ghidra is not an unpacker. Feed it the extracted, decompressed binary from binwalk, not the raw compressed firmware blob, or it will analyse noise.
  • Auto-analysis on a large image is slow and memory-hungry; give the JVM more heap and be patient on first import.
  • A wrong processor variant (Thumb vs ARM, MIPS16, wrong endianness) yields plausible-looking but false decompilation. Confirm the ISA before you trust the output.

What it pairs with

Ghidra sits at the reversing stage. binwalk feeds it the carved binaries; radare2/Cutter is the lighter, scriptable alternative for quick triage and in-place patching; and a jtag-swd-probe with OpenOCD lets you confirm a static hypothesis by breakpointing the real chip. The static read and the live debug reinforce each other.

Further reading