A Rust command-line tool that automates Ghidra reverse engineering tasks such as binary analysis, decompilation, function inspection, and binary patching. It can be used by hand or driven by an AI coding agent like Claude Code.
Claude Code
$ npx -y skills add akiselev/ghidra-cli --agent claude-code
Codex
$ npx skills add akiselev/ghidra-cli -a codex
Cursor
$ npx skills add akiselev/ghidra-cli -a cursor
Gemini CLI
$ npx skills add akiselev/ghidra-cli -a gemini-cli
GitHub Copilot
$ npx skills add akiselev/ghidra-cli -a github-copilot
OpenCode
$ npx skills add akiselev/ghidra-cli -a opencode
Windsurf
$ npx skills add akiselev/ghidra-cli -a windsurf
A Rust CLI for automating Ghidra reverse engineering tasks. Usable directly by hand or driven by an AI coding agent like Claude Code.
ping, status, jobs, and cancel stay live on a separate control planesize > 100 AND name ~ 'crypt')┌─────────────────┐ ┌──────────────────────────────────────┐
│ CLI Command │──TCP──▶ │ GhidraCliBridge.java │
│ ghidra ... │ │ (GhidraScript in analyzeHeadless) │
│ --project X │ │ ServerSocket on localhost:dynamic │
└─────────────────┘ └──────────────────────────────────────┘
The CLI connects directly to a Java bridge running inside Ghidra's JVM. Ghidra loads once and stays resident, so one process holds all state: a rename or an applied type sticks for the next command instead of resetting each invocation, and queries return in-process with no per-command JVM startup. The bridge auto-starts on demand — any command brings it up, so you never launch it by hand.
Each project gets its own bridge process and port file, so you can analyze several binaries at once. The only runtime dependencies are Ghidra and a JDK — no Python/PyGhidra.
git clone https://github.com/akiselev/ghidra-cli
cd ghidra-cli
cargo install --path .
ghidra setup to fetch itjavac and the jdk.compiler module. Ghidra 12.x wants JDK 21; older releases accept JDK 17. ghidra doctor finds a suitable JDK and compiles the bridge as a health check.Point the CLI at your Ghidra install (skip this if you used ghidra setup):
export GHIDRA_INSTALL_DIR=/path/to/ghidra
# Or store it in config:
ghidra config set ghidra_install_dir /path/to/ghidra
ghidra-cli picks the JDK itself and passes it to Ghidra, so you don't have to
juggle PATH. Override the choice with the global --java-home flag, the
java_home config key, or GHIDRA_CLI_JAVA_HOME.
# Check installation
ghidra doctor
# Import a binary. This starts the bridge and runs auto-analysis in one step.
ghidra import ./binary --project myproject --program mybinary
# Query functions (uses the running bridge)
ghidra function list
# Decompile a function
ghidra decompile main
# Find interesting strings
ghidra find string "password"
# Get cross-references
ghidra x-ref to 0x401000
# Generate call graph
ghidra graph callers main --depth 3
These work before any subcommand, so you can set them once for a whole invocation:
ghidra --project P --program bin function list # --project/--program are global
| Flag | Effect |
|---|---|
--project <P> | Project name or path |
--program <PROG> | Program within the project |
--projects-dir <DIR> | Where Ghidra projects live (default: a cache dir; overrides ghidra_project_dir) |
--java-home <PATH> | Full JDK for Ghidra to use (must be a JDK, not a JRE) |
--json | Compact JSON output |
--pretty | Pretty-printed JSON |
-v / -vv / -vvv | Log verbosity: warn / info / debug |
-q / --quiet | Suppress non-essential output |
Ghidra 12.1+ rejects project directories that contain a dot-prefixed component
(such as ~/.cache), so on Linux the default falls back to
~/ghidra-cli-projects. Use --projects-dir to put projects wherever you like.
ghidra project create <name> # Create project
ghidra project list # List projects
ghidra project info [<name>] # Show project info
ghidra project delete <name> # Delete project (removes .gpr/.rep, stops its bridge)
ghidra import <binary> --project <p> # Import + auto-analyze (bridge auto-starts)
ghidra import <binary> --no-analyze # Import only, skip analysis (still persisted)
ghidra import <binary> --detach # Return immediately; bridge keeps importing
ghidra analyze --project <p> # (Re)run analysis on an imported program
ghidra function list # List all functions
ghidra function list --filter "size > 100" # Filter by size
ghidra decompile <name-or-addr> # Decompile function
ghidra decompile main --with-vars --with-params # Include variable/param details
ghidra disasm <address> --instructions 20 # Disassemble instructions
ghidra function disasm <func> --limit 0 # Whole function body (default --limit 1000)
ghidra function set-signature <func> --signature "int foo(int x, char *y)"
ghidra function set-return-type <func> --type void
ghidra function set-calling-convention <func> --convention __cdecl
ghidra function set-var-type <func> --var local_10 --type "MyStruct *"
Decompilation has no native time limit by default, so large valid functions are
not aborted after 30 seconds. Set GHIDRA_CLI_DECOMPILE_TIMEOUT to a positive
number of seconds to impose a Ghidra-side limit; 0 means unbounded. The socket
wait follows the long-operation policy controlled by GHIDRA_CLI_OP_TIMEOUT,
which is also unbounded by default.
ghidra symbol list # List symbols
ghidra symbol create <addr> <name> # Create symbol
ghidra symbol rename <old> <new> # Rename symbol
ghidra type list # List data types (with kind: struct/enum/typedef/...)
ghidra type get <name> # Get type details (fields, enum members, typedef base)
ghidra type create <name> # Create empty struct
ghidra type add-field <struct> --name fd --type int # Add struct field
ghidra type del-field <struct> --name fd # Remove struct field
ghidra type create-enum <name> --values "A=0,B=1" # Create enum
ghidra type typedef <name> <base_type> # Create typedef alias
ghidra type rename <old> <new> # Rename type
ghidra type delete <name> # Delete type
ghidra tag list # All tags (name, comment, use count)
ghidra tag get <name> # Functions carrying a tag
ghidra tag create <name> --comment "…" # Create a tag (comment optional)
ghidra tag add <func> <tag>... # Attach tags (auto-creates missing ones)
ghidra tag remove <func> <tag>... # Detach tags (--all clears every tag)
ghidra tag rename <old> <new> # Rename everywhere it is used
ghidra tag set-comment <name> "…" # Set/clear a tag's comment
ghidra tag delete <name> # Delete tag, detaching from all functions
ghidra function list --tag <name> # Filter by tag (repeatable = AND)
ghidra function list --untagged # Functions with no tags
Tag names are case-sensitive. tag add/remove are idempotent (already-present
and not-present tags are reported, not errors). Function rows include a sorted
tags array, so --fields name,address,tags and --filter "tags ~ 'crypto'"
work too.
ghidra x-ref to <address> # References TO address
ghidra x-ref from <address> # References FROM address
ghidra find string "pattern" # Find strings
ghidra find bytes "90 90 90" # Find byte patterns
ghidra find function "*crypt*" # Find functions by name
ghidra find crypto # Find crypto constants
ghidra find interesting # Find interesting patterns
ghidra graph calls # Full call graph
ghidra graph callers <func> # Who calls this? (--depth optional)
ghidra graph callees <func> # What does this call? (--depth optional)
ghidra graph export dot # Export to DOT format
ghidra patch bytes <addr> "90 90" # Patch bytes
ghidra patch nop <addr> --count 5 # NOP out instructions
ghidra patch export -o patched.bin # Export patched binary
patch nop --count N NOPs N consecutive instructions starting at the address
(default 1), walking instruction by instruction so variable-length ISAs work. If
any address in the run has no instruction, the whole patch rolls back untouched.
ghidra comment get <address> # Get comment
ghidra comment set <addr> "note" --comment-type EOL # Set comment
ghidra comment list # List all comments
function disasm walks the whole function body in address order and honours
--limit (0 = every instruction). When the output is cut at the limit it
prints warning: showing N of M instructions on stderr; the JSON rows stay a
plain instruction array.
--comment-type accepts EOL (default), PRE, POST, or PLATE.
ghidra script list # List available scripts
ghidra script run myscript.py # Run a script file
ghidra script run report.py -- arg1 arg2 # Pass positional args after --
ghidra script run dump.py --expect out.csv:10 # Fail unless out.csv has >=10 rows
ghidra script run Survey.java --read-only # Run concurrently with other reads
script python and script java (inline code) are not supported in bridge
mode; save the code as a .java GhidraScript and use script run.
script run resolves the path to an absolute location, forwards the args after
-- to the script, and captures its stdout in the response. Use --expect PATH[:MIN_ROWS] (repeatable) to make the job fail when an output artifact is
missing, empty, or short, and --allow-empty to permit an expected-but-empty
file.
Scripts run exclusively by default (no other job touches the program while they
run). --read-only runs the script as a reader, alongside other read-only
commands; the bridge records the program's modification number before and after
and fails the job (read_only_violation) if the script changed the program. A
compile error fails the job with the javac diagnostics (script_compile_error);
the bridge never falls back to an older build of the class.
ghidra batch commands.txt # Run commands from file
ghidra stats # Program statistics
ghidra summary # Program summary
Install it with the command on its page. It includes ghidra-cli. Its skills do not fire on their own yet. Free and open source.
Is this plugin yours?
Claim it with GitHubSubmit a pluginPromote it