Implement the Dual-API strategy for IDAPython development, choosing between modern IDA Domain API and legacy SDK. Use when writing IDA Pro plugins, migrating legacy IDAPython code, designing API abstractions, or deciding between ida_domain vs ida_* modules.
Strategic guidance for co-existing Domain API (primary) and legacy IDAPython SDK (secondary).
Domain API = Primary, SDK = Secondary
The Domain API offers:
Legacy SDK offers:
Function Iteration (Modern):
from ida_domain import Database
with Database() as db:
for func in db.functions:
print(f"{func.name}: {len(list(func.instructions))} instructions")
cfg = func.get_flowchart()
for block in cfg.blocks:
print(f"Block {block.id}: {block.start_ea:#x}-{block.end_ea:#x}")
Cross-Reference Analysis:
from ida_domain import xrefs, functions
func = functions.at(0x401000)
for xref in func.xrefs_to:
if xref.type.is_call:
caller = functions.containing(xref.from_ea)
print(f"Called by {caller.name} at {xref.from_ea:#x}")
Required Documentation Template for every SDK call:
# LEGACY SDK USAGE: [reason for not using Domain API]
# Domain API equivalent: [which Domain API would be used if available]
# Geplande migratie: [issue/version when migration expected]
# Zie: [link to documentation]
import ida_bytes # noqa: I001 - legacy import separated
def read_bytes_legacy(ea: int, size: int) -> bytes:
"""Read raw bytes via legacy SDK.
LEGACY SDK USAGE: ida_bytes.get_bytes provides direct buffer access
without Domain API overhead for bulk operations.
Domain API equivalent: db.bytes.read(ea, size) - 15% slower in benchmarks
Geplande migratie: N/A - performance-critical path
Zie: docs/legacy_usage.md#byte-operations
"""
return ida_bytes.get_bytes(ea, size)
Use explicit adapters for safe API bridging:
from dataclasses import dataclass
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from ida_domain import Function
import ida_funcs
@dataclass(frozen=True)
class FunctionAdapter:
"""Adapter for safe interoperability between Domain API and legacy SDK."""
domain_func: "Function"
_legacy_func: "ida_funcs.func_t | None" = None
@property
def legacy_func(self) -> "ida_funcs.func_t":
"""Lazy initialization of legacy func_t structure."""
if self._legacy_func is None:
import ida_funcs
self._legacy_func = ida_funcs.get_func(self.domain_func.start_ea)
if self._legacy_func is None:
raise RuntimeError(f"Legacy func_t not available for {self.domain_func.name}")
return self._legacy_func
Core Reverse Engineering Modules:
functions - Function, FunctionIterator, FlowChart, CFG constructionxrefs - Xref, XrefType, XrefGraph, call graph constructiontypes - Type, TypeLibrary, Struct, Union, type reconstructionflowchart - BasicBlock, Edge, DominatorTree, control flow analysisstrings - String, StringEncoding, StringFinderSupporting Modules:
hooks - Event registration, IDBHook for reactive pluginscomments - Annotation managementnames - Symbolic naming, demanglingsegments - Memory segments, permission checkingdatabase - Global database operations, persistenceAdvanced Modules:
instructions - Instruction decomposition, operand analysisoperands - Detailed operand semantics, addressing modesbytes - Raw byte access with contextsignature_files - FLIRT signature managemententries - Entry point enumeration