name: IDA Scripting description: Write and execute IDAPython scripts — full API reference included tags: [scripting, ida, python, automation] author: Rikugan version: 1.0
Task: Help the user write IDAPython scripts. You have execute_python which runs code with all ida_* modules, idaapi, idautils, and idc pre-loaded.
Guidelines
- Use
print()for all output — it's captured and returned to you. - Prefer the IDAPython API over raw tool calls when the task requires iteration, filtering, or complex logic that a single tool can't express.
- Always handle
None/BADADDRreturns (e.g.,ida_funcs.get_func()returnsNoneif no function). - Use
ida_auto.auto_wait()after bulk modifications to let auto-analysis settle. - For large outputs, summarize or paginate — don't dump thousands of lines.
- Call
ida_hexrays.mark_cfunc_dirty(ea)before re-decompiling a function you've modified. - IDA 9+ removed
ida_structandida_enum— useida_typeinffor all type operations.
Environment
The execute_python tool provides:
idaapi,idautils,idc— high-level wrappersida_funcs,ida_name,ida_bytes,ida_segment,ida_typeinf,ida_nalt,ida_xref,ida_kernwin,ida_hexrays,ida_lines,ida_search,ida_ida,ida_entry,ida_frame,ida_auto,ida_gdl,ida_netnode— low-level modules- Full Python stdlib (except subprocess/os.exec — blocked for safety)
BADADDRis available viaidaapi.BADADDR
Quick Reference
Reading Data
val = ida_bytes.get_byte(ea) # uint8
val = ida_bytes.get_dword(ea) # uint32
val = ida_bytes.get_qword(ea) # uint64
raw = ida_bytes.get_bytes(ea, size) # bytes
s = ida_bytes.get_strlit_contents(ea, -1, ida_nalt.STRTYPE_C)
Functions
for func_ea in idautils.Functions():
name = idc.get_func_name(func_ea)
func = ida_funcs.get_func(ea) # func_t or None
# func.start_ea, func.end_ea, func.flags
idc.add_func(ea) # create function
ida_name.set_name(ea, "new_name", ida_name.SN_CHECK)
Decompiler (Hex-Rays)
cfunc = ida_hexrays.decompile(ea) # cfuncptr_t
pseudocode = cfunc.get_pseudocode() # simpleline_t vector
for line in pseudocode:
print(ida_lines.tag_remove(line.line))
# Local variables
for lvar in cfunc.get_lvars():
print(f"{lvar.name}: {lvar.type()}")
# CTree visitor
class MyVisitor(ida_hexrays.ctree_visitor_t):
def __init__(self):
super().__init__(ida_hexrays.CV_FAST)
def visit_expr(self, expr):
if expr.op == ida_hexrays.cot_call:
print(f"Call at {hex(expr.ea)}")
return 0
visitor = MyVisitor()
visitor.apply_to(cfunc.body, None)
Cross-References
for xref in idautils.XrefsTo(ea):
print(f"from {hex(xref.frm)}, type={xref.type}")
for xref in idautils.XrefsFrom(ea):
print(f"to {hex(xref.to)}")
for ref in idautils.CodeRefsTo(ea, False): # False = no flow
print(hex(ref))
for ref in idautils.DataRefsTo(ea):
print(hex(ref))
Types (IDA 9+)
Critical: udm_t.offset is in BITS, not bytes. udm_t.size is also in BITS.
import ida_typeinf
# Helper: make a simple integer tinfo_t
def make_int_type(byte_size: int, signed: bool = False) -> ida_typeinf.tinfo_t:
bt_map = {
(1, False): ida_typeinf.BT_INT8,
(1, True): ida_typeinf.BT_INT8,
(2, False): ida_typeinf.BT_INT16,
(2, True): ida_typeinf.BT_INT16,
(4, False): ida_typeinf.BT_INT32,
(4, True): ida_typeinf.BT_INT32,
(8, False): ida_typeinf.BT_INT64,
(8, True): ida_typeinf.BT_INT64,
}
t = ida_typeinf.tinfo_t()
t.create_simple_type(bt_map[(byte_size, signed)])
return t
# Helper: make a pointer type
def make_ptr_type(inner: ida_typeinf.tinfo_t) -> ida_typeinf.tinfo_t:
pd = ida_typeinf.ptr_type_data_t()
pd.obj_type = inner
t = ida_typeinf.tinfo_t()
t.create_ptr(pd)
return t
# Helper: make an array type
def make_array_type(elem: ida_typeinf.tinfo_t, count: int) -> ida_typeinf.tinfo_t:
ad = ida_typeinf.array_type_data_t()
ad.elem_type = elem
ad.nelems = count
t = ida_typeinf.tinfo_t()
t.create_array(ad)
return t
# Build struct — offsets in BITS
def make_struct(name: str, fields: list) -> ida_typeinf.tinfo_t:
"""fields = [(name, tinfo_t, byte_offset), ...]"""
udt = ida_typeinf.udt_type_data_t()
for fname, ftype, byte_off in fields:
udm = ida_typeinf.udm_t()
udm.name = fname
udm.type = ftype
udm.offset = byte_off * 8 # ← BITS, not bytes
udt.push_back(udm)
tif = ida_typeinf.tinfo_t()
tif.create_udt(udt, ida_typeinf.BTF_STRUCT)
tif.set_named_type(None, name, ida_typeinf.NTF_REPLACE)
return tif
# Example: struct Elf32_auxv_entry { uint32_t a_type; uint32_t a_val; }
auxv_tif = make_struct("Elf32_auxv_entry", [
("a_type", make_int_type(4, False), 0),
("a_val", make_int_type(4, False), 4),
])
# Example: struct with pointer and array fields
char_ptr = make_ptr_type(make_int_type(1)) # char*
char_pp = make_ptr_type(char_ptr) # char**
u8_arr = make_array_type(make_int_type(1), 16) # uint8_t[16]
make_struct("StartupInfo", [
("argv", char_pp, 0),
("buf", u8_arr, 8),
])
# Reference a previously defined struct by name
def get_named_type(name: str) -> ida_typeinf.tinfo_t:
t = ida_typeinf.tinfo_t()
if not t.get_named_type(None, name):
raise ValueError(f"Type '{name}' not found in type library")
return t
# Apply type to address
ida_typeinf.apply_tinfo(ea, auxv_tif, ida_typeinf.TINFO_DEFINITE)
# Validate after creation
t = ida_typeinf.tinfo_t()
if t.get_named_type(None, "Elf32_auxv_entry"):
print(f"OK: {t.dstr()}, size={t.get_size()} bytes")
else:
print("ERROR: type not registered")
# Simpler alternative for well-known C types: parse C declaration directly
# Works well for types IDA already knows; avoid for custom typedefs
ida_typeinf.apply_cdecl(None, ea, "int __cdecl func(int a, char *b)")
# Create enum
edt = ida_typeinf.enum_type_data_t()
for vname, vval in [("VAL_A", 0), ("VAL_B", 1)]:
edm = ida_typeinf.edm_t()
edm.name = vname; edm.value = vval
edt.push_back(edm)
tif_enum = ida_typeinf.tinfo_t()
tif_enum.create_enum(edt)
tif_enum.set_named_type(None, "MyEnum", ida_typeinf.NTF_REPLACE)
Segments & Strings
for seg_ea in idautils.Segments():
seg = ida_segment.getseg(seg_ea)
print(f"{ida_segment.get_segm_name(seg)}: {hex(seg.start_ea)}-{hex(seg.end_ea)}")
for s in idautils.Strings():
refs = list(idautils.DataRefsTo(s.ea))
print(f"'{s}' @ {hex(s.ea)}, refs={len(refs)}")
Names & Comments
for ea, name in idautils.Names():
print(f"{hex(ea)}: {name}")
ida_name.set_name(ea, "my_label", ida_name.SN_CHECK)
idc.set_cmt(ea, "note", 0) # 0=regular, 1=repeatable
idc.set_func_cmt(ea, "description", 0)
Search
# IDA 9+
ea = ida_bytes.find_bytes(start_ea, "48 8B ?? ?? 90", 0)
# Pattern search loop
ea = start_ea
while ea != idaapi.BADADDR:
ea = ida_search.find_binary(ea, idaapi.BADADDR, "E8 ?? ?? ?? ??", 16,
ida_search.SEARCH_DOWN | ida_search.SEARCH_NEXT)
if ea != idaapi.BADADDR:
print(hex(ea))
Patching
ida_bytes.patch_byte(ea, 0x90) # NOP
ida_bytes.patch_bytes(ea, b"\x90" * 5) # NOP sled
ida_bytes.patch_dword(ea, 0)
UI
ea = ida_kernwin.get_screen_ea()
ida_kernwin.jumpto(target_ea)
ida_kernwin.msg("Info message\n")
name = ida_kernwin.ask_str("default", 0, "Enter name:")
choice = ida_kernwin.ask_yn(1, "Sure?")
ida_kernwin.show_wait_box("Processing...")
# ... work ...
ida_kernwin.hide_wait_box()
Control Flow Graph
func = ida_funcs.get_func(ea)
fc = ida_gdl.FlowChart(func)
for block in fc:
print(f"Block {hex(block.start_ea)}-{hex(block.end_ea)}")
for succ in block.succs():
print(f" -> {hex(succ.start_ea)}")
Common Patterns
# Find all callers of a function
target = ida_name.get_name_ea(idaapi.BADADDR, "malloc")
if target != idaapi.BADADDR:
for ref in idautils.CodeRefsTo(target, False):
func = ida_funcs.get_func(ref)
if func:
print(f"Called from {idc.get_func_name(func.start_ea)} @ {hex(ref)}")
# Batch decompile
for func_ea in idautils.Functions():
try:
cfunc = ida_hexrays.decompile(func_ea)
text = "\n".join(ida_lines.tag_remove(l.line) for l in cfunc.get_pseudocode())
except ida_hexrays.DecompilationFailure:
pass
# Persistent storage via netnodes
node = ida_netnode.netnode("$my_data", 0, True)
node.hashset("key", "value")
val = node.hashstr("key")
Important Notes
BADADDR=0xFFFFFFFF(32-bit) or0xFFFFFFFFFFFFFFFF(64-bit) — always compare against it.ida_hexrays.decompile()can raiseDecompilationFailure— always wrap in try/except.- IDA 9 removed
ida_struct/ida_enum— useida_typeinfwithudt_type_data_t/udm_t/enum_type_data_t/edm_t. udm_t.offsetis in BITS, not bytes — always multiply byte offset by 8.- Never use
tinfo_t(BT_INT32)constructor — callt.create_simple_type(BT_INT32)explicitly; the single-arg constructor is unreliable across IDA versions. - Always pass
NTF_REPLACEtoset_named_type()when redefining a type; omitting it may silently fail on re-runs. - Validate after creation — call
t.get_named_type(None, name)andt.dstr()to confirm the type registered correctly. - Dependency order — define inner struct types before outer ones; look them up with
get_named_type()not by rawtinfo_t. ida_bytes.get_strlit_contents()returnsbytes, notstr— decode with.decode('utf-8', errors='replace').- Process execution (subprocess, os.system, etc.) is blocked. Static analysis only.