# idax
[](https://crates.io/crates/idax)
[](https://docs.rs/idax)
[](LICENSE)
Safe, idiomatic Rust bindings for the [IDA Pro](https://hex-rays.com/ida-pro/) SDK via the [idax](https://github.com/19h/idax) C++ wrapper library.
`idax` provides a concept-driven, fully opaque interface to IDA's analysis engine. All unsafe FFI is encapsulated — users of this crate never write `unsafe` code. Types that hold SDK resources implement `Drop` for automatic cleanup.
## Prerequisites
- **IDA Pro** installed in a standard location (see [IDA discovery](#ida-discovery) below)
- **Rust 2024 edition** (nightly or stable 1.85+)
- **CMake** and a C++23 compiler (the build script compiles the C++ layer automatically)
## Installation
```toml
[dependencies]
idax = "0.3"
```
That's it. No `build.rs` in your crate, no environment variables, no manual library setup. Just `cargo run`.
## IDA discovery
The build script automatically locates your IDA installation:
1. **`$IDADIR`** environment variable (explicit override)
2. **macOS**: scans `/Applications/IDA*.app/Contents/MacOS` (newest version first)
3. **Linux**: scans `/opt/idapro*`, `/opt/ida-*`, `/opt/ida`, then `~/ida*`
If the IDA SDK isn't available locally, it will be fetched automatically during the build. You can override this with the `$IDASDK` environment variable.
## Runtime: `cargo run` vs direct execution
**`cargo run` / `cargo test`** — works out of the box. The build script symlinks the real IDA dynamic libraries into the build output directory, and Cargo automatically adds that directory to the dynamic library search path (`DYLD_FALLBACK_LIBRARY_PATH` on macOS, `LD_LIBRARY_PATH` on Linux).
**Direct execution** (running the compiled binary outside of Cargo) requires the IDA libraries to be discoverable by the dynamic linker. Options:
```bash
# Option 1: Set the library path (recommended for development)
DYLD_LIBRARY_PATH=/Applications/IDA\ Professional\ 9.3.app/Contents/MacOS ./target/release/my-tool # macOS
LD_LIBRARY_PATH=/opt/idapro ./target/release/my-tool # Linux
# Option 2: Add an RPATH to the binary (recommended for deployment)
install_name_tool -add_rpath /Applications/IDA\ Professional\ 9.3.app/Contents/MacOS ./target/release/my-tool # macOS
patchelf --add-rpath /opt/idapro ./target/release/my-tool # Linux
# Option 3: Place the binary next to the IDA libraries
cp ./target/release/my-tool /Applications/IDA\ Professional\ 9.3.app/Contents/MacOS/
```
## Quick start
```rust
use idax::{database, function, segment};
fn main() -> idax::Result<()> {
// Initialize the IDA library
database::init()?;
// Open a binary for analysis
database::open("/path/to/binary", true)?;
// Query metadata
let path = database::input_file_path()?;
let md5 = database::input_md5()?;
println!("Analyzing: {path} (MD5: {md5})");
// Iterate functions
let count = function::count()?;
for i in 0..count {
let func = function::by_index(i)?;
println!(" {:#x}: {}", func.start, func.name);
}
// Iterate segments
let seg_count = segment::count()?;
for i in 0..seg_count {
let seg = segment::by_index(i)?;
println!(" {}: {:#x}..{:#x}", seg.name, seg.start, seg.end);
}
database::close(false)?;
Ok(())
}
```
## Module overview
The crate is organized into modules that mirror the C++ `ida::` namespace hierarchy. Every module covers a distinct analysis domain:
### Core
| [`error`] | Error handling | `Error` (category + code + message + context), `Result<T>`, `Status` |
| [`address`] | Address primitives | `Address` (`u64`), `Range`, predicates (`is_code`, `is_data`, ...), navigation (`next_head`, `prev_head`), iterators (`HeadIterator`, `PredicateIterator`) |
| [`database`] | Database lifecycle | `init`, `open`, `open_binary`, `save`, `close`, metadata queries (`input_file_path`, `input_md5`, `image_base`, `processor_name`, ...), import enumeration, snapshots |
### Analysis objects
| [`segment`] | Segments | CRUD (`at`, `by_name`, `by_index`, `create`, `remove`), properties (`set_name`, `set_permissions`, `set_bitness`, `resize`, `move`), traversal (`next`, `prev`), comments |
| [`function`] | Functions | CRUD, chunks (`chunks`, `add_tail`, `remove_tail`), stack frames (`frame`, `frame_variable_by_name`, `define_stack_variable`), register variables, callers/callees, `item_addresses`, `code_addresses` |
| [`instruction`] | Instructions | `decode`, `create`, `text`, operand introspection (`operand_text`, `operand_byte_width`, `operand_register_name`), operand formatting (`set_operand_hex`, `set_operand_offset`, `set_operand_struct_offset_*`), xref conveniences (`code_refs_from`, `call_targets`, `is_call`, `is_jump`), navigation (`next`, `prev`) |
| [`data`] | Byte-level I/O | Read (`read_byte` .. `read_qword`, `read_bytes`, `read_string`, `read_typed`), write, patch, originals, define (`define_byte` .. `define_struct`, `undefine`), `find_binary_pattern` |
| [`name`] | Naming | `get`, `set`, `force_set`, `remove`, `demangled`, `resolve`, `all_user_defined`, properties (`is_public`, `is_weak`) |
| [`xref`] | Cross-references | `refs_from`, `refs_to`, code/data ref variants, range variants, `add_code`, `add_data`, `remove_code`, `remove_data` |
| [`comment`] | Comments | Regular/repeatable (`get`, `set`, `append`, `remove`), anterior/posterior lines (`add_anterior`, `set_anterior_lines`, `anterior_lines`, ...), `render` |
| [`entry`] | Entry points | `count`, `by_index`, `by_ordinal`, `add`, `rename`, forwarders |
| [`fixup`] | Fixups / relocations | `at`, `set`, `remove`, `in_range`, iteration (`first`, `next`, `prev`), custom fixup handler registration |
| [`search`] | Search | `text`, `binary_pattern`, `immediate`, `next_code`, `next_data`, `next_unknown`, `next_error`, `next_defined` |
### Type system
| [`types`] | Type introspection & construction | `TypeInfo` (RAII handle with `Drop`), primitives (`void`, `int8` .. `uint64`, `float32`, `float64`), compound types (`pointer_to`, `array_of`, `create_struct`, `create_union`, `function_type`, `enum_type`), introspection (`is_pointer`, `pointee_type`, `members`, ...), application (`apply`, `retrieve`, `save_as`), type libraries (`load_library`, `import`) |
### Advanced
| [`decompiler`] | Hex-Rays decompiler | `decompile` / `decompile_range` returning `DecompiledFunction` (RAII), pseudocode (`pseudocode_lines`, `pseudocode_text`), ctree traversal (`ctree_items`, `find_ctree_item_at`), microcode (`microcode` returning `MicrocodeFunction`), ctree/microcode modification, event subscriptions (`on_decompiled`, `on_ctree_modified`, ...) |
| [`debugger`] | Debugger control | Process lifecycle (`start`, `attach`, `detach`, `suspend`, `resume`, `step_*`, `terminate`), breakpoints (`add_breakpoint`, `remove_breakpoint`, `enable_breakpoint`, `breakpoints`), memory (`read_memory`, `write_memory`), registers (`register_value`, `set_register_value`), threads, appcall (`call_function`), module/exception/event subscriptions, custom executor registration |
| [`storage`] | Netnode storage | `Node` (RAII handle), typed value stores: altval (`set_altval` / `altval`), supval (`set_supval` / `supval`), hashval (`set_hashval` / `hashval`), blob (`set_blob` / `blob`), `create` / `open` / `remove` |
| [`lumina`] | Lumina server | `pull`, `push` |
| [`analysis`] | Auto-analysis | `is_enabled`, `set_enabled`, `is_idle`, `wait`, `schedule`, `schedule_range`, `schedule_function`, `cancel`, `revert_decisions` |
| [`event`] | IDB event subscriptions | Typed callbacks for segment/function/rename/patch/comment events, filtered subscriptions, `ScopedSubscription` (RAII unsubscribe on drop) |
### Extension points
| [`plugin`] | Plugin lifecycle | Action registration (`register_action` / `register_action_ex`), menu/toolbar/popup attachment, `ActionContext` for context-aware handlers |
| [`loader`] | Loader modules | `InputFileHandle` (seek, read, filename), `LoadFlags` decode/encode, `file_to_database`, `memory_to_database`, `set_processor`, `abort_load` |
| [`processor`] | Processor modules | `Processor` trait (5 required + 15 optional methods), `InstructionFeature` / `RegisterInfo` / `AssemblerInfo` types |
| [`graph`] | Custom graphs | `Graph` (RAII handle), `GraphCallback` trait for interactive event handling, `flow_chart` for function CFG extraction |
| [`ui`] | UI utilities | `message`, `warning`, `error`, `info` dialogs, `ask_*` input prompts, `ChooserImpl` trait for custom list dialogs, widget management, timer scheduling, clipboard, UI event subscriptions |
| [`lines`] | Color tags | `strip_color_tags`, `has_color_tags` |
| [`diagnostics`] | Logging | `log`, `log_error`, `performance_counter`, `reset_performance_counter`, `dump_performance_counters`, `is_verbose`, `set_verbose` |
## Error handling
All fallible operations return `idax::Result<T>` (alias for `std::result::Result<T, idax::Error>`) or `idax::Status` (alias for `Result<()>`). The `Error` type carries:
- **`category`** — `ErrorCategory` enum: `Validation`, `NotFound`, `Conflict`, `Unsupported`, `SdkFailure`, `Internal`
- **`code`** — numeric error code (0 when unspecified)
- **`message`** — human-readable description
- **`context`** — additional context (e.g. which SDK function failed)
```rust
use idax::{function, Error};
use idax::error::ErrorCategory;
match function::at(0xDEAD) {
Ok(func) => println!("Found: {}", func.name),
Err(e) if e.category == ErrorCategory::NotFound => {
println!("No function at that address");
}
Err(e) => return Err(e),
}
```
## RAII / Drop
Types that hold SDK resources implement `Drop` for automatic cleanup:
| `TypeInfo` | `types` | Opaque type handle |
| `Node` | `storage` | Netnode handle |
| `DecompiledFunction` | `decompiler` | Decompilation result |
| `Graph` | `graph` | Interactive graph handle |
| `ScopedSubscription` | `event`, `decompiler`, `debugger` | Unsubscribes on drop |
## Architecture
```
Your Rust code
|
[ idax ] safe, idiomatic Rust API
|
[ idax-sys ] raw extern "C" FFI bindings (generated by bindgen)
|
[ C shim ] idax_shim.h / idax_shim.cpp (thin C bridge)
|
[ libidax.a ] idax C++ wrapper library
|
[ IDA SDK ] ida.dylib / ida.so / ida.dll
```
## License
MIT