idac
The IDA Pro CLI built for agents and humans, powered by
ida-nexus. Run
idac decompile "sub_08041337" from any shell or agent against either a live IDA
session or a headless database.
idacis in early alpha and actively developed. It is already useful day to day, but the CLI surface may still change between releases.
Contents
- Why idac
- Demo
- Quick start
- Requirements
- Upgrading from 0.19
- How it works
- Agent sandbox setup
- Usage
- Highlights
- Agent plugin
- Development
- Credits
Why idac
- Shell-native — compose with pipes,
xargs,jq, and your agent's existing tool-use loop. The CLI connects through Nexus; no separate MCP server is required. - Agent-native by default — every command can emit structured JSON (
-j), and theidacAgent Plugin teaches compatible agents to driveidacinstead of guessing at raw IDAPython. - Safe mutations — supported mutations offer
preview, which applies the change, captures the before/after, and restores it with IDA undo or an operation-specific rollback. Dry-run retypes, prototype changes, and other preview-capable edits before committing them. - Built for batches — recover an entire class hierarchy, retype a hundred locals, or decompile every
Handler_*in one invocation against a shared context. - Live or headless — the same commands work against a running IDA GUI session,
a saved
.i64, or a binary that IDA can open. Select a path with-c, an exact running instance with--instance, or omit both when exactly one instance is ready.
Demo
Run this from a regular shell against the fixture committed in this repo. Agents using a per-command sandbox should first follow Agent sandbox setup:
idac decompilemany "CreateHandler_" --out-dir decomp/ \
-c fixtures/idb/handler_hierarchy.i64
Every matching function is decompiled into its own .c file (named <symbol>_0x<address>) alongside a manifest.json index:
decomp/
├── CreateHandler_Text_0x100000588.c
├── CreateHandler_Stream_0x100000608.c
├── CreateHandler_Pack_0x100000688.c
└── manifest.json
The .c files hold the real Hex-Rays pseudocode:
Handler *__cdecl CreateHandler_Text()
{
Handler_Text *v1; // [xsp+8h] [xbp-18h]
v1 = (Handler_Text *)operator new(0x38u);
Handler_Text::Handler_Text(v1);
return v1;
}
manifest.json records the exact address, symbol, and artifact paths for each function (abridged to one of three entries):
{
"ok": true,
"pattern": "CreateHandler_",
"out_dir": "decomp",
"functions_total": 3,
"functions_succeeded": 3,
"functions_failed": 0,
"functions": [
{
"identifier": "CreateHandler_Text",
"name": "CreateHandler_Text",
"address": "0x100000588",
"ok": true,
"chars": 175,
"artifact_path": "decomp/CreateHandler_Text_0x100000588.c",
"artifact_stem": "CreateHandler_Text_0x100000588",
"artifacts": { "decompile": "decomp/CreateHandler_Text_0x100000588.c" }
}
]
}
The same command works against a live GUI session — drop -c when it is the only
READY Nexus instance.
Quick start
Install the CLI from PyPI. For live desktop work, install the matching GUI integration:
uv tool install idac # installs the `idac` command on your PATH
idac setup gui # GUI plugin matching the installed ida-nexus client
idac doctor # report local versions and check the IDA environment
Restart IDA or load the installed Nexus component, then rerun idac doctor and
idac targets list --json. Headless work does not require the GUI component.
From a regular shell, select an existing database or binary with -c to start a
managed worker. For Linux agents with per-command sandboxes, follow
Agent sandbox setup before any database read or edit.
For the agent guidance, install the Agent Plugin (see Agent plugin).
ida-hcli>=0.24.0 is an idac runtime dependency. Setup and diagnostics
run it through idac's Python environment; they do not require uvx or a
separately installed HCLI executable. Runtime dependencies use minimum versions;
uv.lock records the resolved versions for reproducible development installs.
setup gui selects the release matching the installed Nexus client and lets the
installer resolve ida-domain within the declared requirement.
To install the latest development version straight from git instead:
uv tool install git+https://github.com/trailofbits/idac.git
Talk to a live GUI session:
idac targets list --json
idac decompile "sub_08041337"
idac decompile "sub_08041337" --f5 # force a fresh Hex-Rays pass
idac decompile "sub_08041337" --instance "<record-id>"
Work headless against an existing database from a regular shell:
idac database show -c sample.i64
idac decompile "ExampleClass::method_1" -c sample.i64
To run from a checkout without installing globally, use uv run idac --help.
Requirements
- Python 3.11+ and
uv. - IDA Pro 9.4+ with the Hex-Rays decompiler for
decompile,ctree, and class recovery. - A valid IDA license and an IDA Python environment on Python 3.11 or newer.
ida-nexus>=0.13.2andida-domain>=0.5.1, installed as dependencies ofidac. Install the matching GUI component withidac setup guiwhen using a live GUI.- Linux for persistent shared sessions opened with
idac database open.
Run idac doctor to report CLI package versions, check the configured IDA version
through HCLI without starting IDA, and check the installed GUI
component, Nexus discovery, and the runtime inside every ready IDA instance.
When Codex or Claude is on PATH, it also compares installed idac Agent Plugin
versions with the CLI version and warns if they differ. Each optional agent check
has a two-second default timeout; --timeout overrides it.
Remote runtimes must satisfy the package requirements, and Nexus enforces protocol
compatibility. idac does not fall back to another backend.
Upgrading from 0.19
The rewrite requires Python 3.11+ and IDA Pro 9.4+. Install the matching Nexus GUI
component with idac setup gui for live desktop work, and install the Agent Plugin
separately for agent guidance.
If uv's exclude-newer policy makes ida-nexus>=0.13.2 unavailable, installation
fails during dependency resolution. Use a policy cutoff that admits the required
Nexus release before upgrading; the resolver cannot satisfy this dependency with
an older release.
Update existing commands and scripts as follows:
| Previous usage | Current usage |
|---|---|
-c db:sample.i64 |
-c sample.i64; existing binary paths work too |
-c pid:1234, -c module:NAME, or a bare module selector |
--instance RECORD_ID from idac targets list --json |
A legacy .idb database |
Convert it to .i64 in IDA first |
misc plugin install |
setup gui, followed by restarting IDA or loading the Nexus component |
misc skill install |
Install the Agent Plugin through your client's marketplace |
idac docs |
Subcommand --help, --full-help, and the installed skill's references |
targets cleanup |
Use database close --instance RECORD_ID for a persistent database opened by idac |
database save DESTINATION |
database save checkpoints the selected database; it accepts no destination path |
py exec --persist |
Combine dependent Python work in one invocation; each execution has a fresh namespace |
targets list --json returns Nexus records with record_id, state, detail,
backend, pid, idb_path, exe_path, managed, and started_at. Databases opened
with database open also report session_status and, when present, session_error.
Update scripts that read the old target fields. Omitted selectors require exactly one READY
instance, including managed headless workers. -c can attach to a matching GUI
database, so it does not guarantee headless execution.
Put target and timeout options on batch and preview, not on their child
commands. Successful headless mutations save automatically; batches keep earlier
successful changes even if a later step fails. GUI saves remain explicit. See
How it works and batch usage
for execution and journal behavior.
How it works
Every IDA operation goes through the public ida-nexus Python API. -c/--context PATH
opens or attaches to an .i64 or input binary. Nexus prefers a matching live GUI
database, reuses a matching managed worker, or starts a headless worker. Use
--instance RECORD_ID to attach to one exact record from idac targets list.
With neither selector, idac proceeds only when discovery finds exactly one READY
instance. Ambiguous, missing, blocked, disconnected, timed-out, or version-mismatched
targets fail explicitly. An operation is never retried on a different instance.
Headless opens wait for auto-analysis, and successful headless mutations save automatically. Live GUI commands use the current analysis state and leave saving explicit. Persistent Linux sessions support worker reuse across commands and agents. See the target and lifecycle guide for lease lifetime, command coordination, saves, and failure recovery.
Use idac targets list --json to inspect discovery records and idac doctor to
diagnose the local and remote runtime stack.
Agent sandbox setup
Linux command sandboxes can terminate detached IDA workers between tool calls. The idac skill covers safe startup and shutdown; its hosted-session guide documents approvals, multi-agent coordination, and recovery.
Scaffold a project-local reversing workspace for sandboxed agents:
idac workspace init reversing-workspace
That creates workspace-local .claude/ and .codex/ config, agent guidance files,
prompt templates, and a git-backed directory layout for RE work. Nexus discovery and
execution are local to the host; the generated workspace allows the local access
needed by idac. Install the Agent Plugin in the client separately; workspace
initialization does not copy the skill or its references.
Claude and Codex share AGENTS.md directly; the workspace does not generate a
separate CLAUDE.md.
To customize the generated files, see the templates under src/idac/workspace_template/default.
Usage
Use idac <command> --help for one subcommand and idac --full-help for the complete CLI surface. Command grammar, workflow, and IDA reference material ships with the plugin's skill and is loaded by agents that have it installed.
Command families
| Family | Commands |
|---|---|
| Discovery | doctor, 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, disasm --start/--end, ctree |
| Search | search bytes, search strings, xrefs, imports |
| Types | type list, show, deps, check, 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/apply, type struct field set/rename/delete, type enum member set/rename/delete |
| Batch | batch, batch --lint, preview |
| IDAPython | py exec |
| Workspace | workspace init |
| Persistent Linux target | database open, targets list --json, database close |
| Maintenance | misc reanalyze, database save, setup gui |
Output
Most read commands default to --format text. Use --format json (or -j) or --format jsonl when parsing, and -o/--out <path> for large results. With --out, stdout stays empty and a short stderr summary reports the artifact path and result counts. Matching is case-sensitive by default; --regex treats the filter as a regular expression and -i makes it case-insensitive. search bytes and search strings require both --timeout and --segment.
Highlights
A few of the commands that make idac worth reaching for. Install the plugin for the full command and workflow reference.
Preview a mutation before committing
preview is a wrapper that runs the real mutation and restores it with IDA undo or an operation-specific rollback, returning the before/after so you (or an agent) can verify the change first:
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)"
The wrapper owns the preview artifact. A wrapped command cannot set --out,
--out-file, or --out-dir; put --out on preview itself. Output paths are
also rejected when they alias the selected binary/database or any command input.
Put -c, --instance, and --timeout on the wrapper too; child commands cannot
override them.
Unfiltered type, struct, and enum lists can use the preview or batch wrapper's
--out to preserve their full results. File output checks use the selected
Nexus database and input paths, including GUI databases saved under another name.
Recover C++ class hierarchies
Walk vtables, flattened layouts, and inheritance straight from the database:
idac type class list
idac type class hierarchy "ExampleClass"
idac type class show "ExampleDerived" # flattened object layout
idac type class vtable "ExampleDerived" --runtime
Decompile a whole family in one pass
Select by name filter or by reading exact identifiers from a file; emit one combined file or one .c per function plus a manifest.json:
idac decompilemany "Handler_" --out-dir "decomp/" -c sample.i64
idac decompilemany "Handler_.*" --regex --out-dir "decomp/" --disasm --ctree
printf '%s\n' main sub_401000 0x401234 > funcs.txt
idac decompilemany --functions-file "funcs.txt" --out-dir "decomp-exact/"
# Or use --out-file for one combined text file without a manifest.
Pass --f5 after type or prototype changes so each function reflects the latest state. With --out-dir, the manifest records each function's name, exact address, and artifact paths.
If you also write the command summary with --out, choose a different path from
the combined pseudocode file or any generated directory artifact.
For prototype edits that must retain the existing calling convention, use
function prototype set --preserve-cc. This preserves IDA's stored convention
when the declaration parser would otherwise normalize it. For __usercall and
__userpurge, it also preserves explicit argument locations by parameter position
and the return location. These custom-convention edits require the same parameter
count and parameter/return type sizes; supply explicit locations without the flag
when changing those. type check validates
dependent declarations together in a temporary type library, without importing
or replacing database types. Type imports preserve #pragma directives, including
structure packing, while removing comments, includes, and include guards. Changing
an existing local type requires type declare --replace; identical declarations
and references to existing types can be imported without it. A refused replacement
restores the entire import, including any new types declared in the same header.
misc rename is batch-safe, so related renames, prototype edits, comments, and
readbacks can share one Nexus session.
Run an ordered mutation pass with batch
Run many subcommands against one shared context, leaving behind a stable ordered log. Batch files use one subcommand per line (drop the leading idac) and inherit the target (-c or --instance) and --timeout from the batch call. Child commands cannot set their own target or timeout because the wrapper owns one Nexus session for the entire run:
idac batch "recovery.idac" --out "/tmp/recovery_batch.json"
idac batch "recovery.idac" --lint --out "/tmp/recovery_batch_lint.json"
# recovery.idac — run after support types exist locally
type check --decl-file "recovered_classes.h"
type declare --replace --decl-file "recovered_classes.h"
function prototype check "0x100000000" --decl "int __fastcall ExampleClass__parseHeader(ExampleClass *__hidden this, const unsigned __int8 *buf, unsigned int len)"
function prototype set "0x100000000" --decl "int __fastcall ExampleClass__parseHeader(ExampleClass *__hidden this, const unsigned __int8 *buf, unsigned int len)"
misc reanalyze "0x100000000"
function locals rename "0x100000000" 5 --new-name header_size
function locals rename "0x100000000" 6 --new-name record_type
Mutating batches require --out so the result log is preserved before any change runs. batch --lint parses child commands, resolves relative input paths, rejects unsupported batch commands, and warns on risky local selectors before execution. Setup commands are intentionally rejected from batch; misc reanalyze is batch-safe and belongs between type/prototype changes and local cleanup.
Before dispatching the first command, batch writes a pending journal and checkpoints it after every line. It closes the shared Nexus session before replacing that journal with a terminal ok, failed, or interrupted record; Ctrl-C returns 130 without discarding the lifecycle record. Mutating child commands cannot set their own --out—the wrapper artifact is the mutation log—while read-only children may still write separate artifacts.
Child output paths cannot replace existing input files referenced by any batch line. A read-only child can generate a new file for a later child to consume when that file did not exist before the batch started. On subsequent runs, remove that generated file or choose a new path before regenerating it.
Use --fail-fast when later steps depend on earlier ones or local selectors must
stop on the first miss. A batch does not roll back earlier successful mutations;
headless checkpoints survive a later failure or interruption.
Address locals three ways
function locals update/rename/retype share one selector model — local name, numeric index, or canonical local_id. Prefer --index or --local-id for longer passes and after reanalysis, since names drift:
idac function locals rename "sub_08041337" "v12" --new-name "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" --type "unsigned int"
idac function locals apply "sub_08041337" --json-file "locals-plan.json"
Read the canonical local_id with idac function locals list --json. Use update when one local needs both a better name and type in one pass, and apply when several locals in one function should be applied from one fresh locals snapshot.
Escape hatch: raw IDAPython
When no first-class command fits, drop to IDAPython against the same target:
idac py exec --code "result = {'entry': hex(ida_ida.inf_get_start_ea())}"
Each execution uses a fresh namespace; --persist is no longer available. Use
--script PATH or --stdin for multiline code, and keep dependent work in one
invocation. Script contents are read locally and execute inside IDA with the local
filename as __file__. Assign JSON-native data to result; structured output
includes result, result_repr, stdout, and stderr. py exec is treated as
mutating for saving and cannot be previewed.
Agent plugin
The Agent Plugins v1 package in
plugins/idac teaches compatible agents to prefer idac commands over
ad hoc shell or raw IDAPython for RE work. The repository catalog at
.agents/plugins/marketplace.json points directly to
that package; plugins/idac/plugin.json is its manifest and
plugins/idac/skills/idac contains its idac skill. The
plugin is not included in the idac Python package.
Install it through an Agent Plugins-compatible client. With Codex:
codex plugin marketplace add trailofbits/idac
codex plugin add idac@idac
Once installed, the skill loads automatically when relevant. The repository contains one canonical plugin package, with no client-specific compatibility package or manual skill-link fallback.
Run idac doctor after updating the CLI or plugin to check that their versions match.
It reads the installed plugin inventories through codex plugin list --json and
claude plugin list --json when those clients are available on PATH. Version
mismatches are warnings; the Agent Plugin is optional and does not affect CLI health.
For a ready-to-fill task prompt covering anything from a light analysis pass to class-family recovery, run idac workspace init <dir> to scaffold a workspace containing prompts/recovery-pass.md.
Development
git clone https://github.com/trailofbits/idac.git
cd idac
uv sync # project venv with an editable install; run via `uv run idac ...`
make test # run tests
make check # formatting checks + lint + test + audit
To put an idac on your PATH that tracks your checkout, install it as editable:
uv tool install -e .
See docs/development.md for fixture regeneration, Nexus integration tests, and local tooling details.
Credits
Inspired by @banteg's bn Binary Ninja CLI tool.
Backend integration is provided by
ida-nexus.
Written by Codex/gpt-5.3-codex/gpt-5.4/gpt-5.5.
Metadata
Release files for idac 0.20.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| idac-0.20.0.tar.gz | 129.4 kB | With provenance | Details for idac-0.20.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| idac-0.20.0-py3-none-any.whl | Python 3 | none | any | Details for idac-0.20.0-py3-none-any.whl |
Total release size: 270.8 kB
Release files / idac-0.20.0.tar.gz
| Download URL | idac-0.20.0.tar.gz |
|---|---|
| Size | 129.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
77b61e1b7e2f386827a8521b7d78843fdf318bf299d8bb29194b952cf4c87a1e
|
|
BLAKE2b-256 checksum How to use checksums |
d0ddbb89d5cf016bbc2170a1cec73176a817e7a858161cbc97f7bddc21cd12f7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.
Transparency logRelease files / idac-0.20.0-py3-none-any.whl
| Download URL | idac-0.20.0-py3-none-any.whl |
|---|---|
| Size | 141.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b7c7c41ef8296c0a96ce317fe743d24ca0a8b9fe934e2462fb30b2c0954d3813
|
|
BLAKE2b-256 checksum How to use checksums |
9d9d3eae6cb25079dc4d3f95f7c25788afe398a6897073875649a0dd776089d4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.
Transparency logSecurity for idac 0.20.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for idac-0.20.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.