When to Use / When Not to Use
- Use: when decompilation and deep static analysis are needed; headless batch processing; scripted auto-annotation/decryption loops; full PE/ELF/Mach-O support
- Don't use: memory <4GB (lags immediately on startup, switch to [[re-radare2]]); when only a quick initial survey is needed (go to [[re-triage]])
- Don't use: when an IDA workflow already exists and the target is clear (choose by
RE_DECOMPILERsession variable)
Tool Preparation
Refer to [[re-analyze/platform-tips]]—Ghidra is the mainstay for static analysis (sandbox-free); in low-memory environments switch to [[re-radare2]] per platform experience; headless batch processing suits remote environments.
Ghidra (official installation, Java 21 required)
- Download: GitHub
NationalSecurityAgency/ghidrareleases, extract and use (no installation needed) - JDK 21 (required by Ghidra 11.3+):
- Linux:
apt install openjdk-21-jdk/dnf install java-21-openjdk/pacman -S jdk21-openjdk - macOS:
brew install openjdk@21orbrew install --cask temurin - Windows:
choco install temurin21(or Oracle JDK 21)
- Linux:
- Launch:
./ghidraRun(GUI)/./support/analyzeHeadless(headless) - Verify:
java -version(must be 21+);./support/analyzeHeadless -helpoutputs normally - Version: 12.x is the current mainline (verified baseline 12.1.4); at install time, defer to the Latest on the official Releases; running the official release only requires JDK 21, while building Ghidra itself from source requires JDK 25 + Gradle 9.1+; 11.x → 12.x has breaking script API changes, so check the target version's javadoc before writing scripts, see [[gotchas]] for details
ghidra-bridge (Python remote control)
- All platforms:
pip install ghidra-bridge - Prerequisite: In the Ghidra GUI, run the Script Manager
ghidra_bridge_server.py - Verify:
python3 -c "import ghidra_bridge; print(ghidra_bridge.__version__)"
Procedure
-
analyzeHeadless headless import + analysis:
$GHIDRA/support/analyzeHeadless /tmp/proj sample_proj \ -import ./sample.bin -deleteProject -log /tmp/ghidra.logAdd when only creating a project without interaction
-overwrite; for batch processing multiple samples, invoke in a loop. Check the log at-log, analysis completion indicator: no exceptions and the project can be re-imported.-processmode (rerun scripts/analysis on files already imported into the project, choose one of two with-import):$GHIDRA/support/analyzeHeadless /tmp/proj sample_proj \ -process -postScript MyScript.java -scriptPath /tmp/scripts- Add when only doing symbols/strings/single-function decompilation
-noanalysis(skips auto-analysis, completes in seconds); add for read-only review-readOnly; for the full parameter family see [[commands]]
-
GUI: navigation/cross-references/decompilation:
File > Import File→ select sample →Analyze(wait for the progress in the lower-left corner to complete)- Jump:
Go To(G) enter address; the entry point is already marked in Program Treesentry - Cross-references: with the cursor on a function/variable press
R(References window), or right-click > References > Show References To - Decompile: in the Listing press
Ctrl+E(Decompiler window, the default shortcut may differ by version/keymap, defer to Help > Key Bindings) or right-click > Decompile Function, the decompiled view can be copied - Function graph: on a function press
F(Function Graph) to see branch/loop structure, with synchronized navigation with the Decompiler - Underlined address difference: the Listing shows VA (including ImageBase), in scripts often use
getAddressFactory().getDefaultAddressSpace().getAddress("0x401000")
-
Rename + type propagation annotation:
- Function rename: in the Listing
L; variable rename: in the DecompilerL - Set type: right-click variable > Set Data Type (
y), e.g.char *、DWORD、int (*)(int) - Define a struct: Data Type Manager > create a new Structure; after it is referenced in the Decompiler, type propagation automatically improves decompilation quality
- Mark known library functions: right-click > Set Data Type +
f(Function signature), improving parameter semantics at subsequent call sites
- Function rename: in the Listing
-
Python scripts (automatic annotation, decryption loop emulation):
# @category Analysis — 放 GhidraScripts 目录,Script Manager 中 Run from ghidra.program.model.listing import CodeUnit fm = currentProgram.getFunctionManager() for f in fm.getFunctions(True): body = f.getBody() if body.getNumAddresses() > 10000: print("large:", f.getName(), hex(body.getMaxAddress().getOffset()))Decryption loop: after locating the XOR loop, either patch by script or compute directly:
from ghidra.program.model.mem import * fm = currentProgram.getFunctionManager() f = fm.getFunctionAt(currentAddress) # 示例: 从 0x403000 起 0x100 字节异或 0x55 addr = currentProgram.getAddressFactory().getDefaultAddressSpace().getAddress("0x403000") for i in range(0x100): b = getByte(addr.add(i)) ^ 0x55 setByte(addr.add(i), b)- Make a copy before changing bytes:
setBytewrites directly to the current Program (saving modifies the original file) — when analyzing malicious samples you must first copy the Program (File > Save Assave a copy as) or operate on a temporary copy; never patch the original sample directly, otherwise you destroy the chain of evidence Headless batch:analyzeHeadless ... -postScript MyScript.java -scriptPath <dir>
- Make a copy before changing bytes:
-
Export decompiled C:
File > Export Program→ choose formatC/C++, exporting the entire decompilation tree- or in the Decompiler window, per function
Copyto notes - What is exported is the decompilation result, not recovered original source code: variable names/type inference may be wrong (they improve only after renaming), and do not represent the original implementation — use it only as a reading aid; key logic must be verified against the disassembly/Listing
- After export, verify: unsigned function names in the C retain comments (function addresses); compare against the original Listing
Function analysis context checklist
Before analyzing each function, first collect (see the context checklist in [[re-analyze/analysis-contract]]): xrefs (who references it/who it references), strings referenced by the target function, caller/callee signatures, the named symbol table, and recovered structs. Provide everything at once before analyzing to avoid repeatedly digging through xrefs; proactively request additional evidence with no more than 8 tool calls per function.
Single-function deep analysis order
Proceed through the five steps of the “Single-function deep analysis order” in [[re-analyze/analysis-contract]] (types → constants → vtables → identity → decompilation; do not skip steps, and do not decompile first and work backward). Corresponding operations in Ghidra: for types use Type Manager / Apply Function Signature, for vtables use vtable analysis, and for symbol/constant evidence you can use readelf / strings / objdump exports as aids.
Cross-domain combination
-
[[re-address-space]]: converting between Ghidra base addresses and runtime addresses (load bias)
-
[[re-binary-core]]: the default decompiler for step 5 of the workflow
-
[[re-malware]]: deep analysis of malicious samples (obfuscated script annotation)
-
[[re-ctf]]: the main tool for reverse engineering challenges
-
[[re-firmware]]: decompiling firmware ELF components
-
[[re-mobile]]: analysis of App .so libraries
-
Works with [[re-format-pe]] / [[re-format-elf]] / [[re-format-macho]] to read structures; in low-memory environments use [[re-radare2]]
Common pitfalls and traps
-
Automatic analysis misses obfuscated code: junk instructions/misordered code sections may not be disassembled — manually select the section and press
C(code) to force marking, and if necessary fix function boundaries (right-click > Create Function) -
Startup fails due to Java version mismatch: if it reports
UnsupportedClassVersionError→ confirmjava -versionis 21+, andJAVA_HOMEpoints to the correct JDK -
Lag when memory is <4GB: analyzing a large binary exhausts memory → switch to [[re-radare2]] or reduce the analysis scope (
-analysisTimeoutPerFile) -
Headless default analysis options differ from the GUI (some optional items are missing) → use
-postScriptto explicitly execute the analysis script and ensure consistency -
Automatic analysis of large files hangs: Symptom — after importing a large binary, automatic analysis does not finish for a long time / the interface hangs; Cause — the triggers for large-file hangs = indirect call explosion / large C++ RTTI / obfuscated control flow / large data segments; Countermeasure — first reduce the scope of automatic analysis (limit/turn off indirect call and RTTI analysis options), prioritize locating entry points/strings/cross-references, and then gradually expand, see [[re-analyze/platform-tips]] static-first principle
-
Abnormal function boundaries → check for decompilation illusions: Symptom — in the decompiled view, functions are incorrectly split/merged, fake parameters or stack variables are disordered, the function body looks like junk data, and analyzing accordingly yields absurd logic; Cause — handwritten assembly, obfuscated code, tail calls (tail call causes adjacent functions to be incorrectly merged), and no frame pointer (not using rbp/ebp distorts frame inference) cause the decompiler's boundary and frame inference to err; Countermeasure — first check the Listing assembly against the real bytes, and follow
C/Create Function to manually correct boundaries; for functions with tail calls and no frame pointer, take the assembly semantics as authoritative and do not fully trust the decompiler output (see [[re-binary-core]] analysis methodology R4) -
The built-in script interpreter is Jython 2.7, not Python 3: Symptom — the script reports
SyntaxErroras soon as it runs (f-string/walrus operator) orImportError(cannot find third-party libraries installed by pip); Cause — Ghidra's built-in Jython 2.7 uses Python 2 syntax and standard library, does not support Python 3 syntax, and cannot import packages installed by pip; Countermeasure — write scripts with Python 2 compatible syntax (do not use f-strings; usexrange), and when modern Python capabilities (numpy, etc.) are needed, switch to ghidra-bridge to let an external Python 3 interpreter drive the analysis (see tool preparation) -
Improper script/extension selection → runaway automation maintenance costs: Symptom — one-off batch processing logic becomes more and more complex, manual triggering each time is annoying, or persistent automation is needed but the script is repeatedly clicked manually; Cause — the boundary between Script (script: lightweight single file, manual trigger, suitable for one-off/simple repetitive tasks) and Extension (extension: can register menus/panels/event listeners, automatically execute when the project loads, ZIP packaged for team distribution) is not clearly distinguished; Countermeasure — use scripts for simple repetitive tasks, ready to use and discard after use; only build an extension when UI integration, external service access, automatic execution on load, or standardized team distribution is needed
-
Script API is incompatible across versions: Symptom — headless script compilation fails (skipping and reporting that
getPermissions()/getExecute()class methods do not exist); Cause — APIs such as MemoryBlock change between Ghidra versions (12.x isisExecute(), and method names differ across versions); Countermeasure — before writing a script, first check the current version's javadoc (with a small verification script); when compilation fails, suspect API differences first rather than business logic -
If full analysis is too slow, use -noanalysis: when only doing symbol table/strings/single-function decompilation,
analyzeHeadless -noanalysisafter import the script can be used directly — symbol (including JNI exports) and string scanning do not depend on auto-analysis and complete in seconds -
For a command quick reference (headless parameter family / GUI shortcut family / script API family), see [[commands]]; for more pitfalls with version differences, headless batch processing, and script APIs, see [[gotchas]]