Write and execute Binary Ninja Python scripts — full API reference included
print() for all output — it's captured and returned to you.None returns (e.g., bv.get_function_at() returns None if no function at that address).bv.update_analysis_and_wait() after bulk modifications (defining types, creating functions).define_user_*, add_user_*) persist to the database; auto types may be overwritten by analysis._user_ methods.The execute_python tool provides:
bv — the active BinaryViewbinaryninja — the full modulebinaryninjabinaryninjaui — UI module (if available)current_address — cursor address (int, 0 if unavailable)data = bv.read(addr, length) # raw bytes
val = bv.read32(addr) # 32-bit int
ptr = bv.read_pointer(addr) # pointer-sized int
strings = bv.get_strings() # all StringReference objects
s = bv.get_string_at(addr) # single string
func = bv.get_function_at(addr) # exact start
funcs = bv.get_functions_containing(addr) # containing addr
funcs = bv.get_functions_by_name("main") # by name (list)
bv.add_user_function(addr) # create function
func.name = "NewName" # rename
func.type = Type.function(ret, params) # retype
func.set_comment_at(addr, "note") # comment
# Three levels: llil < mlil < hlil (prefer hlil for analysis)
for inst in func.hlil.instructions:
print(f"{hex(inst.address)}: {inst}")
# SSA form for data flow
defn = func.hlil.ssa_form.get_ssa_var_definition(ssa_var)
uses = func.hlil.ssa_form.get_ssa_var_uses(ssa_var)
# Navigate between levels
llil_inst = func.get_llil_at(addr)
hlil_from_llil = llil_inst.hlil
refs = bv.get_code_refs(addr) # code refs TO addr
refs = bv.get_data_refs(addr) # data refs TO addr
refs = bv.get_code_refs_from(addr) # code refs FROM addr
callers = func.callers # calling functions
callees = func.callees # called functions
from binaryninja import Type, StructureBuilder
# Primitives — ALWAYS use Type.* constructors, never raw strings
Type.int(4, True) # int32_t (signed=True default)
Type.int(4, False) # uint32_t
Type.int(8, False) # uint64_t
Type.int(2, False) # uint16_t
Type.int(1, False) # uint8_t
Type.char() # char (signed byte)
Type.void() # void
Type.bool() # bool
Type.float(4) # float
Type.float(8) # double
# Pointers
Type.pointer(bv.arch, Type.char()) # char*
Type.pointer(bv.arch, Type.void()) # void*
Type.pointer(bv.arch, Type.int(4, False)) # uint32_t*
# char** (pointer-to-pointer)
Type.pointer(bv.arch, Type.pointer(bv.arch, Type.char()))
# Arrays — NEVER use string syntax "uint8_t[256]", it will fail
Type.array(Type.int(1, False), 256) # uint8_t[256]
Type.array(Type.char(), 64) # char[64]
Type.array(Type.int(4, False), 8) # uint32_t[8]
from binaryninja import Type, StructureBuilder
# ✅ Correct — always use Type.* for field types, never strings
s = StructureBuilder.create()
s.append(Type.int(4, False), "a_type") # uint32_t a_type
s.append(Type.int(8, False), "a_val") # uint64_t a_val
s.append(Type.pointer(bv.arch, Type.char()), "name") # char* name
s.append(Type.array(Type.int(1, False), 16), "buf") # uint8_t buf[16]
bv.define_user_type("MyStruct", Type.structure_type(s))
# ❌ WRONG — string types silently fail (fields are dropped without error!)
s.append("uint32_t", "field") # SILENT DROP
s.append("uint8_t[16]", "buf") # PARSER ERROR or SILENT DROP
# Dependency order: define inner structs FIRST
s_inner = StructureBuilder.create()
s_inner.append(Type.int(4, False), "x")
s_inner.append(Type.int(4, False), "y")
bv.define_user_type("Entry", Type.structure_type(s_inner))
# Then reference with named_type_from_registered_type
s_outer = StructureBuilder.create()
entry_ref = Type.named_type_from_registered_type(bv, "Entry")
s_outer.append(entry_ref, "entry") # Entry entry
s_outer.append(Type.array(entry_ref, 32), "tbl") # Entry tbl[32]
bv.define_user_type("Table", Type.structure_type(s_outer))
# ALWAYS validate after defining — never assume it worked
t = bv.get_type_by_name("MyStruct")
if t is None:
print("ERROR: type not registered")
elif t.width == 0:
print("ERROR: empty struct — field types were likely rejected")