packages/omo-codex/plugin/skills/debugging/references/tools/ghidra.md
https://github.com/NationalSecurityAgency/ghidra
Ghidra is the NSA's open-source reverse-engineering suite. Its defining feature is a decompiler that turns machine code back into readable C. For any binary you don't have source for, this is the correct starting point — not strings, not hex-staring, not objdump -d.
Use Ghidra when: third-party closed-source libs, malware analysis, vendored binaries whose behavior contradicts docs, CTF challenges, firmware, any time you need to read compiled code.
# macOS
brew install --cask ghidra
# OR download the release ZIP from the repo and ./ghidraRun
# Linux
# Download from https://github.com/NationalSecurityAgency/ghidra/releases
# Requires JDK 21+
./ghidraRun
# Dependency
java -version # must be 21+
Ghidra is a Java Swing app. Looks dated, works well.
ghidraRundebug-<binary-name> (journal this path so you can rm it at Phase 9 if disposable).Two panels you'll use 95% of the time:
Don't try to read the whole binary. Use these to narrow:
Functions — all detected functions. Stripped binaries show FUN_00401234 (address-named); unstripped show actual names.Imports — dynamically-linked functions. Great for "does this binary call system(), strcpy, curl_easy_perform?"Exports — if it's a library.Click to jump. The Decompiler updates instantly.
Search → For Strings
Produces a list of all strings. Right-click a string → References → Show References to Address. Jumps to code that references it. This is how you find which function handles the error message you saw at runtime.
Search → Memory
Search hex or text. Useful for known magic bytes, file-format signatures, constants.
Right-click any function / address → References → Find References to. Shows every place that calls it. Walk the call graph backward from interesting functions.
Ghidra's decompiler is good but needs hints. These three actions dramatically improve its output:
Click a variable in the Decompiler view → press L → type a better name. Ghidra propagates the rename across all uses.
A variable that looks like undefined4 or void * is unhelpful. Click it → press Ctrl+L → set type (e.g. int, char *, struct my_header *).
For pointers to structs from headers you have, use:
File → Parse C Source → paste header file → auto-creates struct types
Then assign the struct type to the pointer. Ghidra resolves field accesses immediately.
Click the function name in the Decompiler → press F (Edit Function Signature) → set return type + argument types. This propagates through callers.
Do these three actions on the 3-5 most relevant functions and the decompiler output becomes near-source-readable.
Listing: look for
- LEA → CMP patterns on sizes
- memcpy/strcpy/sprintf with non-constant sizes
Decompiler: look for
- arithmetic on size_t without bounds check
- `+ user_input` in a length calculation
Navigate from the handler entry (found via Strings or Imports) and check the control flow:
Decompiler: does the function return early / jump to error handler when some flag is not set?
If the check is absent, that's the bug.
Search → For Strings → filter `http:` / `https:` / `/etc/` / `bearer ` / `api_key`
Imports → dlopen, LoadLibrary, dlsym, GetProcAddress
The strings referenced near those calls are often plugin names.
When you need to automate analysis across many binaries, or repeat a workflow:
# Headless analyzer
$GHIDRA_INSTALL_DIR/support/analyzeHeadless \
<project-dir> <project-name> \
-import <binary> \
-postScript <script.py or script.java>
Ghidra supports Python 3 scripts (via Jython-compatible API) and Java. Useful scripts:
system() with constant argumentsThe community has a large collection: https://github.com/NationalSecurityAgency/ghidra/tree/master/Ghidra/Features/Base/ghidra_scripts
Ghidra has built-in bookmarks and comments. Use them as your journal inside the project:
Set Bookmark → tag as Note. Attach a description.Comments → Set EOL Comment (shows up inline in decompiler).Treat these as part of the journal. If you end up promoting this Ghidra project (keeping it after the debug session), the comments become durable documentation.
support/launch.properties and bump VMARGS=-Xmx8G (default is often 2G, too small)./* WARNING: ... */. Increase timeout in Edit → Tool Options → Decompiler.-g and use gdb/pwndbg (see pwndbg.md).ldd / otool -L / readelf -d are faster.strings -n 8 is faster.Ghidra is the right tool when you need to read the logic of a binary you don't have source for.
# If you created a scratch project just for this debug session, journal path and remove:
# (the journal should have the exact path)
# Example:
ls ~/ghidra-projects/ 2>/dev/null
# rm -rf ~/ghidra-projects/debug-<binary-name>
# If you promoted the project (kept it), it's not cleanup — note it in the final summary to the user
# Kill any running Ghidra headless processes
pkill -f 'analyzeHeadless' || true