Overview
A BinaryView is Binary Ninja’s core abstraction for representing and analyzing binary data. It provides a queryable interface to binary files, allowing you to inspect code, data, segments, sections, functions, and more. Think of it as a lens through which you examine different aspects of a binary.
Binary Views are layered - you can have multiple views of the same file (e.g., Raw, PE, ELF, Mach-O), each providing different levels of abstraction and information.
Key Concepts
View Hierarchy
Binary Ninja uses a layered approach to binary analysis:
- Raw View - The most basic view, representing raw binary data
- Format Views - Architecture-specific parsers (PE, ELF, Mach-O, etc.)
- Mapped Views - Views that remap or transform data
Each layer builds upon the previous one, providing progressively more semantic information.
Creating and Opening BinaryViews
import binaryninja
from binaryninja import load
# Load a binary file
bv = load("/path/to/binary")
# Load with specific options
bv = load("/path/to/binary", options={
'analysis.mode': 'basic',
'analysis.linearSweep.autorun': False
})
# Access the raw view
raw_view = bv.parent_view
# Get the view type name
print(bv.view_type) # e.g., "PE", "ELF", "Mach-O"
Core Properties
# File and view information
print(f"File: {bv.file.filename}")
print(f"View Type: {bv.view_type}")
print(f"Architecture: {bv.arch.name}")
print(f"Platform: {bv.platform.name}")
# Address information
print(f"Start: 0x{bv.start:x}")
print(f"Entry Point: 0x{bv.entry_point:x}")
print(f"Length: {len(bv)}")
Endianness
from binaryninja.enums import Endianness
# Check endianness
if bv.endianness == Endianness.LittleEndian:
print("Little endian")
else:
print("Big endian")
Reading and Writing Data
Reading Binary Data
# Read raw bytes
data = bv.read(address, length)
# Read specific data types
value_8 = bv.read8(address) # Read 1 byte
value_16 = bv.read16(address) # Read 2 bytes
value_32 = bv.read32(address) # Read 4 bytes
value_64 = bv.read64(address) # Read 8 bytes
# Read with endianness handling
value = bv.read_le16(address) # Little endian 16-bit
value = bv.read_be32(address) # Big endian 32-bit
Writing Binary Data
# Write raw bytes
bv.write(address, b"\x90\x90\x90\x90")
# Write specific data types
bv.write8(address, 0x90)
bv.write16(address, 0x9090)
bv.write32(address, 0x90909090)
# Insert or remove bytes
bv.insert(address, b"\x00" * 16) # Insert 16 null bytes
bv.remove(address, 16) # Remove 16 bytes
Modifying binary data affects analysis. Binary Ninja will automatically update analysis when data changes, but you may need to reanalyze functions or trigger updates manually.
Segments and Sections
Segments
Segments define memory regions with specific permissions:
# List all segments
for segment in bv.segments:
perms = ""
perms += "r" if segment.readable else "-"
perms += "w" if segment.writable else "-"
perms += "x" if segment.executable else "-"
print(f"Segment: 0x{segment.start:x}-0x{segment.end:x} {perms}")
# Get segment at address
segment = bv.get_segment_at(address)
if segment:
print(f"Address 0x{address:x} is in segment: {segment}")
# Add custom segment
from binaryninja.enums import SegmentFlag
flags = SegmentFlag.SegmentReadable | SegmentFlag.SegmentExecutable
bv.add_user_segment(start=0x1000, length=0x1000,
data_offset=0, data_length=0x1000,
flags=flags)
Sections
Sections provide semantic meaning to regions (e.g., .text, .data, .bss):
# List all sections
for section in bv.sections:
print(f"Section '{section.name}': 0x{section.start:x}-0x{section.end:x}")
print(f" Type: {section.semantics}")
# Get section by name
text_section = bv.get_section_by_name(".text")
if text_section:
print(f".text at 0x{text_section.start:x}")
Functions
Accessing Functions
# Get all functions
for func in bv.functions:
print(f"Function: {func.name} at 0x{func.start:x}")
# Get function at address
func = bv.get_function_at(address)
# Get functions containing an address
funcs = bv.get_functions_containing(address)
# Get function by name
func = bv.get_function_by_name("main")
Creating Functions
# Add function at address
bv.add_function(address)
# Create function with specific platform
bv.create_user_function(address, plat=bv.platform)
# Remove function
bv.remove_function(func)
Strings
# List all strings
for string in bv.strings:
print(f"0x{string.start:x}: {string.value}")
# Get strings in a range
strings_in_range = bv.get_strings(start_address, length)
# String types include ASCII, UTF-8, UTF-16, UTF-32
from binaryninja.enums import StringType
for s in bv.strings:
if s.type == StringType.AsciiString:
print(f"ASCII string: {s.value}")
Analysis Control
Analysis State
from binaryninja.enums import AnalysisState
# Check analysis state
if bv.analysis_info.state == AnalysisState.IdleState:
print("Analysis complete")
elif bv.analysis_info.state == AnalysisState.InitialState:
print("Analysis not started")
else:
print("Analysis in progress")
# Wait for analysis to complete
bv.update_analysis_and_wait()
# Update analysis for specific function
func.reanalyze()
Analysis Events
from binaryninja import AnalysisCompletionEvent
def on_analysis_complete():
print("Analysis finished!")
# Register completion callback
event = AnalysisCompletionEvent(bv, on_analysis_complete)
Notifications
BinaryView supports event notifications for changes:
from binaryninja import BinaryDataNotification, NotificationType
class MyNotification(BinaryDataNotification):
def __init__(self):
# Subscribe to specific notification types
super().__init__(
NotificationType.FunctionAdded |
NotificationType.DataWritten
)
def function_added(self, view, func):
print(f"Function added: {func.name} at 0x{func.start:x}")
def data_written(self, view, offset, length):
print(f"Data written at 0x{offset:x}, length {length}")
# Register notification
notif = MyNotification()
bv.register_notification(notif)
# Unregister when done
bv.unregister_notification(notif)
Available BinaryView Types
Binary Ninja includes these built-in view types:
- ELF - Executable and Linkable Format (Linux, Unix)
- PE - Portable Executable (Windows)
- Mach-O - Mach Object (macOS, iOS)
- COFF - Common Object File Format
- TE - Terse Executable
- MD1Rom - Sega Genesis/MD ROM format
- Shared Cache - Apple dyld shared cache
- Kernel Cache - Apple kernel cache
- Raw - Raw binary data
Practical Example
Here’s a complete example that demonstrates common BinaryView operations:
import binaryninja
from binaryninja import load
def analyze_binary(filepath):
# Load binary
bv = load(filepath)
print(f"## {bv.file.filename} ##")
print(f"- START: 0x{bv.start:x}")
print(f"- ENTRY: 0x{bv.entry_point:x}")
print(f"- ARCH: {bv.arch.name}")
# Wait for analysis
bv.update_analysis_and_wait()
# Print first 10 functions
print("\n### Functions ###")
for i, func in enumerate(bv.functions[:10]):
print(f"0x{func.start:x} | {func.symbol.full_name}")
# Print first 10 strings
print("\n### Strings ###")
for i, s in enumerate(bv.strings[:10]):
print(f"0x{s.start:x} | {len(s)} | {s.value}")
# Print segments
print("\n### Segments ###")
for segment in bv.segments:
r = "r" if segment.readable else "-"
w = "w" if segment.writable else "-"
x = "x" if segment.executable else "-"
print(f"0x{segment.start:x}-0x{segment.end:x} {r}{w}{x}")
bv.file.close()
if __name__ == "__main__":
analyze_binary("/path/to/binary")