307
24
In review
Synced today
What it does
>
Skill profile
Claude Code · Codex · OpenClaw
Keep exploring
More options in Dev Tools.
Python
Updated 8/6/2026
Agent compatibility
Compatibility has not been reviewed for this listing yet. Check the publisher documentation before installing.
Installation
npx skills add https://github.com/cellebrite-labs/ghidra-rpc --skill rootReview source code and installation permissions before adding third-party tools to an agent.
ghidra-rpc is organized in the Uncategorized category. Compare its source, install method, and compatibility before adding it to your workflow.
Third-party agent tools may access source code, credentials, or browser sessions. Read the source documentation and use the minimum permissions needed.
npx skills add https://github.com/cellebrite-labs/ghidra-rpc --skill rootSKILL.md
---
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):**
```bash
cd <skill-dir> && uv run ghidra-rpc <command> ...
```
## Prerequisites Check
Before using any commands, verify the setup:
1. **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`.
2. **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:
```bash
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.
3. **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.
4. **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] [--with-instructions]` | Disassemble N instructions (default 20); returns `listing` text, which already carries address/bytes/mnemonic/operands/comment — pass `--with-instructions` only when you need the machine-parseable array (~4× the output); `warning` field if start address had no instruction | `{address, count, listing[, instructions][, 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 <binary> <name> NAME VAL [NAME VAL ...] [--size 1|2|4|8] [--if-not-exists|--or-replace]` | Create a named enum in the DTM | `{name, path, size, values:[{name,value}], already_existed}` |
| `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-
… Documentation preview truncated. Open the source for the complete file.skill
mattpocock
Build a throwaway prototype to answer a design question. Use when the user wants to sanity-check whether a state model or logic feels right, or explore what a UI should look like.