Binary Ninja provides powerful APIs for automating binary analysis tasks. This guide walks through common analysis workflows with complete working examples from actual Binary Ninja source code.
Extract essential information about a binary, including architecture, entry point, functions, and strings.
1
Load the Binary
Use binaryninja.load() to open a binary file. This returns a BinaryView object that provides access to all analysis features.
from binaryninja import load, LogLevel, log_to_stdout# Enable info logging to consolelog_to_stdout(LogLevel.InfoLog)# Load the binary with context manager for automatic cleanupwith load("/path/to/binary") as bv: # Analysis happens here pass
Using a context manager (with statement) ensures the file is properly closed, preventing memory leaks from circular references between BinaryViews and FileMetadata.
2
Access Binary Properties
The BinaryView provides direct access to key binary properties.
Analyze functions at different levels: machine code, Low Level IL, or higher representations.
Machine Code
Low Level IL
from binaryninja import load, log_infowith load(target) as bv: for func in bv.functions: log_info(repr(func)) for block in func: log_info(f"\t{block}") for insn in block: log_info(f"\t\t{insn}")
This iterates through functions at the disassembly level, showing actual machine instructions.
from binaryninja import load, log_infowith load(target) as bv: for func in bv.functions: log_info(repr(func)) for block in func.low_level_il: log_info(f"\t{block}") for insn in block: log_info(f"\t\t{insn}")
Low Level IL provides a normalized representation that’s easier to analyze programmatically.
Identify and extract system call numbers from binaries. This example demonstrates IL operation filtering and value tracking.
1
Get System Call Convention
from binaryninja import loadbv = load(fileName)calling_convention = bv.platform.system_call_conventionif calling_convention is None: print(f'Error: No syscall convention available for {bv.platform}') return# Get the register that holds the syscall numberregister = calling_convention.int_arg_regs[0]
2
Filter for SYSCALL Instructions
from itertools import chainfrom binaryninja.enums import LowLevelILOperationfor func in bv.functions: # Create generator that finds all SYSCALL operations syscalls = ( il for il in chain.from_iterable(func.low_level_il) if il.operation == LowLevelILOperation.LLIL_SYSCALL ) for il in syscalls: # Process each syscall pass
Using generators and chain.from_iterable() efficiently processes IL instructions across all basic blocks.
3
Extract Syscall Numbers
for il in syscalls: # Get the value of the syscall number register at this instruction value = func.get_reg_value_at(il.address, register).value print(f"System call address: {il.address:#x} - {value:d}")
from itertools import chainfrom binaryninja import loadfrom binaryninja.enums import LowLevelILOperationdef print_syscalls(fileName): """Print Syscall numbers for a provided file""" bv = load(fileName) calling_convention = bv.platform.system_call_convention if calling_convention is None: print(f'Error: No syscall convention available for {bv.platform}') return register = calling_convention.int_arg_regs[0] for func in bv.functions: syscalls = ( il for il in chain.from_iterable(func.low_level_il) if il.operation == LowLevelILOperation.LLIL_SYSCALL ) for il in syscalls: value = func.get_reg_value_at(il.address, register).value print(f"System call address: {il.address:#x} - {value:d}")if __name__ == "__main__": import sys if len(sys.argv) != 2: print(f'Usage: {sys.argv[0]} <file>') else: print_syscalls(sys.argv[1])
Binary Ninja’s value tracking lets you determine register values at specific program points.
from binaryninja import RegisterValueType# Get value at a specific addressvalue = func.get_reg_value_at(address, "rax", arch)if value.type == RegisterValueType.ConstantValue: print(f"rax = {value.value:#x}")elif value.type == RegisterValueType.UndeterminedValue: # Value cannot be determined statically # Try getting possible values from IL instr = func.low_level_il.get_instruction_start(address, arch) if instr is not None: possible = func.low_level_il[instr].get_possible_reg_values("rax") print(f"rax possible values: {possible}")# Get value after an instruction executesvalue_after = func.get_reg_value_after(address, "rax", arch)
See the Data Flow Analysis guide for more advanced value tracking techniques.