idac is the IDA Pro CLI for the agents (and humans). One Unix socket, no JSON-RPC framing, no sidecar daemon, no babysitting — just idac decompile sub_08041337 from any shell or agent
idac is in early alpha. It is pretty good but CLI functionality and features are currently being actively developed.
- Agent-native by default — every command can emit structured JSON, every mutation supports
previewfor dry-run, and the bundled skill teaches Claude Code and Codex to drive it. - Not an MCP server — compose with shell, pipes,
xargs,jq, and your agent's existing tool-use loop. - Built for batches — recover an entire class hierarchy, retype a hundred locals, or decompile every
Handler_*in oneidac batchinvocation against a shared context. - Live or headless — the same commands work against any of your several open IDA databases or saved database. Switch with
-cflag to specify a pid or saved idb/i64
idac decompilemany "Handler_" --out-dir decomp/ -c "db:sample.i64"Every Handler_* function decompiled into its own .c file with a manifest.json. Works identically against a live GUI session — drop the -c flag and idac auto-targets the only open instance.
Install, then wire up the GUI plugin and agent skill:
uv tool install -e .
idac misc plugin install
idac misc skill installTalk to a live GUI session:
idac targets list --json
idac decompile "sub_08041337"
idac decompile "sub_08041337" --f5
idac decompile "sub_08041337" -c "pid:1234"Work headless against an existing database:
idac doctor
idac database show -c "db:sample.i64"
idac decompile "ExampleClass::method_1" -c "db:sample.i64"
idac decompile "sub_08041337" --no-cache -c "db:sample.i64"If running from the repo without a global install:
uv run idac --helpFor idalib, idapro must already be installed. This repo assumes the standard IDA 9.3 macOS layout at /Applications/IDA Professional 9.3.app/Contents/MacOS.
Most commands can run with no context at all when exactly one live IDA GUI session is open. For explicit selection, use -c/--context with either a GUI selector or a database locator such as db:sample.i64.
Drive your live IDA session — idac connects to a running IDA desktop over a Unix socket bridge. Use idac targets list --json to discover GUI and open headless targets. If only one GUI is open, most commands can omit -c entirely. Use -c pid:<pid> or -c <module> when multiple GUI sessions are open.
Spin up headless databases on demand — passing -c "db:<database.i64|idb|binary>" starts or reuses a headless per-database idalib process automatically. Open headless rows show backend: "idalib" in targets list --json. Use idac database save -c "db:<database>" to checkpoint and idac database close -c "db:<database>" to tear down.
Both backends use Unix sockets. The recommended setup is to scaffold a project-local reversing workspace:
idac workspace init reversing-workspaceThat creates workspace-local .claude/ and .codex/ config files, agent guidance files, prompt templates, a reference/ copy of the bundled skill docs, and a git-backed directory layout for reverse-engineering work. The generated sandbox settings are intentionally broad so sandboxed agents can reach the idac Unix socket bridge.
Unix socket access is required for all live GUI operations (read-only and mutating). If read-only commands succeed but a mutation fails, troubleshoot the underlying IDA/database error rather than assuming a socket permission split.
If you prefer manual setup or want to customize the generated files, see the workspace templates under src/idac/workspace_template/default.
Use idac --help for a specific subcommand or idac --full-help for the complete CLI surface.
| Family | Commands |
|---|---|
| Discovery | doctor, docs, targets list, database show, segment list, bookmark list/show, comment show |
| Functions | function list, metadata, frame, stackvars, callees, callers, prototype, locals |
| Decompilation | decompile, decompilemany, disasm, ctree |
| Search | search bytes, search strings, xrefs, imports |
| Types | type list, show, declare, type struct list/show/field, type enum list/show/member, type class vtable |
| Classes | type class list, show, hierarchy, fields, candidates |
| Mutations | misc rename, comment set/delete, bookmark add/set/delete, function prototype set, function locals update/rename/retype, type struct field set/rename/delete, type enum member set/rename/delete |
| Batch | batch, preview |
| IDAPython | py exec |
| Workspace | workspace init |
| Maintenance | misc reanalyze, database open/save/close, targets cleanup, misc plugin, misc skill |
Many models want to fill up their context quickly by running strings so currently search bytes and search strings require both --timeout and --segment.
Preview uses a wrapper command:
idac preview -o "/tmp/preview.json" function prototype set "sub_08041337" --decl "int __fastcall sub_08041337(void *ctx, const unsigned char *buf, unsigned int len)"function locals update, function locals rename, and function locals retype use the same selector model. rename takes the replacement name via --new-name:
idac function locals update "sub_08041337" "v12" --rename "value_maybe" --decl "unsigned int value_maybe;"
idac function locals rename "sub_08041337" "v12" --new-name "value_maybe"
idac function locals rename "sub_08041337" "3" --new-name "value_maybe"
idac function locals rename "sub_08041337" "stack(16)@0x100000460" --new-name "value_maybe"
idac function locals retype "sub_08041337" "v12" --type "unsigned int"
idac function locals retype "sub_08041337" "v12" --decl "unsigned int v12;"Selector forms:
- local name such as
v12 - numeric index such as
3 - canonical local id such as
stack(16)@0x100000460
Use function locals list --json to read the canonical local_id string.
Prefer --index or --local-id for longer mutation passes or after prototype/reanalysis changes:
idac function locals update "sub_08041337" "value_maybe" --index 3 --decl "unsigned int value_maybe;"
idac function locals update "sub_08041337" --local-id "stack(16)@0x100000460" --rename "value_maybe"
idac function locals rename "sub_08041337" --index 3 --new-name "value_maybe"
idac function locals retype "sub_08041337" --local-id "stack(16)@0x100000460" --decl-file "local_v4.h"Use --type when you only need to spell a simple type such as unsigned int or MyStruct *.
Use --decl or --decl-file when the retype needs a full declaration, for example arrays, function pointers, or a declaration whose exact local spelling matters.
Prefer function locals update when a recovered local needs both a better name and a better type in one pass. Keep rename and retype for simple one-off edits.
Most read commands default to --format text. Use --format json (or the -j shortcut) or --format jsonl when parsing output, and -o/--out <path> for large results. When --out is set, stdout stays empty and the CLI prints a short stderr summary with the artifact path; broad discovery commands also include result counts there. Matching is case-sensitive by default, broad list commands use one positional name filter, --regex treats that filter as a regular expression, function list --demangle matches and renders demangled display names, and type list / type struct list / type enum list require --out when no filter is given.
If pseudocode looks stale after reanalysis or nearby mutations, rerun decompile with a fresh Hex-Rays pass:
idac decompile "sub_08041337" --no-cache
idac decompile "sub_08041337" --f5--f5 is an alias for --no-cache, named after the usual Hex-Rays refresh shortcut in the UI.
decompilemany decompiles a set of functions in one pass against a shared context. Select either by name filter or by reading exact identifiers from a file, and choose between one combined output file (--out-file) or one .c file per function plus a manifest.json (--out-dir):
idac decompilemany "Handler_" --out-dir ".idac/tmp/decomp" -c "db:sample.i64"
idac decompilemany "Handler_.*" --regex --out-dir ".idac/tmp/decomp" -c "db:sample.i64"
printf '%s\n' main sub_401000 0x401234 > funcs.txt
idac decompilemany --functions-file "funcs.txt" --out-file ".idac/tmp/decompile.c" -c "db:sample.i64"Pass --f5 (alias for --no-cache) when running readback after type or prototype changes so each function reflects the latest state.
With --out-dir, pass --disasm and/or --ctree to capture matching .asm and .ctree artifacts for each selected function alongside the .c decompile artifact. The manifest records each function's name, exact address, and artifact paths.
Function-targeting commands (decompile, disasm, ctree, and the function family) accept demangled C++ names when they resolve uniquely, for example idac decompile "ExampleClass::method_1". On non-unique matches, use a mangled name, full signature, or address. Other identifier-taking commands (name, comment, bookmark, search, xref, vtable lookups) take only addresses or mangled names.
function metadata and function list --json include a display_name field with the demangled symbol name when available. function list --json rows also include section; text output shows that section column by default. function list --demangle changes filtering and text output to use the display name while keeping JSON name stable.
Run many subcommands against one shared context:
idac batch "recovery.idac" --out "/tmp/recovery_batch.json"
idac batch "rename-pass.idac" -c "db:sample.i64" --out "/tmp/rename-pass.jsonl"Batch files use one shell-like subcommand per line, omit the leading idac (a leading idac is also accepted), and inherit -c/--context and --timeout from batch. Relative child paths such as --decl-file, --functions-file, and per-line --out resolve from the batch file directory. Blank lines and # comments are ignored. If a batch contains persistent mutating commands, batch --out is required so the ordered result log is preserved before any changes run.
A prototype-cleanup batch file (prototype-pass.idac) might look like:
# Run after support types already exist locally.
function prototype set "0x100000000" --decl "int __fastcall ExampleClass__parseHeader(ExampleClass *__hidden this, const unsigned __int8 *buf, unsigned int len)"
function prototype set "0x100000100" --decl "void *__fastcall ExampleClass__buildResult(ExampleClass *__hidden this, InputContext *ctx, const ExampleOptions *options)"
function prototype set "0x100000200" --decl "unsigned int __fastcall ExampleClass__getCount(const ExampleClass *__hidden this)"
A local-rename pass (rename-pass.idac) is just a sequence of function locals rename lines:
function locals rename "0x100000000" 5 --new-name header_size
function locals rename "0x100000000" 6 --new-name record_type
function locals rename "0x100000100" 4 --new-name result_ptr
function locals rename "0x100000100" 11 --new-name error_code
Batch reuses the same handler surface as normal execution, accepts preview ... lines, and writes JSON or JSONL based on the output filename. Maintenance and setup misc commands are intentionally rejected from batch.
A bundled skill in src/idac/skills/idac teaches Claude Code and Codex to prefer idac commands over ad hoc shell or raw IDAPython for RE work.
idac misc skill installThis installs into both ~/.claude/skills/idac and ~/.codex/skills/idac. Both agents auto-discover skills from their skills/ directories.
Once installed, the skill is loaded automatically when relevant. For starter task prompts (general analysis, class-recovery passes, full reverse-engineering passes), run idac workspace init <dir> to scaffold a workspace whose prompts/ directory contains ready-to-edit templates. Use idac docs for an index of bundled command, workflow, and IDA reference material.
uv sync
make test # run tests
make check # format + lint + test + auditSee docs/development.md for fixture regeneration, live GUI tests, and local tooling details.
Inspired by @banteg's bn Binary Ninja CLI tool.
Written by Codex/gpt-5.3-codex/gpt-5.4/gpt-5.5.