Skip to main content

idac

version python status

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.

idac is 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

  • 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 the idac Agent Plugin teaches compatible agents to drive idac instead 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.2 and ida-domain>=0.5.1, installed as dependencies of idac. Install the matching GUI component with idac setup gui when 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)

Source distribution for idac 0.20.0
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)

Table of built distributions (wheels) for idac 0.20.0
File Interpreter ABI Platform Actions
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 log

Release 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 log

Security 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.

Select from 2 files

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 log

A 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.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page