| name | ghidra-rpc |
| description | Reverse engineering assistant powered by Ghidra. Use for binary analysis, decompilation, vulnerability research, auditing compiled code, renaming symbols, annotating disassembly, cross-references, or any RE task - even without explicit mention of Ghidra.
|
Ghidra RPC
A CLI daemon that exposes Ghidra reverse engineering capabilities over a Unix domain socket.
You interact with it by running ghidra-rpc commands via the bash tool - every command
outputs JSON to stdout.
Running Commands
All ghidra-rpc commands must be run with uv run from the ghidra-rpc skill directory
(the directory containing this SKILL.md file):
cd <skill-dir> && uv run ghidra-rpc <command> ...
Prerequisites Check
Before using any commands, verify the setup:
- Is the daemon running? Run
uv run ghidra-rpc status --project <path>. If not running,
the user needs to start it in a separate terminal: uv run ghidra-rpc start --project /path/to/project.gpr
(blocking, human-only). The --headless flag skips the GUI. For non-blocking / automated
startup use start --detach --headless or restart --headless. Both commands accept
--timeout SECS (default: 60 s headless, 180 s GUI) and log daemon output to
/tmp/ghidra-rpc-<hash>.log.
- GUI mode: verify the correct project is active. The daemon passes the
--project path
directly to GhidraRun so Ghidra opens the requested project immediately. After
start --detach (GUI mode), confirm the right project is loaded before issuing any
analysis commands:
uv run ghidra-rpc list-project-programs --project /path/to/project.gpr
Cross-check the returned binary names against what you expect. If the names don't match
(or a warning field appears in the response), stop and ask the user to open the correct
project in Ghidra's Project window before proceeding.
- GUI mode: open the binary in CodeBrowser. In GUI mode the daemon discovers programs
via the running CodeBrowser tool. If the binary is in the project but not currently open
in CodeBrowser,
list-binaries will return empty and all commands will fail with
"Binary not found". Use list-project-programs to see what's stored in the project,
then double-click the program in Ghidra's Project window to open it.
- Is
GHIDRA_INSTALL_DIR set? The daemon will fail loudly if this is missing.
Once the daemon is running, all commands below work automatically. If the daemon dies,
commands will attempt auto-restart from the saved session.
Command Reference
Every command accepts --project <path> (or reads GHIDRA_RPC_PROJECT env var).
All output is JSON. Exit code 0 = success, 1 = error.
Project Management
| Command | Description | Example |
|---|
ghidra-rpc start --project <gpr> [--headless] [--detach] [--ghidra-install-dir DIR] | Start daemon (blocking by default; --detach backgrounds it) | ghidra-rpc start -p /tmp/re.gpr --headless --detach |
ghidra-rpc stop --project <gpr> | Stop the daemon | ghidra-rpc stop -p /tmp/re.gpr |
ghidra-rpc status --project <gpr> | Check if daemon is running | ghidra-rpc status -p /tmp/re.gpr |
status output: {running, socket, mode, mode_source, project, binaries}. binaries is the loaded-binary list when the daemon is running, null when stopped (one-shot health check without a second list-binaries call). mode_source is
"running" when the daemon is live, "session" when the mode is from the saved
session only (daemon stopped), and null if no session exists.
| ghidra-rpc restart --project <gpr> [--headless] [--timeout SECS] [--ghidra-install-dir DIR] | Restart daemon in background (--headless to override mode) | ghidra-rpc restart -p /tmp/re.gpr --headless |
| ghidra-rpc list-binaries --project <gpr> | List binaries loaded in daemon | ghidra-rpc list-binaries -p /tmp/re.gpr |
| ghidra-rpc list-project-programs --project <gpr> | List all programs stored in the project repo (reads project folder, not CodeBrowser live state; returns warning if the active Ghidra project doesn't match) | ghidra-rpc list-project-programs -p /tmp/re.gpr |
| ghidra-rpc save [binary] --project <gpr> | Save program(s) to disk (auto-save also runs after every write) | ghidra-rpc save -p /tmp/re.gpr |
Loading & Analysis
| Command | Description | Example |
|---|
ghidra-rpc load <path> -p <gpr> [--no-analyze] [--analysis-timeout SECS] | Import & analyze a binary | ghidra-rpc load /usr/bin/target -p /tmp/re.gpr |
ghidra-rpc metadata <binary> -p <gpr> | Binary metadata (arch, bits, format) | ghidra-rpc metadata target -p /tmp/re.gpr |
load flags:
--no-analyze - skip auto-analysis entirely (fast; useful when you only need the
listing or plan to run analysis later via Ghidra GUI).
--analysis-timeout SECS - abort auto-analysis after the given wall-clock budget
(best-effort); the binary is saved with whatever analysis completed in time.
Listing
| Command | Description | Output shape |
|---|
ghidra-rpc functions <binary> [--offset N] [--limit N] [--address-min ADDR] [--address-max ADDR] [--with-body] | List functions (paginated); --address-min/max for server-side range filter; --with-body adds body range fields | {functions: [{name, address, signature[, body_min, body_max, body_size]}], count, total, offset} |
ghidra-rpc imports <binary> | List imports | {imports: [{name, address, library}], count} |
ghidra-rpc exports <binary> | List exports | {exports: [{name, address}], count} |
ghidra-rpc relocations <binary> [--address ADDR] [--limit N] | List relocation table entries | {relocations: [{address, type, symbol, bytes, status}], count, total} |
ghidra-rpc list-calling-conventions <binary> | List valid calling convention names for the architecture | {conventions: [str], default, count} |
Decompilation
| Command | Description | Output shape |
|---|
ghidra-rpc decompile <binary> <func> [--timeout SECS] | Decompile to pseudo-C (default 120 s) | {name, address, signature, c_code} |
ghidra-rpc basic-blocks <binary> <func> [--limit N] | Get basic blocks (CFG) of a function | {name, address, blocks:[{start,end,size,instructions,successors,predecessors}], num_blocks, edges} |
ghidra-rpc pcode <binary> <func> [--high] [--timeout SECS] [--limit N] | Get P-code (Ghidra IR); raw or high SSA form (--high) | {name, address, mode, ops:[{address,seq,opcode,output,inputs}], count, truncated} |
ghidra-rpc search-decompiled <binary> <regex> [--class NAME] [--case-sensitive] [--limit N] [--max-scan N] [--timeout SECS] [--socket-timeout SECS] | Regex-search decompiled C across many functions in one call (skips one-RPC-per-function decompile+grep loops) | {matches:[{function,address,matching_lines:[{line,text}]}], count, functions_searched, functions_total, truncated} |
<func> can be a function name or hex address (e.g., main or 0x401000). If the name
is ambiguous, the error message lists matches so you can use the address instead.
search-decompiled is especially useful on multidex Android projects, where xrefs-to
on a method only sees callers within the same DEX program (see the Android APK/DEX
section) — use it in place of a symbols+decompile+grep loop, e.g. search-decompiled app.apk targetF1 --class com::example::Foo. --max-scan (default 5000) bounds how many functions get decompiled
when --class isn't given, so a search on a 50k+-function binary can't run away; raise
--socket-timeout (default 1800s) alongside it if you do widen the scan.
Search
| Command | Description | Output shape |
|---|
ghidra-rpc find-bytes <binary> <pattern> [--limit N] [--address ADDR] | Search for byte pattern with wildcards (e.g. "55 8b ?? 83 ec") | {pattern, matches: [{address, context_hex}], count, truncated} |
ghidra-rpc strings <binary> <query> [--limit N] | Search strings (substring) | {strings: [{address, value, type}], count} |
ghidra-rpc symbols <binary> <query> [--limit N] [--offset N] | Search symbols | {symbols: [{name, address, type}], count, total} |
symbols normalizes literal spaces and underscores as equivalent before matching, so a
query pasted straight from strings output (real spaces) still finds the corresponding
label even though Ghidra auto-generates labels from string content by turning each space
into _ (and leaving every other character, including non-ASCII/CJK text, untouched).
Cross-References
| Command | Description | Output shape |
|---|
ghidra-rpc xrefs-to <binary> <target> [--limit N] [--all-binaries] | Who references this? | {xrefs: [{from_address, from_function, type}], count} |
ghidra-rpc xrefs-from <binary> <target> [--limit N] [--no-stack] | What does this reference? (--no-stack hides Stack[-0x...] entries) | {xrefs: [{to_address, to_function, type}], count} |
Ghidra's reference manager is per-binary: a call whose caller and target live in two
different loaded binaries is invisible to a single-binary xrefs-to lookup (most
commonly hit on multidex Android projects, where a method in one classesN.dex is
called from another, but this isn't DEX-specific — it applies to any project with more
than one binary loaded in the daemon). Pass --all-binaries to also search every other
currently loaded binary for a symbol with the same fully-qualified name and merge in
real callers found there; each merged entry carries a binary field so you can tell
which binary it came from.
Navigation (GUI mode only)
| Command | Description | Output shape |
|---|
ghidra-rpc goto <binary> <target> [function|address] | Navigate GUI | {address, success} |
Memory & Disassembly
| Command | Description | Output shape |
|---|
ghidra-rpc memory-map <binary> | List all memory segments (name, address range, size, rwx flags, type) | {segments: [{name, start, end, size, read, write, execute, initialized, type, source_name}], count} |
ghidra-rpc write-bytes <binary> <address> <hex> | Write raw bytes to an address; does NOT auto-redisassemble | {address, length, hex, verified} |
ghidra-rpc read-bytes <binary> <address> <length> | Read raw bytes; LENGTH accepts decimal or 0x hex | {address, length, hex, hexdump} |
ghidra-rpc read-pointers <binary> <address> <count> [--pointer-size N] | Read COUNT pointers and resolve each to its symbol (vtables, import/jump tables, RTTI arrays) | {address, count, pointer_size, endian, pointers:[{index, offset, slot_address, value, target_address, target_name, target_kind}]} |
ghidra-rpc list-vtable <binary> <address> [--count N] [--pointer-size N] | Dump a C++ vtable's slots as resolved methods; ADDRESS may be a symbol name. Without --count, stops at the next vftable symbol / first non-function pointer (see stopped_reason) | {vtable_address, symbol, count, pointer_size, stopped_reason, slots:[{index, offset, slot_address, target_address, target_name, target_kind}]} |
ghidra-rpc disassemble <binary> <address> [--count N] | Disassemble N instructions (default 20); warning field if start address had no instruction | {address, count, instructions:[{address,bytes,mnemonic,operands,length,comment}], listing[, warning]} |
ghidra-rpc assemble <binary> <address> <instr> [<instr> ...] | Assemble instruction text at address (Ghidra SLEIGH assembler) | {address, bytes_written, hex, instructions:[{address,bytes,mnemonic,operands,length}]} |
Modifications
| Command | Description | Output shape |
|---|
ghidra-rpc create-function <binary> <address> [--name NAME] | Create a function at an address (auto-detects body from flow) | {name, address, size, body} |
ghidra-rpc delete-function <binary> <target> | Remove a function definition (bytes unchanged; address reverts to undefined) | {address, name, deleted} |
ghidra-rpc rename-function <binary> <target> <new_name> [--namespace NS] | Rename function; --namespace moves it into a namespace | {address, old_name, new_name, verified[, old_namespace, new_namespace]} |
ghidra-rpc rename-symbol <binary> <address> <new_name> [--create] | Rename symbol; --create makes a new label if none exists | {address, old_name, new_name, created, verified} |
ghidra-rpc create-label <binary> <address> <name> | Create-or-rename a label at any address (upsert) | {address, name, old_name, action, created, verified} |
ghidra-rpc set-comment <binary> <address> --comment <text> [--type TYPE] | Set comment (comment text is --comment, matching set-bookmark) | {address, comment_type, comment, verified} |
ghidra-rpc batch-rename <binary> --json |--json-file FILE [--mode function|label] | Rename 40+ functions/labels in one transaction; per-item error reporting | {results:[{ok, index, address, old_name, new_name}], count, ok_count, error_count} |
ghidra-rpc batch-set-comment <binary> --json |--json-file FILE | Set comments at many addresses in one transaction | {results:[{ok, index, address, comment_type, comment}], count, ok_count, error_count} |
ghidra-rpc set-signature <binary> <target> <signature> | Set function signature | {address, old_signature, new_signature, verified} |
ghidra-rpc set-data-type <binary> <address> <type> | Define data type in listing | {address, data_type, length, value} |
ghidra-rpc retype-variable <binary> <func> <variable> <type> [--timeout SECS] | Retype decompiler variable | {function, variable, old_type, new_type, verified} |
ghidra-rpc rename-variable <binary> <func> <variable> <new_name> [--timeout SECS] | Rename decompiler variable | {function, variable, new_name, verified} |
ghidra-rpc batch-edit-variable <binary> <func> --json |--json-file FILE [--timeout SECS] | Rename and/or retype many locals in ONE decompiler snapshot — avoids the auto-name renumbering that breaks chained single edits. Each op: {variable|storage, new_name?, data_type?} (identify by name or storage string; at least one of new_name/data_type) | {function, results:[{ok, index, variable, storage, old_name, new_name, old_type, new_type, verified}], count, ok_count, error_count, verified_count} |
ghidra-rpc set-calling-convention <binary> <target> <convention> | Change a function's calling convention | {address, name, old_convention, new_convention, verified} |
ghidra-rpc set-thunk <binary> <thunk> <target> | Mark a function as a thunk (forwarding wrapper) | {thunk_address, thunk_name, target_address, target_name, verified} |
ghidra-rpc set-flow-override <binary> <address> <override> | Override instruction flow type (NONE/BRANCH/CALL/CALL_RETURN/RETURN) | {address, override, old_override, verified} |
ghidra-rpc get-processor-context <binary> <address> [--register REG] | Inspect ISA context register values (e.g. TMode on ARM) | {address, registers:{name:value}} |
ghidra-rpc set-processor-context <binary> <address> <register> <value> [--end ADDR] | Set ISA context register over a range — fixes ARM Thumb mis-classification | {address, end, register, value, verified} |
ghidra-rpc create-namespace <binary> <name> [--parent NS] | Create or look up a namespace | {name, path, id, created} |
ghidra-rpc list-namespaces <binary> [--limit N] | List all namespaces with symbol counts | {namespaces:[{name,path,id,type,symbol_count}], count} |
Comment types: plate, pre, post, eol (default), repeatable.
Signature input is sanitised automatically: trailing semicolons are stripped,
whitespace is trimmed, and inline calling conventions (__thiscall, __fastcall,
__stdcall, __cdecl, __vectorcall, __pascal) are extracted from the string
and applied via the proper Ghidra API. So you can paste a C prototype directly
(e.g. void __thiscall Foo::Bar(int x); works without manual cleanup).
Type expressions for set-data-type, retype-variable, and struct field types:
- Built-ins:
byte, char, int, uint, short, long, float, double, void, string, unicode
- Pointer:
char *, void *, int **
- Array:
char[11], int[4]
- Any type in the program's data type manager by name or path:
MyStruct, /POSIX/size_t
Order of operations — set signatures/types before naming locals. Each of
set-signature, set-data-type, retype-variable, and rename-variable re-runs the
decompiler, which re-numbers the remaining auto-named locals (uVar1, dVar2, …). So
chaining single-variable edits is fragile: after renaming dVar1, the old dVar2 may
now be dVar1. Do all signature/this-typing first, then name locals — or, better, use
batch-edit-variable to apply every rename and retype against a single decompiler
snapshot in one call (it also lets you address a variable by its stable storage string
instead of the volatile auto name).
Bookmarks
| Command | Description | Output shape |
|---|
ghidra-rpc set-bookmark <binary> <address> [--type TYPE] [--category CAT] [--comment TEXT] | Create/update a bookmark at an address | {address, type, category, comment, action} |
ghidra-rpc list-bookmarks <binary> [--type TYPE] [--category CAT] [--address ADDR] [--limit N] | List bookmarks (all, by type, category, or at address) | {bookmarks: [{address, type, category, comment}], count, total} |
ghidra-rpc remove-bookmark <binary> <address> [--type TYPE] [--category CAT] | Remove a bookmark at an address | {address, type, removed} |
Bookmark types: Note (default), Warning, Error, Info, Analysis.
--category is a case-insensitive substring filter. On heavily auto-analyzed binaries
(large native programs, and especially DEX/Dalvik — 50k+ functions is normal), Ghidra
generates many Analysis-type bookmarks (commonly category Address Table) that can
bury a handful of your own; filter them out with --category (or --type to isolate
your own type, e.g. Note) instead of paging through the unfiltered list.
Bookmarks are visible in the Ghidra GUI's Bookmarks window. Use them to
persistently mark interesting locations, track analysis progress, or flag
findings for human review.
Function Tags
| Command | Description | Output shape |
|---|
ghidra-rpc tag-function <binary> <target> --tag TAG | Add a tag to a function for classification | {address, name, tag, all_tags} |
ghidra-rpc untag-function <binary> <target> --tag TAG | Remove a tag from a function | {address, name, tag, removed, all_tags} |
ghidra-rpc list-tags <binary> | List all defined tags with use counts | {tags:[{name,count}], count} |
ghidra-rpc functions-by-tag <binary> --tag TAG [--limit N] | List functions with a specific tag | {tag, functions:[{name,address,signature}], count, total} |
Tags are string labels (e.g. crypto, vuln-sink, analyzed, needs-review)
visible in Ghidra's Function Tags window. Use them to classify functions
and track analysis progress.
Data-Type Authoring
| Command | Description | Output shape |
|---|
ghidra-rpc create-struct <binary> <name> TYPE FIELD [TYPE FIELD ...] [--if-not-exists|--or-replace] | Create a named struct; --if-not-exists is idempotent, --or-replace rebuilds it | {name, path, size, fields:[...], already_existed} |
ghidra-rpc create-struct <binary> <name> --field OFFSET TYPE NAME [--field ...] | Create a struct laying fields at explicit byte offsets (OFFSET is decimal or 0x hex); gaps between fields are auto-padded with undefined bytes (no manual pad fields). Overlaps are rejected. | {name, path, size, fields:[...], already_existed} |
ghidra-rpc create-union <binary> <name> TYPE FIELD [TYPE FIELD ...] [--if-not-exists|--or-replace] | Create a named union; all fields share offset 0 | {name, path, size, fields:[...], already_existed} |
ghidra-rpc modify-struct <binary> <struct_name> --field-offset N|--field-name NAME [--new-type TYPE] [--new-name NAME] | Retype, rename, or re-comment a struct field | {name, path, size, fields:[...], modified_field:{...}} |
ghidra-rpc clear-data-range <binary> <start> <end> | Reset inclusive byte range to undefined | {start, end, cleared} |
ghidra-rpc apply-data-type-range <binary> <start> <end> <type> [--clear] | Stamp a type repeatedly across an inclusive range; --clear clears the region atomically before stamping | {start, end, data_type, type_size, applied_count, cleared} |
ghidra-rpc list-labels <binary> <address> [--end ADDR] [--limit N] | List symbols at an address or in a range, including data type at each location | {labels:[{address,name,type,source,is_primary,data_type,data_length}], count, total} |
| `ghidra-rpc create-enum NAME VAL [NAME VAL ...] [--size 1 | 2 | 4 |
ghidra-rpc modify-enum <binary> <name> [--add NAME:VAL ...] [--remove NAME ...] | Add/remove individual entries from an existing enum | {name, path, size, values:[{name,value}]} |
ghidra-rpc set-equate <binary> <addr> <equate_name> <value> [--operand-index N] [--enum-path PATH] | Attach a named scalar constant to an instruction operand; auto-links to enum if found | {address, operand_index, equate_name, value, enum_linked, verified} |
ghidra-rpc list-equates <binary> [address] [--limit N] | List all equates in the program or only those applied at an address | {equates:[{name,value[,operand_index,address]}], count, total} |
ghidra-rpc list-data-types <binary> [--category CAT] [--query SUBSTR] [--limit N] | List types in the DTM; category: struct, enum, union, typedef, pointer, array, all | {data_types:[{name,path,category,size}], count, total} |
Version Tracking & Diff
| Command | Description | Output shape |
|---|
ghidra-rpc version-track <old> <new> [--changed-only] [--limit N] [--min-similarity F] | Match functions between two versions; find what changed. Results deduplicated per source function. | {matched:[{source_name,destination_name,similarity,confidence,correlator}], unmatched_source, unmatched_destination, summary:{source_functions_total, changed_functions, identical_functions, ...}} |
ghidra-rpc function-diff <bin1> <func1> <bin2> <func2> [--mode decompile|disassembly] | Unified diff of a function between two versions; normalises auto-generated names (FUN_*, DAT_*, local_*, param_*) to suppress relocation noise | {is_identical, diff, raw_code1, raw_code2, func1_address, func2_address} |
ghidra-rpc match-function <bin1> <func> <bin2> [--threshold F] | Find best match for a function in another binary using BSim + correlators | {source:{name,address}, candidates:[{name,address,similarity,confidence,correlator}], count} |
ghidra-rpc decompile-all <binary> [--limit N] [--offset N] [--timeout SECS] [--socket-timeout SECS] | Bulk decompile all functions; for export and external diff tools | {functions:[{name,address,signature,c_code}], count, total, offset, errors} |
decompile-all's default --socket-timeout (1800s) assumes a bulk sweep can take much
longer than any single function's --timeout; raise it further for binaries with tens of
thousands of functions (e.g. large DEX/APK programs).
For the full patch-diff workflow see docs/flows/patch-analysis.md.
Error Handling
Error responses look like:
{"id": "...", "ok": false, "error": "FunctionNotFound", "message": "Function 'foo' not found."}
Common errors:
- DaemonNotRunning: Daemon isn't started. Tell the user to run
ghidra-rpc start.
- ValueError: Bad argument (ambiguous name, invalid address). The message usually tells
you what to do - e.g., use a more specific name or an address.
- RuntimeError: Ghidra-level error. For GUI-only commands in headless mode, the error
says so explicitly.
All CLI output is JSON, including usage errors. A malformed invocation (unknown
option, invalid --type choice, unknown subcommand, missing required argument) is also
reported as {"ok": false, "error": "...", "message": "..."} on stdout, with a nonzero
exit code — never a plain-text Click usage string. Scripted callers can always
json.loads() stdout unconditionally, without special-casing argument errors.
Write Operations & Persistence
Write operations (rename, create-label, create-function, delete-function, set-comment, batch-rename, batch-set-comment, set-signature, set-data-type, retype-variable, rename-variable, batch-edit-variable, set-calling-convention, set-thunk, set-flow-override, set-processor-context, create-struct, create-union, modify-struct, create-enum, modify-enum, set-equate, set-bookmark, clear-data-range, apply-data-type-range, write-bytes, assemble, tag-function, untag-function, create-namespace)
use Ghidra transactions internally. Every write is automatically saved to the project
database on disk after the transaction commits, so changes survive daemon restarts and
are visible when the project is reopened in the Ghidra GUI.
Every write response includes a verified boolean that confirms whether the change was
read back successfully after committing.
You can also save explicitly at any time:
ghidra-rpc save
ghidra-rpc save <binary>
On clean shutdown (stop or Ctrl+C), all programs are saved automatically.
Patch / Diff Analysis Quick Start
ghidra-rpc load /path/to/binary_v1 -p project.gpr
ghidra-rpc load /path/to/binary_v2 -p project.gpr
ghidra-rpc version-track binary_v1 binary_v2 --changed-only
ghidra-rpc function-diff binary_v1 <func> binary_v2 <func>
Full workflow — correlator details, unmatched analysis, bulk decompile:
read docs/flows/patch-analysis.md.
Android APK / DEX Analysis
Ghidra has built-in APK and DEX support (Dalvik processor + FileFormats
loaders) — no extension install needed. load a .dex or .apk directly and
the usual commands (decompile, functions, strings, xrefs-*, …) work on
the Dalvik program, which decompiles to Java-like pseudocode. Note the
multi-dex caveat: load app.apk imports only the primary classes.dex.
For the full workflow — multi-dex extraction, class-qualified :: symbols and
ambiguous methods, the DEX string-vs-xrefs-to gotcha, minified apps, and
manifest/.so extraction — read docs/flows/android-apk.md.
Typical Workflow
- User starts daemon:
cd <skill-dir> && uv run ghidra-rpc start --project /path/to/project.gpr --headless
For automation / non-blocking start: uv run ghidra-rpc start --project /path/to/project.gpr --headless --detach
- Load binary:
uv run ghidra-rpc load /path/to/binary -p /path/to/project.gpr
Response includes short_name (usable alias in all subsequent commands).
(add --no-analyze to skip analysis, --analysis-timeout SECS to cap it)
- Get overview:
metadata, functions, imports, exports
- Investigate:
decompile interesting functions, xrefs-to to trace callers
- Annotate:
rename-function, set-comment to document findings
- Search:
strings for hardcoded values, symbols for specific patterns
FAQ / Common Gotchas
create-label properly replaces DEFAULT (auto-analysis) symbols:
create-label creates a USER_DEFINED label and sets it as the primary symbol at the
address. If a DEFAULT symbol (e.g. DAT_00418138) already exists, it is demoted to
secondary. The decompiler will then use the new name. rename-symbol renames in
place but requires an existing symbol; create-label is preferred for annotating
addresses regardless of existing symbols.
Annotating an address with no existing symbol (rename-symbol fails with "No symbol found"):
rename-symbol requires a symbol to already exist at the address. Auto-analysis only
places DAT_ symbols where it detects data; inside arrays or dense struct regions
many addresses have no symbol. Use create-label instead - it creates a new USER_DEFINED
label if none exists, or renames the existing one:
ghidra-rpc create-label <binary> 0x0040e4ac topPtrErrorMsg
You can also add --create to rename-symbol for the same upsert behaviour.
Defining and applying a struct across a memory region:
Full workflow to stamp a struct across a repeated data table:
ghidra-rpc create-struct binary ErrorEntry int errorNumber "char *" ptrErrorMsg --if-not-exists
ghidra-rpc apply-data-type-range binary 0x0040e4a8 0x0040e55f ErrorEntry --clear
After this, each address 0x0040e4a8, 0x0040e4b0, ... has type ErrorEntry.
Without --clear, apply-data-type-range skips positions where existing data
conflicts and reports them in the errors list. Use clear-data-range first if
you need finer control over which region is cleared.
Discovering what labels exist at an address or range:
ghidra-rpc list-labels binary 0x0040e4a8
ghidra-rpc list-labels binary 0x0040e4a8 --end 0x0040e55f --limit 50
Using the binary name after load:
load returns both a full key (binary) with a hash suffix and a short_name
(the original filename). Use short_name in all subsequent commands - it works
because commands match binary names by substring:
ghidra-rpc decompile WinHelloCPP.exe main
ghidra-rpc decompile WinHelloCPP main
If two binaries share the same stem, use the full key to disambiguate.
Trailing semicolons in signatures: Ghidra's parser rejects C prototypes that end
with ;. ghidra-rpc strips trailing semicolons automatically, so you can paste verbatim.
list-binaries is empty but I loaded a binary: In GUI mode, the binary must be
open in CodeBrowser. Use list-project-programs to see what's stored in the project
repo, then open it in CodeBrowser's Project window.
Daemon log file: When started in the background (start --detach or restart),
logs are written to /tmp/ghidra-rpc-<hash>.log (same directory and stem as the Unix
socket). If the daemon fails to start or become responsive, check that file first:
tail -50 /tmp/ghidra-rpc-*.log
The timeout error message always prints the exact log path.
GHIDRA_INSTALL_DIR not found after backgrounding: Environment variables may be
lost when daemonising (nohup, cron, systemd units, etc.). Fix options, in order of preference:
- Pass
--ghidra-install-dir /path/to/ghidra to start or restart - the value is
persisted in the session file and forwarded to every subsequent restart automatically.
- Export
GHIDRA_INSTALL_DIR in your shell profile (~/.bashrc, ~/.zshrc) so it
survives login sessions.
- Use
start --detach --headless from the same terminal where the env var is set.
Session file location: By default the session file (.ghidra-rpc-<hash>.json) is
written alongside the .gpr project file, keeping all state self-contained. To use a
custom directory (e.g. a shared location or a RAM-disk), set GHIDRA_RPC_STATE_DIR:
export GHIDRA_RPC_STATE_DIR=/tmp/ghidra-sessions
Changes not visible in the Ghidra GUI: Make sure you open the project copy
(e.g. basic_code-501243 in the Project window), not the raw binary via File → Open.
Also ensure you are not running two separate Ghidra instances on the same project
(causes lock conflicts). Changes made by ghidra-rpc are auto-saved to disk and will
be visible the next time the program is opened in the GUI.
restart without a prior session: Pass --headless so restart can create
a fresh headless session instead of requiring a prior start.
Decompiler timeout on certain functions: Some functions trigger expensive
decompiler analysis paths and time out even with simple assembly. Use
decompile --timeout 300 (or higher) for stubborn functions. The default timeout
is 120 s (increased from 60 s to handle large firmware functions). The
retype-variable and rename-variable commands also accept --timeout since it
triggers an internal decompilation pass.
decompile returns bad-instruction warnings — use pcode --high as fallback:
When decompile produces output like "Bad instruction data" or fails to decode
a function body (common on ARM Thumb code in regions that auto-analysis
mis-classified as data), try pcode --high instead:
ghidra-rpc pcode binary 0x03288102 --high
The P-code engine re-decodes bytes from the function object's context rather
than from the listing-level disassembler, so it often succeeds where decompile
fails. High P-code also reveals all CALL / CALLIND / BRANCHIND targets and data
flow, making it useful for tracing arguments even without valid decompilation.
Fix the root cause with set-processor-context (see below).
disassemble starts from the wrong address (B2 warning):
If disassemble is called at an address with no instruction (e.g. an address
that is mid-instruction or undefined), the response now includes a warning
field explaining that disassembly started from the next available instruction.
Check for warning in the JSON response when the listing looks wrong.
ARM Thumb mis-classification — set-processor-context is the fix (F1/F5):
When ARM auto-analysis classifies Thumb code as data, the listing disassembler
decodes it as 32-bit ARM (TMode=0) and produces garbage. The full recovery
workflow:
ghidra-rpc get-processor-context binary 0x03288100 --register TMode
ghidra-rpc clear-data-range binary 0x03288100 0x032883ff
ghidra-rpc set-processor-context binary 0x03288100 TMode 1 --end 0x032883ff
ghidra-rpc disassemble binary 0x03288100 --count 20
ghidra-rpc create-function binary 0x03288100
If create-function fails because a bad stub still exists at the address,
use delete-function first to remove it.
Bad stubs blocking re-creation — use delete-function (F2):
If create-function 0x03288103 (wrong parity) left a stub, and now
create-function 0x03288102 (correct address) fails with "already exists at
overlapping address", delete the bad stub first:
ghidra-rpc delete-function binary 0x03288103
ghidra-rpc create-function binary 0x03288102
Batch rename / comment 40+ functions without round-trip overhead (F7):
Use batch-rename and batch-set-comment to apply many annotations in a single
server round-trip and Ghidra transaction:
ghidra-rpc batch-rename binary --json-file renames.json
ghidra-rpc batch-rename binary --json \
'[{"target":"sub_401234","new_name":"init_uart"},{"target":"0x400200","new_name":"isr_handler"}]'
ghidra-rpc batch-rename binary --mode label --json \
'[{"address":"0x0333afcc","new_name":"g_debugStr"}]'
ghidra-rpc batch-set-comment binary --json \
'[{"address":"0x03288102","comment":"Thumb ISR entry","comment_type":"plate"}]'
All items that succeed are committed in one transaction; failed items are
reported per-item in results[].ok=false and do not roll back the successes.
Concurrent write operations are serialised: The daemon serialises all command
handler invocations internally to prevent Ghidra transaction conflicts. You do
not need to serialize write commands externally — parallel writes are queued
automatically. This is slightly slower than concurrent execution but eliminates
silent partial failures.
Reporting Bugs and Missing Features
While using ghidra-rpc you may encounter bugs, errors, or missing capabilities.
Please report them - real-world usage drives continuous improvement.
At the end of any analysis session where you hit a problem, save a brief report:
cat > /tmp/ghidra-rpc-issues-$(date +%Y%m%d-%H%M%S).md << 'EOF'
**Date:** $(date)
**Binary:** <name of binary analysed>
<!-- Describe: command used, error message / unexpected output, expected behaviour -->
<!-- Describe what you needed but couldn't do with the current command set -->
### Suggestions
<!-- Any UX improvements or new commands that would have helped -->
EOF
Then inform the user:
"I saved a ghidra-rpc issue report to /tmp/ghidra-rpc-issues-<timestamp>.md.
If you'd like to submit it, run: cat /tmp/ghidra-rpc-issues-*.md"
The report is written to /tmp so it stays local to the machine and is lost on reboot -
it is never sent anywhere automatically.
Further Documentation
For detailed guidance on specific workflows:
- Installation & setup: read
docs/install.md
- Quick start tutorial: read
docs/quickstart.md
- Troubleshooting: read
docs/troubleshooting.md
- Binary audit workflow: read
docs/flows/binary-audit.md
- Vulnerability research: read
docs/flows/vulnerability-research.md
- Patch/diff analysis: read
docs/flows/patch-analysis.md
- Android APK / DEX analysis: read
docs/flows/android-apk.md