Source code for binaryninja.binaryview

# Copyright (c) 2015-2017 Vector 35 LLC
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.

import struct
import traceback
import ctypes
import abc
import threading

# Binary Ninja components
import _binaryninjacore as core
from enums import AnalysisState, SymbolType, InstructionTextTokenType, Endianness, ModificationStatus, StringType, SegmentFlag
import function
import startup
import architecture
import platform
import associateddatastore
import fileaccessor
import filemetadata
import log
import databuffer
import basicblock
import types
import lineardisassembly


[docs]class BinaryDataNotification(object):
[docs] def __init__(self): pass
[docs] def data_written(self, view, offset, length): pass
[docs] def data_inserted(self, view, offset, length): pass
[docs] def data_removed(self, view, offset, length): pass
[docs] def function_added(self, view, func): pass
[docs] def function_removed(self, view, func): pass
[docs] def function_updated(self, view, func): pass
[docs] def data_var_added(self, view, var): pass
[docs] def data_var_removed(self, view, var): pass
[docs] def data_var_updated(self, view, var): pass
[docs] def string_found(self, view, string_type, offset, length): pass
[docs] def string_removed(self, view, string_type, offset, length): pass
[docs] def type_defined(self, view, name, type): pass
[docs] def type_undefined(self, view, name, type): pass
[docs]class StringReference(object):
[docs] def __init__(self, string_type, start, length): self.type = string_type self.start = start self.length = length
def __repr__(self): return "<%s: %#x, len %#x>" % (self.type, self.start, self.length)
[docs]class AnalysisCompletionEvent(object):
[docs] def __init__(self, view, callback): self.view = view self.callback = callback self._cb = ctypes.CFUNCTYPE(None, ctypes.c_void_p)(self._notify) self.handle = core.BNAddAnalysisCompletionEvent(self.view.handle, None, self._cb)
def __del__(self): core.BNFreeAnalysisCompletionEvent(self.handle) def _notify(self, ctxt): try: self.callback() except: log.log_error(traceback.format_exc()) def _empty_callback(self): pass
[docs] def cancel(self): self.callback = self._empty_callback core.BNCancelAnalysisCompletionEvent(self.handle)
[docs]class AnalysisProgress(object):
[docs] def __init__(self, state, count, total): self.state = state self.count = count self.total = total
def __str__(self): if self.state == AnalysisState.DisassembleState: return "Disassembling (%d/%d)" % (self.count, self.total) if self.state == AnalysisState.AnalyzeState: return "Analyzing (%d/%d)" % (self.count, self.total) return "Idle" def __repr__(self): return "<progress: %s>" % str(self)
[docs]class DataVariable(object):
[docs] def __init__(self, addr, var_type, auto_discovered): self.address = addr self.type = var_type self.auto_discovered = auto_discovered
def __repr__(self): return "<var 0x%x: %s>" % (self.address, str(self.type))
[docs]class BinaryDataNotificationCallbacks(object):
[docs] def __init__(self, view, notify): self.view = view self.notify = notify self._cb = core.BNBinaryDataNotification() self._cb.context = 0 self._cb.dataWritten = self._cb.dataWritten.__class__(self._data_written) self._cb.dataInserted = self._cb.dataInserted.__class__(self._data_inserted) self._cb.dataRemoved = self._cb.dataRemoved.__class__(self._data_removed) self._cb.functionAdded = self._cb.functionAdded.__class__(self._function_added) self._cb.functionRemoved = self._cb.functionRemoved.__class__(self._function_removed) self._cb.functionUpdated = self._cb.functionUpdated.__class__(self._function_updated) self._cb.dataVariableAdded = self._cb.dataVariableAdded.__class__(self._data_var_added) self._cb.dataVariableRemoved = self._cb.dataVariableRemoved.__class__(self._data_var_removed) self._cb.dataVariableUpdated = self._cb.dataVariableUpdated.__class__(self._data_var_updated) self._cb.stringFound = self._cb.stringFound.__class__(self._string_found) self._cb.stringRemoved = self._cb.stringRemoved.__class__(self._string_removed) self._cb.typeDefined = self._cb.typeDefined.__class__(self._type_defined) self._cb.typeUndefined = self._cb.typeUndefined.__class__(self._type_undefined)
def _register(self): core.BNRegisterDataNotification(self.view.handle, self._cb) def _unregister(self): core.BNUnregisterDataNotification(self.view.handle, self._cb) def _data_written(self, ctxt, view, offset, length): try: self.notify.data_written(self.view, offset, length) except OSError: log.log_error(traceback.format_exc()) def _data_inserted(self, ctxt, view, offset, length): try: self.notify.data_inserted(self.view, offset, length) except: log.log_error(traceback.format_exc()) def _data_removed(self, ctxt, view, offset, length): try: self.notify.data_removed(self.view, offset, length) except: log.log_error(traceback.format_exc()) def _function_added(self, ctxt, view, func): try: self.notify.function_added(self.view, function.Function(self.view, core.BNNewFunctionReference(func))) except: log.log_error(traceback.format_exc()) def _function_removed(self, ctxt, view, func): try: self.notify.function_removed(self.view, function.Function(self.view, core.BNNewFunctionReference(func))) except: log.log_error(traceback.format_exc()) def _function_updated(self, ctxt, view, func): try: self.notify.function_updated(self.view, function.Function(self.view, core.BNNewFunctionReference(func))) except: log.log_error(traceback.format_exc()) def _data_var_added(self, ctxt, view, var): try: address = var[0].address var_type = types.Type(core.BNNewTypeReference(var[0].type)) auto_discovered = var[0].autoDiscovered self.notify.data_var_added(self.view, DataVariable(address, var_type, auto_discovered)) except: log.log_error(traceback.format_exc()) def _data_var_removed(self, ctxt, view, var): try: address = var[0].address var_type = types.Type(core.BNNewTypeReference(var[0].type)) auto_discovered = var[0].autoDiscovered self.notify.data_var_removed(self.view, DataVariable(address, var_type, auto_discovered)) except: log.log_error(traceback.format_exc()) def _data_var_updated(self, ctxt, view, var): try: address = var[0].address var_type = types.Type(core.BNNewTypeReference(var[0].type)) auto_discovered = var[0].autoDiscovered self.notify.data_var_updated(self.view, DataVariable(address, var_type, auto_discovered)) except: log.log_error(traceback.format_exc()) def _string_found(self, ctxt, view, string_type, offset, length): try: self.notify.string_found(self.view, StringType(string_type), offset, length) except: log.log_error(traceback.format_exc()) def _string_removed(self, ctxt, view, string_type, offset, length): try: self.notify.string_removed(self.view, StringType(string_type), offset, length) except: log.log_error(traceback.format_exc()) def _type_defined(self, ctxt, view, name, type_obj): try: qualified_name = types.QualifiedName._from_core_struct(name[0]) self.notify.type_defined(view, qualified_name, types.Type(core.BNNewTypeReference(type_obj))) except: log.log_error(traceback.format_exc()) def _type_undefined(self, ctxt, view, name, type_obj): try: qualified_name = types.QualifiedName._from_core_struct(name[0]) self.notify.type_undefined(view, qualified_name, types.Type(core.BNNewTypeReference(type_obj))) except: log.log_error(traceback.format_exc())
class _BinaryViewTypeMetaclass(type): @property def list(self): """List all BinaryView types (read-only)""" startup._init_plugins() count = ctypes.c_ulonglong() types = core.BNGetBinaryViewTypes(count) result = [] for i in xrange(0, count.value): result.append(BinaryViewType(types[i])) core.BNFreeBinaryViewTypeList(types) return result def __iter__(self): startup._init_plugins() count = ctypes.c_ulonglong() types = core.BNGetBinaryViewTypes(count) try: for i in xrange(0, count.value): yield BinaryViewType(types[i]) finally: core.BNFreeBinaryViewTypeList(types) def __getitem__(self, value): startup._init_plugins() view_type = core.BNGetBinaryViewTypeByName(str(value)) if view_type is None: raise KeyError("'%s' is not a valid view type" % str(value)) return BinaryViewType(view_type)
[docs]class BinaryViewType(object): __metaclass__ = _BinaryViewTypeMetaclass
[docs] def __init__(self, handle): self.handle = core.handle_of_type(handle, core.BNBinaryViewType)
def __eq__(self, value): if not isinstance(value, BinaryViewType): return False return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents) def __ne__(self, value): if not isinstance(value, BinaryViewType): return True return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) @property def name(self): """BinaryView name (read-only)""" return core.BNGetBinaryViewTypeName(self.handle) @property def long_name(self): """BinaryView long name (read-only)""" return core.BNGetBinaryViewTypeLongName(self.handle) def __repr__(self): return "<view type: '%s'>" % self.name
[docs] def create(self, data): view = core.BNCreateBinaryViewOfType(self.handle, data.handle) if view is None: return None return BinaryView(file_metadata=data.file, handle=view)
[docs] def open(self, src, file_metadata=None): data = BinaryView.open(src, file_metadata) if data is None: return None return self.create(data)
@classmethod
[docs] def get_view_of_file(cls, filename, update_analysis=True): """ ``get_view_of_file`` returns the first available, non-Raw `BinaryView` available. :param str filename: Path to filename or bndb :param bool update_analysis: defaults to True. Pass False to not run update_analysis_and_wait. :return: returns a BinaryView object for the given filename. :rtype: BinaryView or None """ sqlite = "SQLite format 3" if filename.endswith(".bndb"): f = open(filename, 'r') if f is None or f.read(len(sqlite)) != sqlite: return None f.close() view = filemetadata.FileMetadata().open_existing_database(filename) else: view = BinaryView.open(filename) if view is None: return None for available in view.available_view_types: if available.name != "Raw": if filename.endswith(".bndb"): bv = view.get_view_of_type(available.name) else: bv = cls[available.name].open(filename) if update_analysis: bv.update_analysis_and_wait() return bv return None
[docs] def is_valid_for_data(self, data): return core.BNIsBinaryViewTypeValidForData(self.handle, data.handle)
[docs] def register_arch(self, ident, endian, arch): core.BNRegisterArchitectureForViewType(self.handle, ident, endian, arch.handle)
[docs] def get_arch(self, ident, endian): arch = core.BNGetArchitectureForViewType(self.handle, ident, endian) if arch is None: return None return architecture.Architecture(arch)
[docs] def register_platform(self, ident, arch, plat): core.BNRegisterPlatformForViewType(self.handle, ident, arch.handle, plat.handle)
[docs] def register_default_platform(self, arch, plat): core.BNRegisterDefaultPlatformForViewType(self.handle, arch.handle, plat.handle)
[docs] def get_platform(self, ident, arch): plat = core.BNGetPlatformForViewType(self.handle, ident, arch.handle) if plat is None: return None return platform.Platform(None, plat)
[docs]class Segment(object):
[docs] def __init__(self, start, length, data_offset, data_length, flags): self.start = start self.length = length self.data_offset = data_offset self.data_length = data_length self.flags = flags
@property def end(self): return self.start + self.length def __len__(self): return self.length def __repr__(self): return "<segment: %#x-%#x, %s%s%s>" % (self.start, self.end, "r" if (self.flags & SegmentFlag.SegmentReadable) != 0 else "-", "w" if (self.flags & SegmentFlag.SegmentWritable) != 0 else "-", "x" if (self.flags & SegmentFlag.SegmentExecutable) != 0 else "-")
[docs]class Section(object):
[docs] def __init__(self, name, section_type, start, length, linked_section, info_section, info_data, align, entry_size): self.name = name self.type = section_type self.start = start self.length = length self.linked_section = linked_section self.info_section = info_section self.info_data = info_data self.align = align self.entry_size = entry_size
@property def end(self): return self.start + self.length def __len__(self): return self.length def __repr__(self): return "<section %s: %#x-%#x>" % (self.name, self.start, self.end)
[docs]class AddressRange(object):
[docs] def __init__(self, start, end): self.start = start self.end = end
@property def length(self): return self.end - self.start def __len__(self): return self.end - self.start def __repr__(self): return "<%#x-%#x>" % (self.start, self.end)
class _BinaryViewAssociatedDataStore(associateddatastore._AssociatedDataStore): _defaults = {}
[docs]class BinaryView(object): """ ``class BinaryView`` implements a view on binary data, and presents a queryable interface of a binary file. One key job of BinaryView is file format parsing which allows Binary Ninja to read, write, insert, remove portions of the file given a virtual address. For the purposes of this documentation we define a virtual address as the memory address that the various pieces of the physical file will be loaded at. A binary file does not have to have just one BinaryView, thus much of the interface to manipulate disassembly exists within or is accessed through a BinaryView. All files are guaranteed to have at least the ``Raw`` BinaryView. The ``Raw`` BinaryView is simply a hex editor, but is helpful for manipulating binary files via their absolute addresses. BinaryViews are plugins and thus registered with Binary Ninja at startup, and thus should **never** be instantiated directly as this is already done. The list of available BinaryViews can be seen in the BinaryViewType class which provides an iterator and map of the various installed BinaryViews:: >>> list(BinaryViewType) [<view type: 'Raw'>, <view type: 'ELF'>, <view type: 'Mach-O'>, <view type: 'PE'>] >>> BinaryViewType['ELF'] <view type: 'ELF'> To open a file with a given BinaryView the following code can be used:: >>> bv = BinaryViewType['Mach-O'].open("/bin/ls") >>> bv <BinaryView: '/bin/ls', start 0x100000000, len 0xa000> `By convention in the rest of this document we will use bv to mean an open BinaryView of an executable file.` When a BinaryView is open on an executable view, analysis does not automatically run, this can be done by running the ``update_analysis_and_wait()`` method which disassembles the executable and returns when all disassembly is finished:: >>> bv.update_analysis_and_wait() >>> Since BinaryNinja's analysis is multi-threaded (depending on version) this can also be done in the background by using the ``update_analysis()`` method instead. By standard python convention methods which start with '_' should be considered private and should not be called externally. Additionanlly, methods which begin with ``perform_`` should not be called either and are used explicitly for subclassing the BinaryView. .. note:: An important note on the ``*_user_*()`` methods. Binary Ninja makes a distinction between edits \ performed by the user and actions performed by auto analysis. Auto analysis actions that can quickly be recalculated \ are not saved to the database. Auto analysis actions that take a long time and all user edits are stored in the \ database (e.g. ``remove_user_function()`` rather than ``remove_function()``). Thus use ``_user_`` methods if saving \ to the database is desired. """ name = None long_name = None _registered = False _registered_cb = None registered_view_type = None next_address = 0 _associated_data = {}
[docs] def __init__(self, file_metadata=None, parent_view=None, handle=None): if handle is not None: self.handle = core.handle_of_type(handle, core.BNBinaryView) if file_metadata is None: self.file = filemetadata.FileMetadata(handle=core.BNGetFileForView(handle)) else: self.file = file_metadata elif self.__class__ is BinaryView: startup._init_plugins() if file_metadata is None: file_metadata = filemetadata.FileMetadata() self.handle = core.BNCreateBinaryDataView(file_metadata.handle) self.file = filemetadata.FileMetadata(handle=core.BNNewFileReference(file_metadata.handle)) else: startup._init_plugins() if not self.__class__._registered: raise TypeError("view type not registered") self._cb = core.BNCustomBinaryView() self._cb.context = 0 self._cb.init = self._cb.init.__class__(self._init) self._cb.read = self._cb.read.__class__(self._read) self._cb.write = self._cb.write.__class__(self._write) self._cb.insert = self._cb.insert.__class__(self._insert) self._cb.remove = self._cb.remove.__class__(self._remove) self._cb.getModification = self._cb.getModification.__class__(self._get_modification) self._cb.isValidOffset = self._cb.isValidOffset.__class__(self._is_valid_offset) self._cb.isOffsetReadable = self._cb.isOffsetReadable.__class__(self._is_offset_readable) self._cb.isOffsetWritable = self._cb.isOffsetWritable.__class__(self._is_offset_writable) self._cb.isOffsetExecutable = self._cb.isOffsetExecutable.__class__(self._is_offset_executable) self._cb.getNextValidOffset = self._cb.getNextValidOffset.__class__(self._get_next_valid_offset) self._cb.getStart = self._cb.getStart.__class__(self._get_start) self._cb.getLength = self._cb.getLength.__class__(self._get_length) self._cb.getEntryPoint = self._cb.getEntryPoint.__class__(self._get_entry_point) self._cb.isExecutable = self._cb.isExecutable.__class__(self._is_executable) self._cb.getDefaultEndianness = self._cb.getDefaultEndianness.__class__(self._get_default_endianness) self._cb.getAddressSize = self._cb.getAddressSize.__class__(self._get_address_size) self._cb.save = self._cb.save.__class__(self._save) self.file = file_metadata if parent_view is not None: parent_view = parent_view.handle self.handle = core.BNCreateCustomBinaryView(self.__class__.name, file_metadata.handle, parent_view, self._cb) self.notifications = {} self.next_address = None # Do NOT try to access view before init() is called, use placeholder
def __eq__(self, value): if not isinstance(value, BinaryView): return False return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents) def __ne__(self, value): if not isinstance(value, BinaryView): return True return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) @classmethod
[docs] def register(cls): startup._init_plugins() if cls.name is None: raise ValueError("view 'name' not defined") if cls.long_name is None: cls.long_name = cls.name cls._registered_cb = core.BNCustomBinaryViewType() cls._registered_cb.context = 0 cls._registered_cb.create = cls._registered_cb.create.__class__(cls._create) cls._registered_cb.isValidForData = cls._registered_cb.isValidForData.__class__(cls._is_valid_for_data) cls.registered_view_type = BinaryViewType(core.BNRegisterBinaryViewType(cls.name, cls.long_name, cls._registered_cb)) cls._registered = True
@classmethod def _create(cls, ctxt, data): try: file_metadata = filemetadata.FileMetadata(handle=core.BNGetFileForView(data)) view = cls(BinaryView(file_metadata=file_metadata, handle=core.BNNewViewReference(data))) if view is None: return None return ctypes.cast(core.BNNewViewReference(view.handle), ctypes.c_void_p).value except: log.log_error(traceback.format_exc()) return None @classmethod def _is_valid_for_data(cls, ctxt, data): try: return cls.is_valid_for_data(BinaryView(handle=core.BNNewViewReference(data))) except: log.log_error(traceback.format_exc()) return False @classmethod
[docs] def open(cls, src, file_metadata=None): startup._init_plugins() if isinstance(src, fileaccessor.FileAccessor): if file_metadata is None: file_metadata = filemetadata.FileMetadata() view = core.BNCreateBinaryDataViewFromFile(file_metadata.handle, src._cb) else: if file_metadata is None: file_metadata = filemetadata.FileMetadata(str(src)) view = core.BNCreateBinaryDataViewFromFilename(file_metadata.handle, str(src)) if view is None: return None result = BinaryView(file_metadata=file_metadata, handle=view) return result
@classmethod
[docs] def new(cls, data=None, file_metadata=None): startup._init_plugins() if file_metadata is None: file_metadata = filemetadata.FileMetadata() if data is None: view = core.BNCreateBinaryDataView(file_metadata.handle) else: buf = databuffer.DataBuffer(data) view = core.BNCreateBinaryDataViewFromBuffer(file_metadata.handle, buf.handle) if view is None: return None result = BinaryView(file_metadata=file_metadata, handle=view) return result
@classmethod def _unregister(cls, view): handle = ctypes.cast(view, ctypes.c_void_p) if handle.value in cls._associated_data: del cls._associated_data[handle.value] @classmethod
[docs] def set_default_session_data(cls, name, value): """ ``set_default_session_data`` saves a variable to the BinaryView. :param name: name of the variable to be saved :param value: value of the variable to be saved :Example: >>> BinaryView.set_default_session_data("variable_name", "value") >>> bv.session_data.variable_name 'value' """ _BinaryViewAssociatedDataStore.set_default(name, value)
def __del__(self): for i in self.notifications.values(): i._unregister() core.BNFreeBinaryView(self.handle) def __iter__(self): count = ctypes.c_ulonglong(0) funcs = core.BNGetAnalysisFunctionList(self.handle, count) try: for i in xrange(0, count.value): yield function.Function(self, core.BNNewFunctionReference(funcs[i])) finally: core.BNFreeFunctionList(funcs, count.value) @property def parent_view(self): """View that contains the raw data used by this view (read-only)""" result = core.BNGetParentView(self.handle) if result is None: return None return BinaryView(handle=result) @property def modified(self): """boolean modification state of the BinaryView (read/write)""" return self.file.modified @modified.setter def modified(self, value): self.file.modified = value @property def analysis_changed(self): """boolean analysis state changed of the currently running analysis (read-only)""" return self.file.analysis_changed @property def has_database(self): """boolean has a database been written to disk (read-only)""" return self.file.has_database @property def view(self): return self.file.view @view.setter def view(self, value): self.file.view = value @property def offset(self): return self.file.offset @offset.setter def offset(self, value): self.file.offset = value @property def start(self): """Start offset of the binary (read-only)""" return core.BNGetStartOffset(self.handle) @property def end(self): """End offset of the binary (read-only)""" return core.BNGetEndOffset(self.handle) @property def entry_point(self): """Entry point of the binary (read-only)""" return core.BNGetEntryPoint(self.handle) @property def arch(self): """The architecture associated with the current BinaryView (read/write)""" arch = core.BNGetDefaultArchitecture(self.handle) if arch is None: return None return architecture.Architecture(handle=arch) @arch.setter def arch(self, value): if value is None: core.BNSetDefaultArchitecture(self.handle, None) else: core.BNSetDefaultArchitecture(self.handle, value.handle) @property def platform(self): """The platform associated with the current BinaryView (read/write)""" plat = core.BNGetDefaultPlatform(self.handle) if plat is None: return None return platform.Platform(self.arch, handle=plat) @platform.setter def platform(self, value): if value is None: core.BNSetDefaultPlatform(self.handle, None) else: core.BNSetDefaultPlatform(self.handle, value.handle) @property def endianness(self): """Endianness of the binary (read-only)""" return Endianness(core.BNGetDefaultEndianness(self.handle)) @property def address_size(self): """Address size of the binary (read-only)""" return core.BNGetViewAddressSize(self.handle) @property def executable(self): """Whether the binary is an executable (read-only)""" return core.BNIsExecutableView(self.handle) @property def functions(self): """List of functions (read-only)""" count = ctypes.c_ulonglong(0) funcs = core.BNGetAnalysisFunctionList(self.handle, count) result = [] for i in xrange(0, count.value): result.append(function.Function(self, core.BNNewFunctionReference(funcs[i]))) core.BNFreeFunctionList(funcs, count.value) return result @property def has_functions(self): """Boolean whether the binary has functions (read-only)""" return core.BNHasFunctions(self.handle) @property def entry_function(self): """Entry function (read-only)""" func = core.BNGetAnalysisEntryPoint(self.handle) if func is None: return None return function.Function(self, func) @property def symbols(self): """Dict of symbols (read-only)""" count = ctypes.c_ulonglong(0) syms = core.BNGetSymbols(self.handle, count) result = {} for i in xrange(0, count.value): sym = types.Symbol(None, None, None, handle=core.BNNewSymbolReference(syms[i])) result[sym.raw_name] = sym core.BNFreeSymbolList(syms, count.value) return result @property def view_type(self): """View type (read-only)""" return core.BNGetViewType(self.handle) @property def available_view_types(self): """Available view types (read-only)""" count = ctypes.c_ulonglong(0) types = core.BNGetBinaryViewTypesForData(self.handle, count) result = [] for i in xrange(0, count.value): result.append(BinaryViewType(types[i])) core.BNFreeBinaryViewTypeList(types) return result @property def strings(self): """List of strings (read-only)""" return self.get_strings() @property def saved(self): """boolean state of whether or not the file has been saved (read/write)""" return self.file.saved @saved.setter def saved(self, value): self.file.saved = value @property def analysis_progress(self): """Status of current analysis (read-only)""" result = core.BNGetAnalysisProgress(self.handle) return AnalysisProgress(result.state, result.count, result.total) @property def linear_disassembly(self): """Iterator for all lines in the linear disassembly of the view""" return self.get_linear_disassembly(None) @property def data_vars(self): """List of data variables (read-only)""" count = ctypes.c_ulonglong(0) var_list = core.BNGetDataVariables(self.handle, count) result = {} for i in xrange(0, count.value): addr = var_list[i].address var_type = types.Type(core.BNNewTypeReference(var_list[i].type)) auto_discovered = var_list[i].autoDiscovered result[addr] = DataVariable(addr, var_type, auto_discovered) core.BNFreeDataVariables(var_list, count.value) return result @property def types(self): """List of defined types (read-only)""" count = ctypes.c_ulonglong(0) type_list = core.BNGetAnalysisTypeList(self.handle, count) result = {} for i in xrange(0, count.value): name = types.QualifiedName._from_core_struct(type_list[i].name) result[name] = types.Type(core.BNNewTypeReference(type_list[i].type)) core.BNFreeTypeList(type_list, count.value) return result @property def segments(self): """List of segments (read-only)""" count = ctypes.c_ulonglong(0) segment_list = core.BNGetSegments(self.handle, count) result = [] for i in xrange(0, count.value): result.append(Segment(segment_list[i].start, segment_list[i].length, segment_list[i].dataOffset, segment_list[i].dataLength, segment_list[i].flags)) core.BNFreeSegmentList(segment_list) return result @property def sections(self): """List of sections (read-only)""" count = ctypes.c_ulonglong(0) section_list = core.BNGetSections(self.handle, count) result = {} for i in xrange(0, count.value): result[section_list[i].name] = Section(section_list[i].name, section_list[i].type, section_list[i].start, section_list[i].length, section_list[i].linkedSection, section_list[i].infoSection, section_list[i].infoData, section_list[i].align, section_list[i].entrySize) core.BNFreeSectionList(section_list, count.value) return result @property def allocated_ranges(self): """List of valid address ranges for this view (read-only)""" count = ctypes.c_ulonglong(0) range_list = core.BNGetAllocatedRanges(self.handle, count) result = [] for i in xrange(0, count.value): result.append(AddressRange(range_list[i].start, range_list[i].end)) core.BNFreeAddressRanges(range_list) return result @property def session_data(self): """Dictionary object where plugins can store arbitrary data associated with the view""" handle = ctypes.cast(self.handle, ctypes.c_void_p) if handle.value not in BinaryView._associated_data: obj = _BinaryViewAssociatedDataStore() BinaryView._associated_data[handle.value] = obj return obj else: return BinaryView._associated_data[handle.value] def __len__(self): return int(core.BNGetViewLength(self.handle)) def __getitem__(self, i): if isinstance(i, tuple): result = "" for s in i: result += self.__getitem__(s) return result elif isinstance(i, slice): if i.step is not None: raise IndexError("step not implemented") i = i.indices(self.end) start = i[0] stop = i[1] if stop <= start: return "" return str(self.read(start, stop - start)) elif i < 0: if i >= -len(self): value = str(self.read(int(len(self) + i), 1)) if len(value) == 0: return IndexError("index not readable") return value raise IndexError("index out of range") elif (i >= self.start) and (i < self.end): value = str(self.read(int(i), 1)) if len(value) == 0: return IndexError("index not readable") return value else: raise IndexError("index out of range") def __setitem__(self, i, value): if isinstance(i, slice): if i.step is not None: raise IndexError("step not supported on assignment") i = i.indices(self.end) start = i[0] stop = i[1] if stop < start: stop = start if len(value) != (stop - start): self.remove(start, stop - start) self.insert(start, value) else: self.write(start, value) elif i < 0: if i >= -len(self): if len(value) != 1: raise ValueError("expected single byte for assignment") if self.write(int(len(self) + i), value) != 1: raise IndexError("index not writable") else: raise IndexError("index out of range") elif (i >= self.start) and (i < self.end): if len(value) != 1: raise ValueError("expected single byte for assignment") if self.write(int(i), value) != 1: raise IndexError("index not writable") else: raise IndexError("index out of range") def __repr__(self): start = self.start length = len(self) if start != 0: size = "start %#x, len %#x" % (start, length) else: size = "len %#x" % length filename = self.file.filename if len(filename) > 0: return "<BinaryView: '%s', %s>" % (filename, size) return "<BinaryView: %s>" % (size) def _init(self, ctxt): try: return self.init() except: log.log_error(traceback.format_exc()) return False def _read(self, ctxt, dest, offset, length): try: data = self.perform_read(offset, length) if data is None: return 0 if len(data) > length: data = data[0:length] ctypes.memmove(dest, str(data), len(data)) return len(data) except: log.log_error(traceback.format_exc()) return 0 def _write(self, ctxt, offset, src, length): try: data = ctypes.create_string_buffer(length) ctypes.memmove(data, src, length) return self.perform_write(offset, data.raw) except: log.log_error(traceback.format_exc()) return 0 def _insert(self, ctxt, offset, src, length): try: data = ctypes.create_string_buffer(length) ctypes.memmove(data, src, length) return self.perform_insert(offset, data.raw) except: log.log_error(traceback.format_exc()) return 0 def _remove(self, ctxt, offset, length): try: return self.perform_remove(offset, length) except: log.log_error(traceback.format_exc()) return 0 def _get_modification(self, ctxt, offset): try: return self.perform_get_modification(offset) except: log.log_error(traceback.format_exc()) return ModificationStatus.Original def _is_valid_offset(self, ctxt, offset): try: return self.perform_is_valid_offset(offset) except: log.log_error(traceback.format_exc()) return False def _is_offset_readable(self, ctxt, offset): try: return self.perform_is_offset_readable(offset) except: log.log_error(traceback.format_exc()) return False def _is_offset_writable(self, ctxt, offset): try: return self.perform_is_offset_writable(offset) except: log.log_error(traceback.format_exc()) return False def _is_offset_executable(self, ctxt, offset): try: return self.perform_is_offset_executable(offset) except: log.log_error(traceback.format_exc()) return False def _get_next_valid_offset(self, ctxt, offset): try: return self.perform_get_next_valid_offset(offset) except: log.log_error(traceback.format_exc()) return offset def _get_start(self, ctxt): try: return self.perform_get_start() except: log.log_error(traceback.format_exc()) return 0 def _get_length(self, ctxt): try: return self.perform_get_length() except: log.log_error(traceback.format_exc()) return 0 def _get_entry_point(self, ctxt): try: return self.perform_get_entry_point() except: log.log_error(traceback.format_exc()) return 0 def _is_executable(self, ctxt): try: return self.perform_is_executable() except: log.log_error(traceback.format_exc()) return False def _get_default_endianness(self, ctxt): try: return self.perform_get_default_endianness() except: log.log_error(traceback.format_exc()) return Endianness.LittleEndian def _get_address_size(self, ctxt): try: return self.perform_get_address_size() except: log.log_error(traceback.format_exc()) return 8 def _save(self, ctxt, file_accessor): try: return self.perform_save(fileaccessor.CoreFileAccessor(file_accessor)) except: log.log_error(traceback.format_exc()) return False
[docs] def init(self): return True
[docs] def get_disassembly(self, addr, arch=None): """ ``get_disassembly`` simple helper function for printing disassembly of a given address :param int addr: virtual address of instruction :param Architecture arch: optional Architecture, ``self.arch`` is used if this parameter is None :return: a str representation of the instruction at virtual address ``addr`` or None :rtype: str or None :Example: >>> bv.get_disassembly(bv.entry_point) 'push ebp' >>> """ if arch is None: arch = self.arch txt, size = arch.get_instruction_text(self.read(addr, arch.max_instr_length), addr) self.next_address = addr + size if txt is None: return None return ''.join(str(a) for a in txt).strip()
[docs] def get_next_disassembly(self, arch=None): """ ``get_next_disassembly`` simple helper function for printing disassembly of the next instruction. The internal state of the instruction to be printed is stored in the ``next_address`` attribute :param Architecture arch: optional Architecture, ``self.arch`` is used if this parameter is None :return: a str representation of the instruction at virtual address ``self.next_address`` :rtype: str or None :Example: >>> bv.get_next_disassembly() 'push ebp' >>> bv.get_next_disassembly() 'mov ebp, esp' >>> #Now reset the starting point back to the entry point >>> bv.next_address = bv.entry_point >>> bv.get_next_disassembly() 'push ebp' >>> """ if arch is None: arch = self.arch if self.next_address is None: self.next_address = self.entry_point txt, size = arch.get_instruction_text(self.read(self.next_address, arch.max_instr_length), self.next_address) self.next_address += size if txt is None: return None return ''.join(str(a) for a in txt).strip()
[docs] def perform_save(self, accessor): if self.parent_view is not None: return self.parent_view.save(accessor) return False
@abc.abstractmethod
[docs] def perform_get_address_size(self): raise NotImplementedError
[docs] def perform_get_length(self): """ ``perform_get_length`` implements a query for the size of the virtual address range used by the BinaryView. .. note:: This method **may** be overridden by custom BinaryViews. Use ``add_auto_segment`` to provide data without overriding this method. .. warning:: This method **must not** be called directly. :return: returns the size of the virtual address range used by the BinaryView. :rtype: int """ return 0
[docs] def perform_read(self, addr, length): """ ``perform_read`` implements a mapping between a virtual address and an absolute file offset, reading ``length`` bytes from the rebased address ``addr``. .. note:: This method **may** be overridden by custom BinaryViews. Use ``add_auto_segment`` to provide data without overriding this method. .. warning:: This method **must not** be called directly. :param int addr: a virtual address to attempt to read from :param int length: the number of bytes to be read :return: length bytes read from addr, should return empty string on error :rtype: str """ return ""
[docs] def perform_write(self, addr, data): """ ``perform_write`` implements a mapping between a virtual address and an absolute file offset, writing the bytes ``data`` to rebased address ``addr``. .. note:: This method **may** be overridden by custom BinaryViews. Use ``add_auto_segment`` to provide data without overriding this method. .. warning:: This method **must not** be called directly. :param int addr: a virtual address :param str data: the data to be written :return: length of data written, should return 0 on error :rtype: int """ return 0
[docs] def perform_insert(self, addr, data): """ ``perform_insert`` implements a mapping between a virtual address and an absolute file offset, inserting the bytes ``data`` to rebased address ``addr``. .. note:: This method **may** be overridden by custom BinaryViews. If not overridden, inserting is disallowed .. warning:: This method **must not** be called directly. :param int addr: a virtual address :param str data: the data to be inserted :return: length of data inserted, should return 0 on error :rtype: int """ return 0
[docs] def perform_remove(self, addr, length): """ ``perform_remove`` implements a mapping between a virtual address and an absolute file offset, removing ``length`` bytes from the rebased address ``addr``. .. note:: This method **may** be overridden by custom BinaryViews. If not overridden, removing data is disallowed .. warning:: This method **must not** be called directly. :param int addr: a virtual address :param str data: the data to be removed :return: length of data removed, should return 0 on error :rtype: int """ return 0
[docs] def perform_get_modification(self, addr): """ ``perform_get_modification`` implements query to the whether the virtual address ``addr`` is modified. .. note:: This method **may** be overridden by custom BinaryViews. Use ``add_auto_segment`` to provide data without overriding this method. .. warning:: This method **must not** be called directly. :param int addr: a virtual address to be checked :return: One of the following: Original = 0, Changed = 1, Inserted = 2 :rtype: ModificationStatus """ return ModificationStatus.Original
[docs] def perform_is_valid_offset(self, addr): """ ``perform_is_valid_offset`` implements a check if an virtual address ``addr`` is valid. .. note:: This method **may** be overridden by custom BinaryViews. Use ``add_auto_segment`` to provide data without overriding this method. .. warning:: This method **must not** be called directly. :param int addr: a virtual address to be checked :return: true if the virtual address is valid, false if the virtual address is invalid or error :rtype: bool """ data = self.read(addr, 1) return (data is not None) and (len(data) == 1)
[docs] def perform_is_offset_readable(self, offset): """ ``perform_is_offset_readable`` implements a check if an virtual address is readable. .. note:: This method **may** be overridden by custom BinaryViews. Use ``add_auto_segment`` to provide data without overriding this method. .. warning:: This method **must not** be called directly. :param int offset: a virtual address to be checked :return: true if the virtual address is readable, false if the virtual address is not readable or error :rtype: bool """ return self.is_valid_offset(offset)
[docs] def perform_is_offset_writable(self, addr): """ ``perform_is_offset_writable`` implements a check if a virtual address ``addr`` is writable. .. note:: This method **may** be overridden by custom BinaryViews. Use ``add_auto_segment`` to provide data without overriding this method. .. warning:: This method **must not** be called directly. :param int addr: a virtual address to be checked :return: true if the virtual address is writable, false if the virtual address is not writable or error :rtype: bool """ return self.is_valid_offset(addr)
[docs] def perform_is_offset_executable(self, addr): """ ``perform_is_offset_executable`` implements a check if a virtual address ``addr`` is executable. .. note:: This method **may** be overridden by custom BinaryViews. Use ``add_auto_segment`` to provide data without overriding this method. .. warning:: This method **must not** be called directly. :param int addr: a virtual address to be checked :return: true if the virtual address is executable, false if the virtual address is not executable or error :rtype: int """ return self.is_valid_offset(addr)
[docs] def perform_get_next_valid_offset(self, addr): """ ``perform_get_next_valid_offset`` implements a query for the next valid readable, writable, or executable virtual memory address. .. note:: This method **may** be overridden by custom BinaryViews. Use ``add_auto_segment`` to provide data without overriding this method. .. warning:: This method **must not** be called directly. :param int addr: a virtual address to start checking from. :return: the next readable, writable, or executable virtual memory address :rtype: int """ if addr < self.perform_get_start(): return self.perform_get_start() return addr
[docs] def perform_get_start(self): """ ``perform_get_start`` implements a query for the first readable, writable, or executable virtual address in the BinaryView. .. note:: This method **may** be overridden by custom BinaryViews. Use ``add_auto_segment`` to provide data without overriding this method. .. warning:: This method **must not** be called directly. :return: returns the first virtual address in the BinaryView. :rtype: int """ return 0
[docs] def perform_get_entry_point(self): """ ``perform_get_entry_point`` implements a query for the initial entry point for code execution. .. note:: This method **should** be implmented for custom BinaryViews that are executable. .. warning:: This method **must not** be called directly. :return: the virtual address of the entry point :rtype: int """ return 0
[docs] def perform_is_executable(self): """ ``perform_is_executable`` implements a check which returns true if the BinaryView is executable. .. note:: This method **must** be implemented for custom BinaryViews that are executable. .. warning:: This method **must not** be called directly. :return: true if the current BinaryView is executable, false if it is not executable or on error :rtype: bool """ return False
[docs] def perform_get_default_endianness(self): """ ``perform_get_default_endianness`` implements a check which returns true if the BinaryView is executable. .. note:: This method **may** be implemented for custom BinaryViews that are not LittleEndian. .. warning:: This method **must not** be called directly. :return: either ``Endianness.LittleEndian`` or ``Endianness.BigEndian`` :rtype: Endianness """ return Endianness.LittleEndian
[docs] def create_database(self, filename, progress_func=None): """ ``create_database`` writes the current database (.bndb) file out to the specified file. :param str filename: path and filename to write the bndb to, this string `should` have ".bndb" appended to it. :param callable() progress_func: optional function to be called with the current progress and total count. :return: true on success, false on failure :rtype: bool """ return self.file.create_database(filename, progress_func)
[docs] def save_auto_snapshot(self, progress_func=None): """ ``save_auto_snapshot`` saves the current database to the already created file. .. note:: :py:method:`create_database` should have been called prior to executing this method :param callable() progress_func: optional function to be called with the current progress and total count. :return: True if it successfully saved the snapshot, False otherwise :rtype: bool """ return self.file.save_auto_snapshot(progress_func)
[docs] def get_view_of_type(self, name): """ ``get_view_of_type`` returns the BinaryView associated with the provided name if it exists. :param str name: Name of the view to be retrieved :return: BinaryView object assocated with the provided name or None on failure :rtype: BinaryView or None """ return self.file.get_view_of_type(name)
[docs] def begin_undo_actions(self): """ ``begin_undo_actions`` start recording actions taken so the can be undone at some point. :rtype: None :Example: >>> bv.get_disassembly(0x100012f1) 'xor eax, eax' >>> bv.begin_undo_actions() >>> bv.convert_to_nop(0x100012f1) True >>> bv.commit_undo_actions() >>> bv.get_disassembly(0x100012f1) 'nop' >>> bv.undo() >>> bv.get_disassembly(0x100012f1) 'xor eax, eax' >>> """ self.file.begin_undo_actions()
[docs] def add_undo_action(self, action): core.BNAddUndoAction(self.handle, action.__class__.name, action._cb)
[docs] def commit_undo_actions(self): """ ``commit_undo_actions`` commit the actions taken since the last commit to the undo database. :rtype: None :Example: >>> bv.get_disassembly(0x100012f1) 'xor eax, eax' >>> bv.begin_undo_actions() >>> bv.convert_to_nop(0x100012f1) True >>> bv.commit_undo_actions() >>> bv.get_disassembly(0x100012f1) 'nop' >>> bv.undo() >>> bv.get_disassembly(0x100012f1) 'xor eax, eax' >>> """ self.file.commit_undo_actions()
[docs] def undo(self): """ ``undo`` undo the last commited action in the undo database. :rtype: None :Example: >>> bv.get_disassembly(0x100012f1) 'xor eax, eax' >>> bv.begin_undo_actions() >>> bv.convert_to_nop(0x100012f1) True >>> bv.commit_undo_actions() >>> bv.get_disassembly(0x100012f1) 'nop' >>> bv.undo() >>> bv.get_disassembly(0x100012f1) 'xor eax, eax' >>> bv.redo() >>> bv.get_disassembly(0x100012f1) 'nop' >>> """ self.file.undo()
[docs] def redo(self): """ ``redo`` redo the last commited action in the undo database. :rtype: None :Example: >>> bv.get_disassembly(0x100012f1) 'xor eax, eax' >>> bv.begin_undo_actions() >>> bv.convert_to_nop(0x100012f1) True >>> bv.commit_undo_actions() >>> bv.get_disassembly(0x100012f1) 'nop' >>> bv.undo() >>> bv.get_disassembly(0x100012f1) 'xor eax, eax' >>> bv.redo() >>> bv.get_disassembly(0x100012f1) 'nop' >>> """ self.file.redo()
[docs] def navigate(self, view, offset): self.file.navigate(view, offset)
[docs] def read(self, addr, length): """ ``read`` returns the data reads at most ``length`` bytes from virtual address ``addr``. :param int addr: virtual address to read from. :param int length: number of bytes to read. :return: at most ``length`` bytes from the virtual address ``addr``, empty string on error or no data. :rtype: str :Example: >>> #Opening a x86_64 Mach-O binary >>> bv = BinaryViewType['Raw'].open("/bin/ls") >>> bv.read(0,4) \'\\xcf\\xfa\\xed\\xfe\' """ buf = databuffer.DataBuffer(handle=core.BNReadViewBuffer(self.handle, addr, length)) return str(buf)
[docs] def write(self, addr, data): """ ``write`` writes the bytes in ``data`` to the virtual address ``addr``. :param int addr: virtual address to write to. :param str data: data to be written at addr. :return: number of bytes written to virtual address ``addr`` :rtype: int :Example: >>> bv.read(0,4) 'BBBB' >>> bv.write(0, "AAAA") 4L >>> bv.read(0,4) 'AAAA' """ buf = databuffer.DataBuffer(data) return core.BNWriteViewBuffer(self.handle, addr, buf.handle)
[docs] def insert(self, addr, data): """ ``insert`` inserts the bytes in ``data`` to the virtual address ``addr``. :param int addr: virtual address to write to. :param str data: data to be inserted at addr. :return: number of bytes inserted to virtual address ``addr`` :rtype: int :Example: >>> bv.insert(0,"BBBB") 4L >>> bv.read(0,8) 'BBBBAAAA' """ buf = databuffer.DataBuffer(data) return core.BNInsertViewBuffer(self.handle, addr, buf.handle)
[docs] def remove(self, addr, length): """ ``remove`` removes at most ``length`` bytes from virtual address ``addr``. :param int addr: virtual address to remove from. :param int length: number of bytes to remove. :return: number of bytes removed from virtual address ``addr`` :rtype: int :Example: >>> bv.read(0,8) 'BBBBAAAA' >>> bv.remove(0,4) 4L >>> bv.read(0,4) 'AAAA' """ return core.BNRemoveViewData(self.handle, addr, length)
[docs] def get_modification(self, addr, length=None): """ ``get_modification`` returns the modified bytes of up to ``length`` bytes from virtual address ``addr``, or if ``length`` is None returns the ModificationStatus. :param int addr: virtual address to get modification from :param int length: optional length of modification :return: Either ModificationStatus of the byte at ``addr``, or string of modified bytes at ``addr`` :rtype: ModificationStatus or str """ if length is None: return ModificationStatus(core.BNGetModification(self.handle, addr)) data = (ModificationStatus * length)() length = core.BNGetModificationArray(self.handle, addr, data, length) return data[0:length]
[docs] def is_valid_offset(self, addr): """ ``is_valid_offset`` checks if an virtual address ``addr`` is valid . :param int addr: a virtual address to be checked :return: true if the virtual address is valid, false if the virtual address is invalid or error :rtype: bool """ return core.BNIsValidOffset(self.handle, addr)
[docs] def is_offset_readable(self, addr): """ ``is_offset_readable`` checks if an virtual address ``addr`` is valid for reading. :param int addr: a virtual address to be checked :return: true if the virtual address is valid for reading, false if the virtual address is invalid or error :rtype: bool """ return core.BNIsOffsetReadable(self.handle, addr)
[docs] def is_offset_writable(self, addr): """ ``is_offset_writable`` checks if an virtual address ``addr`` is valid for writing. :param int addr: a virtual address to be checked :return: true if the virtual address is valid for writing, false if the virtual address is invalid or error :rtype: bool """ return core.BNIsOffsetWritable(self.handle, addr)
[docs] def is_offset_executable(self, addr): """ ``is_offset_executable`` checks if an virtual address ``addr`` is valid for executing. :param int addr: a virtual address to be checked :return: true if the virtual address is valid for executing, false if the virtual address is invalid or error :rtype: bool """ return core.BNIsOffsetExecutable(self.handle, addr)
[docs] def save(self, dest): """ ``save`` saves the original binary file to the provided destination ``dest`` along with any modifications. :param str dest: destination path and filename of file to be written :return: boolean True on success, False on failure :rtype: bool """ if isinstance(dest, fileaccessor.FileAccessor): return core.BNSaveToFile(self.handle, dest._cb) return core.BNSaveToFilename(self.handle, str(dest))
[docs] def register_notification(self, notify): cb = BinaryDataNotificationCallbacks(self, notify) cb._register() self.notifications[notify] = cb
[docs] def unregister_notification(self, notify): if notify in self.notifications: self.notifications[notify]._unregister() del self.notifications[notify]
[docs] def add_function(self, addr, plat=None): """ ``add_function`` add a new function of the given ``plat`` at the virtual address ``addr`` :param int addr: virtual address of the function to be added :param Platform plat: Platform for the function to be added :rtype: None :Example: >>> bv.add_function(1) >>> bv.functions [<func: [email protected]>] """ if self.platform is None: raise Exception("Default platform not set in BinaryView") if plat is None: plat = self.platform core.BNAddFunctionForAnalysis(self.handle, plat.handle, addr)
[docs] def add_entry_point(self, addr, plat=None): """ ``add_entry_point`` adds an virtual address to start analysis from for a given plat. :param int addr: virtual address to start analysis from :param Platform plat: Platform for the entry point analysis :rtype: None :Example: >>> bv.add_entry_point(0xdeadbeef) >>> """ if self.platform is None: raise Exception("Default platform not set in BinaryView") if plat is None: plat = self.platform core.BNAddEntryPointForAnalysis(self.handle, plat.handle, addr)
[docs] def remove_function(self, func): """ ``remove_function`` removes the function ``func`` from the list of functions :param Function func: a Function object. :rtype: None :Example: >>> bv.functions [<func: [email protected]>] >>> bv.remove_function(bv.functions[0]) >>> bv.functions [] """ core.BNRemoveAnalysisFunction(self.handle, func.handle)
[docs] def create_user_function(self, addr, plat=None): """ ``create_user_function`` add a new *user* function of the given ``plat`` at the virtual address ``addr`` :param int addr: virtual address of the *user* function to be added :param Platform plat: Platform for the function to be added :rtype: None :Example: >>> bv.create_user_function(1) >>> bv.functions [<func: [email protected]>] """ if plat is None: plat = self.platform core.BNCreateUserFunction(self.handle, plat.handle, addr)
[docs] def remove_user_function(self, func): """ ``remove_user_function`` removes the *user* function ``func`` from the list of functions :param Function func: a Function object. :rtype: None :Example: >>> bv.functions [<func: [email protected]>] >>> bv.remove_user_function(bv.functions[0]) >>> bv.functions [] """ core.BNRemoveUserFunction(self.handle, func.handle)
[docs] def update_analysis(self): """ ``update_analysis`` asynchronously starts the analysis running and returns immediately. Analysis of BinaryViews does not occur automatically, the user must start analysis by calling either ``update_analysis()`` or ``update_analysis_and_wait()``. An analysis update **must** be run after changes are made which could change analysis results such as adding functions. :rtype: None """ core.BNUpdateAnalysis(self.handle)
[docs] def update_analysis_and_wait(self): """ ``update_analysis_and_wait`` blocking call to update the analysis, this call returns when the analysis is complete. Analysis of BinaryViews does not occur automatically, the user must start analysis by calling either ``update_analysis()`` or ``update_analysis_and_wait()``. An analysis update **must** be run after changes are made which could change analysis results such as adding functions. :rtype: None """ class WaitEvent(object): def __init__(self): self.cond = threading.Condition() self.done = False def complete(self): self.cond.acquire() self.done = True self.cond.notify() self.cond.release() def wait(self): self.cond.acquire() while not self.done: self.cond.wait() self.cond.release() wait = WaitEvent() # TODO: figure out if we actually need this 'event' variable, likely we do event = AnalysisCompletionEvent(self, lambda: wait.complete()) core.BNUpdateAnalysis(self.handle) wait.wait()
[docs] def abort_analysis(self): """ ``abort_analysis`` will abort the currently running analysis. :rtype: None """ core.BNAbortAnalysis(self.handle)
[docs] def define_data_var(self, addr, var_type): """ ``define_data_var`` defines a non-user data variable ``var_type`` at the virtual address ``addr``. :param int addr: virtual address to define the given data variable :param Type var_type: type to be defined at the given virtual address :rtype: None :Example: >>> t = bv.parse_type_string("int foo") >>> t (<type: int32_t>, 'foo') >>> bv.define_data_var(bv.entry_point, t[0]) >>> """ core.BNDefineDataVariable(self.handle, addr, var_type.handle)
[docs] def define_user_data_var(self, addr, var_type): """ ``define_user_data_var`` defines a user data variable ``var_type`` at the virtual address ``addr``. :param int addr: virtual address to define the given data variable :param binaryninja.Type var_type: type to be defined at the given virtual address :rtype: None :Example: >>> t = bv.parse_type_string("int foo") >>> t (<type: int32_t>, 'foo') >>> bv.define_user_data_var(bv.entry_point, t[0]) >>> """ core.BNDefineUserDataVariable(self.handle, addr, var_type.handle)
[docs] def undefine_data_var(self, addr): """ ``undefine_data_var`` removes the non-user data variable at the virtual address ``addr``. :param int addr: virtual address to define the data variable to be removed :rtype: None :Example: >>> bv.undefine_data_var(bv.entry_point) >>> """ core.BNUndefineDataVariable(self.handle, addr)
[docs] def undefine_user_data_var(self, addr): """ ``undefine_user_data_var`` removes the user data variable at the virtual address ``addr``. :param int addr: virtual address to define the data variable to be removed :rtype: None :Example: >>> bv.undefine_user_data_var(bv.entry_point) >>> """ core.BNUndefineUserDataVariable(self.handle, addr)
[docs] def get_data_var_at(self, addr): """ ``get_data_var_at`` returns the data type at a given virtual address. :param int addr: virtual address to get the data type from :return: returns the DataVariable at the given virtual address, None on error. :rtype: DataVariable :Example: >>> t = bv.parse_type_string("int foo") >>> bv.define_data_var(bv.entry_point, t[0]) >>> bv.get_data_var_at(bv.entry_point) <var 0x100001174: int32_t> """ var = core.BNDataVariable() if not core.BNGetDataVariableAtAddress(self.handle, addr, var): return None return DataVariable(var.address, types.Type(var.type), var.autoDiscovered)
[docs] def get_function_at(self, addr, plat=None): """ ``get_function_at`` gets a binaryninja.Function object for the function at the virtual address ``addr``: :param int addr: virtual address of the desired function :param Platform plat: plat of the desired function :return: returns a Function object or None for the function at the virtual address provided :rtype: Function :Example: >>> bv.get_function_at(bv.entry_point) <func: [email protected]> >>> """ if plat is None: plat = self.platform func = core.BNGetAnalysisFunction(self.handle, plat.handle, addr) if func is None: return None return function.Function(self, func)
[docs] def get_functions_at(self, addr): """ ``get_functions_at`` get a list of binaryninja.Function objects (one for each valid plat) at the given virtual address. Binary Ninja does not limit the number of platforms in a given file thus there may be multiple functions defined from different architectures at the same location. This API allows you to query all of valid platforms. :param int addr: virtual address of the desired Function object list. :return: a list of binaryninja.Function objects defined at the provided virtual address :rtype: list(Function) """ count = ctypes.c_ulonglong(0) funcs = core.BNGetAnalysisFunctionsForAddress(self.handle, addr, count) result = [] for i in xrange(0, count.value): result.append(function.Function(self, core.BNNewFunctionReference(funcs[i]))) core.BNFreeFunctionList(funcs, count.value) return result
[docs] def get_recent_function_at(self, addr): func = core.BNGetRecentAnalysisFunctionForAddress(self.handle, addr) if func is None: return None return function.Function(self, func)
[docs] def get_basic_blocks_at(self, addr): """ ``get_basic_blocks_at`` get a list of :py:Class:`BasicBlock` objects which exist at the provided virtual address. :param int addr: virtual address of BasicBlock desired :return: a list of :py:Class:`BasicBlock` objects :rtype: list(BasicBlock) """ count = ctypes.c_ulonglong(0) blocks = core.BNGetBasicBlocksForAddress(self.handle, addr, count) result = [] for i in xrange(0, count.value): result.append(basicblock.BasicBlock(self, core.BNNewBasicBlockReference(blocks[i]))) core.BNFreeBasicBlockList(blocks, count.value) return result
[docs] def get_basic_blocks_starting_at(self, addr): """ ``get_basic_blocks_starting_at`` get a list of :py:Class:`BasicBlock` objects which start at the provided virtual address. :param int addr: virtual address of BasicBlock desired :return: a list of :py:Class:`BasicBlock` objects :rtype: list(BasicBlock) """ count = ctypes.c_ulonglong(0) blocks = core.BNGetBasicBlocksStartingAtAddress(self.handle, addr, count) result = [] for i in xrange(0, count.value): result.append(basicblock.BasicBlock(self, core.BNNewBasicBlockReference(blocks[i]))) core.BNFreeBasicBlockList(blocks, count.value) return result
[docs] def get_recent_basic_block_at(self, addr): block = core.BNGetRecentBasicBlockForAddress(self.handle, addr) if block is None: return None return basicblock.BasicBlock(self, block)
[docs] def get_code_refs(self, addr, length=None): """ ``get_code_refs`` returns a list of ReferenceSource objects (xrefs or cross-references) that point to the provided virtual address. :param int addr: virtual address to query for references :return: List of References for the given virtual address :rtype: list(ReferenceSource) :Example: >>> bv.get_code_refs(here) [<ref: [email protected]>] >>> """ count = ctypes.c_ulonglong(0) if length is None: refs = core.BNGetCodeReferences(self.handle, addr, count) else: refs = core.BNGetCodeReferencesInRange(self.handle, addr, length, count) result = [] for i in xrange(0, count.value): if refs[i].func: func = function.Function(self, core.BNNewFunctionReference(refs[i].func)) else: func = None if refs[i].arch: arch = architecture.Architecture(refs[i].arch) else: arch = None addr = refs[i].addr result.append(architecture.ReferenceSource(func, arch, addr)) core.BNFreeCodeReferences(refs, count.value) return result
[docs] def get_symbol_at(self, addr): """ ``get_symbol_at`` returns the Symbol at the provided virtual address. :param int addr: virtual address to query for symbol :return: Symbol for the given virtual address :rtype: Symbol :Example: >>> bv.get_symbol_at(bv.entry_point) <FunctionSymbol: "_start" @ 0x100001174> >>> """ sym = core.BNGetSymbolByAddress(self.handle, addr) if sym is None: return None return types.Symbol(None, None, None, handle = sym)
[docs] def get_symbol_by_raw_name(self, name): """ ``get_symbol_by_raw_name`` retrieves a Symbol object for the given a raw (mangled) name. :param str name: raw (mangled) name of Symbol to be retrieved :return: Symbol object corresponding to the provided raw name :rtype: Symbol :Example: >>> bv.get_symbol_by_raw_name('[email protected]@@[email protected]@W421@@Z') <FunctionSymbol: "public: static enum Foobar::foo __cdecl Foobar::testf(enum Foobar::foo)" @ 0x10001100> >>> """ sym = core.BNGetSymbolByRawName(self.handle, name) if sym is None: return None return types.Symbol(None, None, None, handle = sym)
[docs] def get_symbols_by_name(self, name): """ ``get_symbols_by_name`` retrieves a list of Symbol objects for the given symbol name. :param str name: name of Symbol object to be retrieved :return: Symbol object corresponding to the provided name :rtype: Symbol :Example: >>> bv.get_symbols_by_name('[email protected]@@[email protected]@W421@@Z') [<FunctionSymbol: "public: static enum Foobar::foo __cdecl Foobar::testf(enum Foobar::foo)" @ 0x10001100>] >>> """ count = ctypes.c_ulonglong(0) syms = core.BNGetSymbolsByName(self.handle, name, count) result = [] for i in xrange(0, count.value): result.append(types.Symbol(None, None, None, handle = core.BNNewSymbolReference(syms[i]))) core.BNFreeSymbolList(syms, count.value) return result
[docs] def get_symbols(self, start = None, length = None): """ ``get_symbols`` retrieves the list of all Symbol objects in the optionally provided range. :param int start: optional start virtual address :param int length: optional length :return: list of all Symbol objects, or those Symbol objects in the range of ``start``-``start+length`` :rtype: list(Symbol) :Example: >>> bv.get_symbols(0x1000200c, 1) [<ImportAddressSymbol: "[email protected]" @ 0x1000200c>] >>> """ count = ctypes.c_ulonglong(0) if start is None: syms = core.BNGetSymbols(self.handle, count) else: syms = core.BNGetSymbolsInRange(self.handle, start, length, count) result = [] for i in xrange(0, count.value): result.append(types.Symbol(None, None, None, handle = core.BNNewSymbolReference(syms[i]))) core.BNFreeSymbolList(syms, count.value) return result
[docs] def get_symbols_of_type(self, sym_type, start = None, length = None): """ ``get_symbols_of_type`` retrieves a list of all Symbol objects of the provided symbol type in the optionally provided range. :param SymbolType sym_type: A Symbol type: :py:Class:`Symbol`. :param int start: optional start virtual address :param int length: optional length :return: list of all Symbol objects of type sym_type, or those Symbol objects in the range of ``start``-``start+length`` :rtype: list(Symbol) :Example: >>> bv.get_symbols_of_type(SymbolType.ImportAddressSymbol, 0x10002028, 1) [<ImportAddressSymbol: "[email protected]" @ 0x10002028>] >>> """ if isinstance(sym_type, str): sym_type = SymbolType[sym_type] count = ctypes.c_ulonglong(0) if start is None: syms = core.BNGetSymbolsOfType(self.handle, sym_type, count) else: syms = core.BNGetSymbolsOfTypeInRange(self.handle, sym_type, start, length, count) result = [] for i in xrange(0, count.value): result.append(types.Symbol(None, None, None, handle = core.BNNewSymbolReference(syms[i]))) core.BNFreeSymbolList(syms, count.value) return result
[docs] def define_auto_symbol(self, sym): """ ``define_auto_symbol`` adds a symbol to the internal list of automatically discovered Symbol objects. .. warning:: If multiple symbols for the same address are defined, only the most recent symbol will ever be used. :param Symbol sym: the symbol to define :rtype: None """ core.BNDefineAutoSymbol(self.handle, sym.handle)
[docs] def define_auto_symbol_and_var_or_function(self, sym, sym_type, plat=None): """ ``define_auto_symbol_and_var_or_function`` .. warning:: If multiple symbols for the same address are defined, only the most recent symbol will ever be used. :param Symbol sym: the symbol to define :param SymbolType sym_type: Type of symbol being defined :param Platform plat: (optional) platform :rtype: None """ if plat is None: plat = self.plat if plat is not None: plat = plat.handle if sym_type is not None: sym_type = sym_type.handle core.BNDefineAutoSymbolAndVariableOrFunction(self.handle, plat, sym.handle, sym_type)
[docs] def undefine_auto_symbol(self, sym): """ ``undefine_auto_symbol`` removes a symbol from the internal list of automatically discovered Symbol objects. :param Symbol sym: the symbol to undefine :rtype: None """ core.BNUndefineAutoSymbol(self.handle, sym.handle)
[docs] def define_user_symbol(self, sym): """ ``define_user_symbol`` adds a symbol to the internal list of user added Symbol objects. .. warning:: If multiple symbols for the same address are defined, only the most recent symbol will ever be used. :param Symbol sym: the symbol to define :rtype: None """ core.BNDefineUserSymbol(self.handle, sym.handle)
[docs] def undefine_user_symbol(self, sym): """ ``undefine_user_symbol`` removes a symbol from the internal list of user added Symbol objects. :param Symbol sym: the symbol to undefine :rtype: None """ core.BNUndefineUserSymbol(self.handle, sym.handle)
[docs] def define_imported_function(self, import_addr_sym, func): """ ``define_imported_function`` defines an imported Function ``func`` with a ImportedFunctionSymbol type. :param Symbol import_addr_sym: A Symbol object with type ImportedFunctionSymbol :param Function func: A Function object to define as an imported function :rtype: None """ core.BNDefineImportedFunction(self.handle, import_addr_sym.handle, func.handle)
[docs] def is_never_branch_patch_available(self, addr, arch=None): """ ``is_never_branch_patch_available`` queries the architecture plugin to determine if the instruction at the instruction at ``addr`` can be made to **never branch**. The actual logic of which is implemented in the ``perform_is_never_branch_patch_available`` in the corresponding architecture. :param int addr: the virtual address of the instruction to be patched :param Architecture arch: (optional) the architecture of the instructions if different from the default :return: True if the instruction can be patched, False otherwise :rtype: bool :Example: >>> bv.get_disassembly(0x100012ed) 'test eax, eax' >>> bv.is_never_branch_patch_available(0x100012ed) False >>> bv.get_disassembly(0x100012ef) 'jg 0x100012f5' >>> bv.is_never_branch_patch_available(0x100012ef) True >>> """ if arch is None: arch = self.arch return core.BNIsNeverBranchPatchAvailable(self.handle, arch.handle, addr)
[docs] def is_always_branch_patch_available(self, addr, arch=None): """ ``is_always_branch_patch_available`` queries the architecture plugin to determine if the instruction at ``addr`` can be made to **always branch**. The actual logic of which is implemented in the ``perform_is_always_branch_patch_available`` in the corresponding architecture. :param int addr: the virtual address of the instruction to be patched :param Architecture arch: (optional) the architecture for the current view :return: True if the instruction can be patched, False otherwise :rtype: bool :Example: >>> bv.get_disassembly(0x100012ed) 'test eax, eax' >>> bv.is_always_branch_patch_available(0x100012ed) False >>> bv.get_disassembly(0x100012ef) 'jg 0x100012f5' >>> bv.is_always_branch_patch_available(0x100012ef) True >>> """ if arch is None: arch = self.arch return core.BNIsAlwaysBranchPatchAvailable(self.handle, arch.handle, addr)
[docs] def is_invert_branch_patch_available(self, addr, arch=None): """ ``is_invert_branch_patch_available`` queries the architecture plugin to determine if the instruction at ``addr`` is a branch that can be inverted. The actual logic of which is implemented in the ``perform_is_invert_branch_patch_available`` in the corresponding architecture. :param int addr: the virtual address of the instruction to be patched :param Architecture arch: (optional) the architecture of the instructions if different from the default :return: True if the instruction can be patched, False otherwise :rtype: bool :Example: >>> bv.get_disassembly(0x100012ed) 'test eax, eax' >>> bv.is_invert_branch_patch_available(0x100012ed) False >>> bv.get_disassembly(0x100012ef) 'jg 0x100012f5' >>> bv.is_invert_branch_patch_available(0x100012ef) True >>> """ if arch is None: arch = self.arch return core.BNIsInvertBranchPatchAvailable(self.handle, arch.handle, addr)
[docs] def is_skip_and_return_zero_patch_available(self, addr, arch=None): """ ``is_skip_and_return_zero_patch_available`` queries the architecture plugin to determine if the instruction at ``addr`` is similar to an x86 "call" instruction which can be made to return zero. The actual logic of which is implemented in the ``perform_is_skip_and_return_zero_patch_available`` in the corresponding architecture. :param int addr: the virtual address of the instruction to be patched :param Architecture arch: (optional) the architecture of the instructions if different from the default :return: True if the instruction can be patched, False otherwise :rtype: bool :Example: >>> bv.get_disassembly(0x100012f6) 'mov dword [0x10003020], eax' >>> bv.is_skip_and_return_zero_patch_available(0x100012f6) False >>> bv.get_disassembly(0x100012fb) 'call 0x10001629' >>> bv.is_skip_and_return_zero_patch_available(0x100012fb) True >>> """ if arch is None: arch = self.arch return core.BNIsSkipAndReturnZeroPatchAvailable(self.handle, arch.handle, addr)
[docs] def is_skip_and_return_value_patch_available(self, addr, arch=None): """ ``is_skip_and_return_value_patch_available`` queries the architecture plugin to determine if the instruction at ``addr`` is similar to an x86 "call" instruction which can be made to return a value. The actual logic of which is implemented in the ``perform_is_skip_and_return_value_patch_available`` in the corresponding architecture. :param int addr: the virtual address of the instruction to be patched :param Architecture arch: (optional) the architecture of the instructions if different from the default :return: True if the instruction can be patched, False otherwise :rtype: bool :Example: >>> bv.get_disassembly(0x100012f6) 'mov dword [0x10003020], eax' >>> bv.is_skip_and_return_value_patch_available(0x100012f6) False >>> bv.get_disassembly(0x100012fb) 'call 0x10001629' >>> bv.is_skip_and_return_value_patch_available(0x100012fb) True >>> """ if arch is None: arch = self.arch return core.BNIsSkipAndReturnValuePatchAvailable(self.handle, arch.handle, addr)
[docs] def convert_to_nop(self, addr, arch=None): """ ``convert_to_nop`` converts the instruction at virtual address ``addr`` to a nop of the provided architecture. .. note:: This API performs a binary patch, analysis may need to be updated afterward. Additionally the binary\ file must be saved in order to preserve the changes made. :param int addr: virtual address of the instruction to conver to nops :param Architecture arch: (optional) the architecture of the instructions if different from the default :return: True on success, False on falure. :rtype: bool :Example: >>> bv.get_disassembly(0x100012fb) 'call 0x10001629' >>> bv.convert_to_nop(0x100012fb) True >>> #The above 'call' instruction is 5 bytes, a nop in x86 is 1 byte, >>> # thus 5 nops are used: >>> bv.get_disassembly(0x100012fb) 'nop' >>> bv.get_next_disassembly() 'nop' >>> bv.get_next_disassembly() 'nop' >>> bv.get_next_disassembly() 'nop' >>> bv.get_next_disassembly() 'nop' >>> bv.get_next_disassembly() 'mov byte [ebp-0x1c], al' """ if arch is None: arch = self.arch return core.BNConvertToNop(self.handle, arch.handle, addr)
[docs] def always_branch(self, addr, arch=None): """ ``always_branch`` convert the instruction of architecture ``arch`` at the virtual address ``addr`` to an unconditional branch. .. note:: This API performs a binary patch, analysis may need to be updated afterward. Additionally the binary\ file must be saved in order to preserve the changes made. :param int addr: virtual address of the instruction to be modified :param Architecture arch: (optional) the architecture of the instructions if different from the default :return: True on success, False on falure. :rtype: bool :Example: >>> bv.get_disassembly(0x100012ef) 'jg 0x100012f5' >>> bv.always_branch(0x100012ef) True >>> bv.get_disassembly(0x100012ef) 'jmp 0x100012f5' >>> """ if arch is None: arch = self.arch return core.BNAlwaysBranch(self.handle, arch.handle, addr)
[docs] def never_branch(self, addr, arch=None): """ ``never_branch`` convert the branch instruction of architecture ``arch`` at the virtual address ``addr`` to a fall through. .. note:: This API performs a binary patch, analysis may need to be updated afterward. Additionally the binary\ file must be saved in order to preserve the changes made. :param int addr: virtual address of the instruction to be modified :param Architecture arch: (optional) the architecture of the instructions if different from the default :return: True on success, False on falure. :rtype: bool :Example: >>> bv.get_disassembly(0x1000130e) 'jne 0x10001317' >>> bv.never_branch(0x1000130e) True >>> bv.get_disassembly(0x1000130e) 'nop' >>> """ if arch is None: arch = self.arch return core.BNConvertToNop(self.handle, arch.handle, addr)
[docs] def invert_branch(self, addr, arch=None): """ ``invert_branch`` convert the branch instruction of architecture ``arch`` at the virtual address ``addr`` to the inverse branch. .. note:: This API performs a binary patch, analysis may need to be updated afterward. Additionally the binary file must be saved in order to preserve the changes made. :param int addr: virtual address of the instruction to be modified :param Architecture arch: (optional) the architecture of the instructions if different from the default :return: True on success, False on falure. :rtype: bool :Example: >>> bv.get_disassembly(0x1000130e) 'je 0x10001317' >>> bv.invert_branch(0x1000130e) True >>> >>> bv.get_disassembly(0x1000130e) 'jne 0x10001317' >>> """ if arch is None: arch = self.arch return core.BNInvertBranch(self.handle, arch.handle, addr)
[docs] def skip_and_return_value(self, addr, value, arch=None): """ ``skip_and_return_value`` convert the ``call`` instruction of architecture ``arch`` at the virtual address ``addr`` to the equivilent of returning a value. :param int addr: virtual address of the instruction to be modified :param int value: value to make the instruction *return* :param Architecture arch: (optional) the architecture of the instructions if different from the default :return: True on success, False on falure. :rtype: bool :Example: >>> bv.get_disassembly(0x1000132a) 'call 0x1000134a' >>> bv.skip_and_return_value(0x1000132a, 42) True >>> #The return value from x86 functions is stored in eax thus: >>> bv.get_disassembly(0x1000132a) 'mov eax, 0x2a' >>> """ if arch is None: arch = self.arch return core.BNSkipAndReturnValue(self.handle, arch.handle, addr, value)
[docs] def get_instruction_length(self, addr, arch=None): """ ``get_instruction_length`` returns the number of bytes in the instruction of Architecture ``arch`` at the virtual address ``addr`` :param int addr: virtual address of the instruction query :param Architecture arch: (optional) the architecture of the instructions if different from the default :return: Number of bytes in instruction :rtype: int :Example: >>> bv.get_disassembly(0x100012f1) 'xor eax, eax' >>> bv.get_instruction_length(0x100012f1) 2L >>> """ if arch is None: arch = self.arch return core.BNGetInstructionLength(self.handle, arch.handle, addr)
[docs] def notify_data_written(self, offset, length): core.BNNotifyDataWritten(self.handle, offset, length)
[docs] def notify_data_inserted(self, offset, length): core.BNNotifyDataInserted(self.handle, offset, length)
[docs] def notify_data_removed(self, offset, length): core.BNNotifyDataRemoved(self.handle, offset, length)
[docs] def get_strings(self, start = None, length = None): """ ``get_strings`` returns a list of strings defined in the binary in the optional virtual address range: ``start-(start+length)`` :param int start: optional virtual address to start the string list from, defaults to start of the binary :param int length: optional length range to return strings from, defaults to length of the binary :return: a list of all strings or a list of strings defined between ``start`` and ``start+length`` :rtype: list(str()) :Example: >>> bv.get_strings(0x1000004d, 1) [<AsciiString: 0x1000004d, len 0x2c>] >>> """ count = ctypes.c_ulonglong(0) if start is None: strings = core.BNGetStrings(self.handle, count) else: strings = core.BNGetStringsInRange(self.handle, start, length, count) result = [] for i in xrange(0, count.value): result.append(StringReference(StringType(strings[i].type), strings[i].start, strings[i].length)) core.BNFreeStringReferenceList(strings) return result
[docs] def add_analysis_completion_event(self, callback): """ ``add_analysis_completion_event`` sets up a call back function to be called when analysis has been completed. This is helpful when using asynchronously analysis. :param callable() callback: A function to be called with no parameters when analysis has completed. :return: An initialized AnalysisCompletionEvent object. :rtype: AnalysisCompletionEvent :Example: >>> def completionEvent(): ... print "done" ... >>> bv.add_analysis_completion_event(completionEvent) <binaryninja.AnalysisCompletionEvent object at 0x10a2c9f10> >>> bv.update_analysis() done >>> """ return AnalysisCompletionEvent(self, callback)
[docs] def get_next_function_start_after(self, addr): """ ``get_next_function_start_after`` returns the virtual address of the Function that occurs after the virtual address ``addr`` :param int addr: the virtual address to start looking from. :return: the virtual address of the next Function :rtype: int :Example: >>> bv.get_next_function_start_after(bv.entry_point) 268441061L >>> hex(bv.get_next_function_start_after(bv.entry_point)) '0x100015e5L' >>> hex(bv.get_next_function_start_after(0x100015e5)) '0x10001629L' >>> hex(bv.get_next_function_start_after(0x10001629)) '0x1000165eL' >>> """ return core.BNGetNextFunctionStartAfterAddress(self.handle, addr)
[docs] def get_next_basic_block_start_after(self, addr): """ ``get_next_basic_block_start_after`` returns the virtual address of the BasicBlock that occurs after the virtual address ``addr`` :param int addr: the virtual address to start looking from. :return: the virtual address of the next BasicBlock :rtype: int :Example: >>> hex(bv.get_next_basic_block_start_after(bv.entry_point)) '0x100014a8L' >>> hex(bv.get_next_basic_block_start_after(0x100014a8)) '0x100014adL' >>> """ return core.BNGetNextBasicBlockStartAfterAddress(self.handle, addr)
[docs] def get_next_data_after(self, addr): """ ``get_next_data_after`` retrieves the virtual address of the next non-code byte. :param int addr: the virtual address to start looking from. :return: the virtual address of the next data byte which is data, not code :rtype: int :Example: >>> hex(bv.get_next_data_after(0x10000000)) '0x10000001L' """ return core.BNGetNextDataAfterAddress(self.handle, addr)
[docs] def get_next_data_var_after(self, addr): """ ``get_next_data_var_after`` retrieves the next virtual address of the next :py:Class:`DataVariable` :param int addr: the virtual address to start looking from. :return: the virtual address of the next :py:Class:`DataVariable` :rtype: int :Example: >>> hex(bv.get_next_data_var_after(0x10000000)) '0x1000003cL' >>> bv.get_data_var_at(0x1000003c) <var 0x1000003c: int32_t> >>> """ return core.BNGetNextDataVariableAfterAddress(self.handle, addr)
[docs] def get_previous_function_start_before(self, addr): """ ``get_previous_function_start_before`` returns the virtual address of the Function that occurs prior to the virtual address provided :param int addr: the virtual address to start looking from. :return: the virtual address of the previous Function :rtype: int :Example: >>> hex(bv.entry_point) '0x1000149fL' >>> hex(bv.get_next_function_start_after(bv.entry_point)) '0x100015e5L' >>> hex(bv.get_previous_function_start_before(0x100015e5)) '0x1000149fL' >>> """ return core.BNGetPreviousFunctionStartBeforeAddress(self.handle, addr)
[docs] def get_previous_basic_block_start_before(self, addr): """ ``get_previous_basic_block_start_before`` returns the virtual address of the BasicBlock that occurs prior to the provided virtual address :param int addr: the virtual address to start looking from. :return: the virtual address of the previous BasicBlock :rtype: int :Example: >>> hex(bv.entry_point) '0x1000149fL' >>> hex(bv.get_next_basic_block_start_after(bv.entry_point)) '0x100014a8L' >>> hex(bv.get_previous_basic_block_start_before(0x100014a8)) '0x1000149fL' >>> """ return core.BNGetPreviousBasicBlockStartBeforeAddress(self.handle, addr)
[docs] def get_previous_basic_block_end_before(self, addr): """ ``get_previous_basic_block_end_before`` :param int addr: the virtual address to start looking from. :return: the virtual address of the previous BasicBlock end :rtype: int :Example: >>> hex(bv.entry_point) '0x1000149fL' >>> hex(bv.get_next_basic_block_start_after(bv.entry_point)) '0x100014a8L' >>> hex(bv.get_previous_basic_block_end_before(0x100014a8)) '0x100014a8L' """ return core.BNGetPreviousBasicBlockEndBeforeAddress(self.handle, addr)
[docs] def get_previous_data_before(self, addr): """ ``get_previous_data_before`` :param int addr: the virtual address to start looking from. :return: the virtual address of the previous data (non-code) byte :rtype: int :Example: >>> hex(bv.get_previous_data_before(0x1000001)) '0x1000000L' >>> """ return core.BNGetPreviousDataBeforeAddress(self.handle, addr)
[docs] def get_previous_data_var_before(self, addr): """ ``get_previous_data_var_before`` :param int addr: the virtual address to start looking from. :return: the virtual address of the previous :py:Class:`DataVariable` :rtype: int :Example: >>> hex(bv.get_previous_data_var_before(0x1000003c)) '0x10000000L' >>> bv.get_data_var_at(0x10000000) <var 0x10000000: int16_t> >>> """ return core.BNGetPreviousDataVariableBeforeAddress(self.handle, addr)
[docs] def get_linear_disassembly_position_at(self, addr, settings): """ ``get_linear_disassembly_position_at`` instantiates a :py:class:`LinearDisassemblyPosition` object for use in :py:method:`get_previous_linear_disassembly_lines` or :py:method:`get_next_linear_disassembly_lines`. :param int addr: virtual address of linear disassembly position :param DisassemblySettings settings: an instantiated :py:class:`DisassemblySettings` object :return: An instantied :py:class:`LinearDisassemblyPosition` object for the provided virtual address :rtype: LinearDisassemblyPosition :Example: >>> settings = DisassemblySettings() >>> pos = bv.get_linear_disassembly_position_at(0x1000149f, settings) >>> lines = bv.get_previous_linear_disassembly_lines(pos, settings) >>> lines [<0x1000149a: pop esi>, <0x1000149b: pop ebp>, <0x1000149c: retn 0xc>, <0x1000149f: >] """ if settings is not None: settings = settings.handle pos = core.BNGetLinearDisassemblyPositionForAddress(self.handle, addr, settings) func = None block = None if pos.function: func = function.Function(self, pos.function) if pos.block: block = basicblock.BasicBlock(self, pos.block) return lineardisassembly.LinearDisassemblyPosition(func, block, pos.address)
def _get_linear_disassembly_lines(self, api, pos, settings): pos_obj = core.BNLinearDisassemblyPosition() pos_obj.function = None pos_obj.block = None pos_obj.address = pos.address if pos.function is not None: pos_obj.function = core.BNNewFunctionReference(pos.function.handle) if pos.block is not None: pos_obj.block = core.BNNewBasicBlockReference(pos.block.handle) if settings is not None: settings = settings.handle count = ctypes.c_ulonglong(0) lines = api(self.handle, pos_obj, settings, count) result = [] for i in xrange(0, count.value): func = None block = None if lines[i].function: func = function.Function(self, core.BNNewFunctionReference(lines[i].function)) if lines[i].block: block = basicblock.BasicBlock(self, core.BNNewBasicBlockReference(lines[i].block)) addr = lines[i].contents.addr tokens = [] for j in xrange(0, lines[i].contents.count): token_type = InstructionTextTokenType(lines[i].contents.tokens[j].type) text = lines[i].contents.tokens[j].text value = lines[i].contents.tokens[j].value size = lines[i].contents.tokens[j].size operand = lines[i].contents.tokens[j].operand context = lines[i].contents.tokens[j].context address = lines[i].contents.tokens[j].address tokens.append(function.InstructionTextToken(token_type, text, value, size, operand, context, address)) contents = function.DisassemblyTextLine(addr, tokens) result.append(lineardisassembly.LinearDisassemblyLine(lines[i].type, func, block, lines[i].lineOffset, contents)) func = None block = None if pos_obj.function: func = function.Function(self, pos_obj.function) if pos_obj.block: block = basicblock.BasicBlock(self, pos_obj.block) pos.function = func pos.block = block pos.address = pos_obj.address core.BNFreeLinearDisassemblyLines(lines, count.value) return result
[docs] def get_previous_linear_disassembly_lines(self, pos, settings): """ ``get_previous_linear_disassembly_lines`` retrieves a list of :py:class:`LinearDisassemblyLine` objects for the previous disassembly lines, and updates the LinearDisassemblyPosition passed in. This function can be called repeatedly to get more lines of linear disassembly. :param LinearDisassemblyPosition pos: Position to start retrieving linear disassembly lines from :param DisassemblySettings settings: DisassemblySettings display settings for the linear disassembly :return: a list of :py:class:`LinearDisassemblyLine` objects for the previous lines. :Example: >>> settings = DisassemblySettings() >>> pos = bv.get_linear_disassembly_position_at(0x1000149a, settings) >>> bv.get_previous_linear_disassembly_lines(pos, settings) [<0x10001488: push dword [ebp+0x10 {arg_c}]>, ... , <0x1000149a: >] >>> bv.get_previous_linear_disassembly_lines(pos, settings) [<0x10001483: xor eax, eax {0x0}>, ... , <0x10001488: >] """ return self._get_linear_disassembly_lines(core.BNGetPreviousLinearDisassemblyLines, pos, settings)
[docs] def get_next_linear_disassembly_lines(self, pos, settings): """ ``get_next_linear_disassembly_lines`` retrieves a list of :py:class:`LinearDisassemblyLine` objects for the next disassembly lines, and updates the LinearDisassemblyPosition passed in. This function can be called repeatedly to get more lines of linear disassembly. :param LinearDisassemblyPosition pos: Position to start retrieving linear disassembly lines from :param DisassemblySettings settings: DisassemblySettings display settings for the linear disassembly :return: a list of :py:class:`LinearDisassemblyLine` objects for the next lines. :Example: >>> settings = DisassemblySettings() >>> pos = bv.get_linear_disassembly_position_at(0x10001483, settings) >>> bv.get_next_linear_disassembly_lines(pos, settings) [<0x10001483: xor eax, eax {0x0}>, <0x10001485: inc eax {0x1}>, ... , <0x10001488: >] >>> bv.get_next_linear_disassembly_lines(pos, settings) [<0x10001488: push dword [ebp+0x10 {arg_c}]>, ... , <0x1000149a: >] >>> """ return self._get_linear_disassembly_lines(core.BNGetNextLinearDisassemblyLines, pos, settings)
[docs] def get_linear_disassembly(self, settings): """ ``get_linear_disassembly`` gets an iterator for all lines in the linear disassembly of the view for the given disassembly settings. .. note:: linear_disassembly doesn't just return disassembly it will return a single line from the linear view,\ and thus will contain both data views, and disassembly. :param DisassemblySettings settings: instance specifying the desired output formatting. :return: An iterator containing formatted dissassembly lines. :rtype: LinearDisassemblyIterator :Example: >>> settings = DisassemblySettings() >>> lines = bv.get_linear_disassembly(settings) >>> for line in lines: ... print line ... break ... cf fa ed fe 07 00 00 01 ........ """ class LinearDisassemblyIterator(object): def __init__(self, view, settings): self.view = view self.settings = settings def __iter__(self): pos = self.view.get_linear_disassembly_position_at(self.view.start, self.settings) while True: lines = self.view.get_next_linear_disassembly_lines(pos, self.settings) if len(lines) == 0: break for line in lines: yield line return iter(LinearDisassemblyIterator(self, settings))
[docs] def parse_type_string(self, text): """ ``parse_type_string`` converts `C-style` string into a :py:Class:`Type`. :param str text: `C-style` string of type to create :return: A tuple of a :py:Class:`Type` and type name :rtype: tuple(Type, QualifiedName) :Example: >>> bv.parse_type_string("int foo") (<type: int32_t>, 'foo') >>> """ result = core.BNQualifiedNameAndType() errors = ctypes.c_char_p() if not core.BNParseTypeString(self.handle, text, result, errors): error_str = errors.value core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) raise SyntaxError(error_str) type_obj = types.Type(core.BNNewTypeReference(result.type)) name = types.QualifiedName._from_core_struct(result.name) core.BNFreeQualifiedNameAndType(result) return type_obj, name
[docs] def get_type_by_name(self, name): """ ``get_type_by_name`` returns the defined type whose name corresponds with the provided ``name`` :param QualifiedName name: Type name to lookup :return: A :py:Class:`Type` or None if the type does not exist :rtype: Type or None :Example: >>> type, name = bv.parse_type_string("int foo") >>> bv.define_user_type(name, type) >>> bv.get_type_by_name(name) <type: int32_t> >>> """ name = types.QualifiedName(name)._get_core_struct() obj = core.BNGetAnalysisTypeByName(self.handle, name) if not obj: return None return types.Type(obj)
[docs] def get_type_by_id(self, id): """ ``get_type_by_id`` returns the defined type whose unique identifier corresponds with the provided ``id`` :param str id: Unique identifier to lookup :return: A :py:Class:`Type` or None if the type does not exist :rtype: Type or None :Example: >>> type, name = bv.parse_type_string("int foo") >>> type_id = Type.generate_auto_type_id("source", name) >>> bv.define_type(type_id, name, type) >>> bv.get_type_by_id(type_id) <type: int32_t> >>> """ obj = core.BNGetAnalysisTypeById(self.handle, id) if not obj: return None return types.Type(obj)
[docs] def get_type_name_by_id(self, id): """ ``get_type_name_by_id`` returns the defined type name whose unique identifier corresponds with the provided ``id`` :param str id: Unique identifier to lookup :return: A QualifiedName or None if the type does not exist :rtype: QualifiedName or None :Example: >>> type, name = bv.parse_type_string("int foo") >>> type_id = Type.generate_auto_type_id("source", name) >>> bv.define_type(type_id, name, type) 'foo' >>> bv.get_type_name_by_id(type_id) 'foo' >>> """ name = core.BNGetAnalysisTypeNameById(self.handle, id) result = types.QualifiedName._from_core_struct(name) core.BNFreeQualifiedName(name) if len(result) == 0: return None return result
[docs] def get_type_id(self, name): """ ``get_type_id`` returns the unique indentifier of the defined type whose name corresponds with the provided ``name`` :param QualifiedName name: Type name to lookup :return: The unique identifier of the type :rtype: str :Example: >>> type, name = bv.parse_type_string("int foo") >>> type_id = Type.generate_auto_type_id("source", name) >>> registered_name = bv.define_type(type_id, name, type) >>> bv.get_type_id(registered_name) == type_id True >>> """ name = types.QualifiedName(name)._get_core_struct() return core.BNGetAnalysisTypeId(self.handle, name)
[docs] def is_type_auto_defined(self, name): """ ``is_type_auto_defined`` queries the user type list of name. If name is not in the *user* type list then the name is considered an *auto* type. :param QualifiedName name: Name of type to query :return: True if the type is not a *user* type. False if the type is a *user* type. :Example: >>> bv.is_type_auto_defined("foo") True >>> bv.define_user_type("foo", bv.parse_type_string("struct {int x,y;}")[0]) >>> bv.is_type_auto_defined("foo") False >>> """ name = types.QualifiedName(name)._get_core_struct() return core.BNIsAnalysisTypeAutoDefined(self.handle, name)
[docs] def define_type(self, type_id, default_name, type_obj): """ ``define_type`` registers a :py:Class:`Type` ``type_obj`` of the given ``name`` in the global list of types for the current :py:Class:`BinaryView`. This method should only be used for automatically generated types. :param str type_id: Unique identifier for the automatically generated type :param QualifiedName default_name: Name of the type to be registered :param Type type_obj: Type object to be registered :return: Registered name of the type. May not be the same as the requested name if the user has renamed types. :rtype: QualifiedName :Example: >>> type, name = bv.parse_type_string("int foo") >>> registered_name = bv.define_type(Type.generate_auto_type_id("source", name), name, type) >>> bv.get_type_by_name(registered_name) <type: int32_t> """ name = types.QualifiedName(default_name)._get_core_struct() reg_name = core.BNDefineAnalysisType(self.handle, type_id, name, type_obj.handle) result = types.QualifiedName._from_core_struct(reg_name) core.BNFreeQualifiedName(reg_name) return result
[docs] def define_user_type(self, name, type_obj): """ ``define_user_type`` registers a :py:Class:`Type` ``type_obj`` of the given ``name`` in the global list of user types for the current :py:Class:`BinaryView`. :param QualifiedName name: Name of the user type to be registered :param Type type_obj: Type object to be registered :rtype: None :Example: >>> type, name = bv.parse_type_string("int foo") >>> bv.define_user_type(name, type) >>> bv.get_type_by_name(name) <type: int32_t> """ name = types.QualifiedName(name)._get_core_struct() core.BNDefineUserAnalysisType(self.handle, name, type_obj.handle)
[docs] def undefine_type(self, type_id): """ ``undefine_type`` removes a :py:Class:`Type` from the global list of types for the current :py:Class:`BinaryView` :param str type_id: Unique identifier of type to be undefined :rtype: None :Example: >>> type, name = bv.parse_type_string("int foo") >>> type_id = Type.generate_auto_type_id("source", name) >>> bv.define_type(type_id, name, type) >>> bv.get_type_by_name(name) <type: int32_t> >>> bv.undefine_type(type_id) >>> bv.get_type_by_name(name) >>> """ core.BNUndefineAnalysisType(self.handle, type_id)
[docs] def undefine_user_type(self, name): """ ``undefine_user_type`` removes a :py:Class:`Type` from the global list of user types for the current :py:Class:`BinaryView` :param QualifiedName name: Name of user type to be undefined :rtype: None :Example: >>> type, name = bv.parse_type_string("int foo") >>> bv.define_user_type(name, type) >>> bv.get_type_by_name(name) <type: int32_t> >>> bv.undefine_user_type(name) >>> bv.get_type_by_name(name) >>> """ name = types.QualifiedName(name)._get_core_struct() core.BNUndefineUserAnalysisType(self.handle, name)
[docs] def rename_type(self, old_name, new_name): """ ``rename_type`` renames a type in the global list of types for the current :py:Class:`BinaryView` :param QualifiedName old_name: Existing name of type to be renamed :param QualifiedName new_name: New name of type to be renamed :rtype: None :Example: >>> type, name = bv.parse_type_string("int foo") >>> bv.define_user_type(name, type) >>> bv.get_type_by_name("foo") <type: int32_t> >>> bv.rename_type("foo", "bar") >>> bv.get_type_by_name("bar") <type: int32_t> >>> """ old_name = types.QualifiedName(old_name)._get_core_struct() new_name = types.QualifiedName(new_name)._get_core_struct() core.BNRenameAnalysisType(self.handle, old_name, new_name)
[docs] def register_platform_types(self, platform): """ ``register_platform_types`` ensures that the platform-specific types for a :py:Class:`Platform` are available for the current :py:Class:`BinaryView`. This is automatically performed when adding a new function or setting the default platform. :param Platform platform: Platform containing types to be registered :rtype: None :Example: >>> platform = Platform["linux-x86"] >>> bv.register_platform_types(platform) >>> """ core.BNRegisterPlatformTypes(self.handle, platform.handle)
[docs] def find_next_data(self, start, data, flags = 0): """ ``find_next_data`` searchs for the bytes in data starting at the virtual address ``start`` either, case-sensitive, or case-insensitive. :param int start: virtual address to start searching from. :param str data: bytes to search for :param FindFlags flags: case-sensitivity flag, one of the following: ==================== ====================== FindFlags Description ==================== ====================== NoFindFlags Case-sensitive find FindCaseInsensitive Case-insensitive find ==================== ====================== """ buf = databuffer.DataBuffer(str(data)) result = ctypes.c_ulonglong() if not core.BNFindNextData(self.handle, start, buf.handle, result, flags): return None return result.value
[docs] def reanalyze(self): """ ``reanalyze`` causes all functions to be reanalyzed. This function does not wait for the analysis to finish. :rtype: None """ core.BNReanalyzeAllFunctions(self.handle)
[docs] def show_plain_text_report(self, title, contents): core.BNShowPlainTextReport(self.handle, title, contents)
[docs] def show_markdown_report(self, title, contents, plaintext = ""): core.BNShowMarkdownReport(self.handle, title, contents, plaintext)
[docs] def show_html_report(self, title, contents, plaintext = ""): core.BNShowHTMLReport(self.handle, title, contents, plaintext)
[docs] def get_address_input(self, prompt, title, current_address = None): if current_address is None: current_address = self.file.offset value = ctypes.c_ulonglong() if not core.BNGetAddressInput(value, prompt, title, self.handle, current_address): return None return value.value
[docs] def add_auto_segment(self, start, length, data_offset, data_length, flags): core.BNAddAutoSegment(self.handle, start, length, data_offset, data_length, flags)
[docs] def remove_auto_segment(self, start, length): core.BNRemoveAutoSegment(self.handle, start, length)
[docs] def add_user_segment(self, start, length, data_offset, data_length, flags): core.BNAddUserSegment(self.handle, start, length, data_offset, data_length, flags)
[docs] def remove_user_segment(self, start, length): core.BNRemoveUserSegment(self.handle, start, length)
[docs] def get_segment_at(self, addr): segment = core.BNSegment() if not core.BNGetSegmentAt(self.handle, addr, segment): return None result = Segment(segment.start, segment.length, segment.dataOffset, segment.dataLength, segment.flags) return result
[docs] def get_address_for_data_offset(self, offset): address = ctypes.c_ulonglong() if not core.BNGetAddressForDataOffset(self.handle, offset, address): return None return address.value
[docs] def add_auto_section(self, name, start, length, type = "", align = 1, entry_size = 1, linked_section = "", info_section = "", info_data = 0): core.BNAddAutoSection(self.handle, name, start, length, type, align, entry_size, linked_section, info_section, info_data)
[docs] def remove_auto_section(self, name): core.BNRemoveAutoSection(self.handle, name)
[docs] def add_user_section(self, name, start, length, type = "", align = 1, entry_size = 1, linked_section = "", info_section = "", info_data = 0): core.BNAddUserSection(self.handle, name, start, length, type, align, entry_size, linked_section, info_section, info_data)
[docs] def remove_user_section(self, name): core.BNRemoveUserSection(self.handle, name)
[docs] def get_sections_at(self, addr): count = ctypes.c_ulonglong(0) section_list = core.BNGetSectionsAt(self.handle, addr, count) result = [] for i in xrange(0, count.value): result.append(Section(section_list[i].name, section_list[i].type, section_list[i].start, section_list[i].length, section_list[i].linkedSection, section_list[i].infoSection, section_list[i].infoData, section_list[i].align, section_list[i].entrySize)) core.BNFreeSectionList(section_list, count.value) return result
[docs] def get_section_by_name(self, name): section = core.BNSection() if not core.BNGetSectionByName(self.handle, name, section): return None result = Section(section.name, section.type, section.start, section.length, section.linkedSection, section.infoSection, section.infoData, section.align, section.entrySize) core.BNFreeSection(section) return result
[docs] def get_unique_section_names(self, name_list): incoming_names = (ctypes.c_char_p * len(name_list))() for i in xrange(0, len(name_list)): incoming_names[i] = name_list[i] outgoing_names = core.BNGetUniqueSectionNames(self.handle, incoming_names, len(name_list)) result = [] for i in xrange(0, len(name_list)): result.append(str(outgoing_names[i])) core.BNFreeStringList(outgoing_names, len(name_list)) return result
def __setattr__(self, name, value): try: object.__setattr__(self, name, value) except AttributeError: raise AttributeError("attribute '%s' is read only" % name)
[docs]class BinaryReader(object): """ ``class BinaryReader`` is a convenience class for reading binary data. BinaryReader can be instantiated as follows and the rest of the document will start from this context :: >>> from binaryninja import * >>> bv = BinaryViewType['Mach-O'].open("/bin/ls") >>> br = BinaryReader(bv) >>> hex(br.read32()) '0xfeedfacfL' >>> Or using the optional endian parameter :: >>> from binaryninja import * >>> br = BinaryReader(bv, Endianness.BigEndian) >>> hex(br.read32()) '0xcffaedfeL' >>> """
[docs] def __init__(self, view, endian = None): self.handle = core.BNCreateBinaryReader(view.handle) if endian is None: core.BNSetBinaryReaderEndianness(self.handle, view.endianness) else: core.BNSetBinaryReaderEndianness(self.handle, endian)
def __del__(self): core.BNFreeBinaryReader(self.handle) def __eq__(self, value): if not isinstance(value, BinaryReader): return False return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents) def __ne__(self, value): if not isinstance(value, BinaryReader): return True return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) @property def endianness(self): """ The Endianness to read data. (read/write) :getter: returns the endianness of the reader :setter: sets the endianness of the reader (BigEndian or LittleEndian) :type: Endianness """ return core.BNGetBinaryReaderEndianness(self.handle) @endianness.setter def endianness(self, value): core.BNSetBinaryReaderEndianness(self.handle, value) @property def offset(self): """ The current read offset (read/write). :getter: returns the current internal offset :setter: sets the internal offset :type: int """ return core.BNGetReaderPosition(self.handle) @offset.setter def offset(self, value): core.BNSeekBinaryReader(self.handle, value) @property def eof(self): """ Is end of file (read-only) :getter: returns boolean, true if end of file, false otherwise :type: bool """ return core.BNIsEndOfFile(self.handle)
[docs] def read(self, length): """ ``read`` returns ``length`` bytes read from the current offset, adding ``length`` to offset. :param int length: number of bytes to read. :return: ``length`` bytes from current offset :rtype: str, or None on failure :Example: >>> br.read(8) '\\xcf\\xfa\\xed\\xfe\\x07\\x00\\x00\\x01' >>> """ dest = ctypes.create_string_buffer(length) if not core.BNReadData(self.handle, dest, length): return None return dest.raw
[docs] def read8(self): """ ``read8`` returns a one byte integer from offet incrementing the offset. :return: byte at offset. :rtype: int, or None on failure :Example: >>> br.seek(0x100000000) >>> br.read8() 207 >>> """ result = ctypes.c_ubyte() if not core.BNRead8(self.handle, result): return None return result.value
[docs] def read16(self): """ ``read16`` returns a two byte integer from offet incrementing the offset by two, using specified endianness. :return: a two byte integer at offset. :rtype: int, or None on failure :Example: >>> br.seek(0x100000000) >>> hex(br.read16()) '0xfacf' >>> """ result = ctypes.c_ushort() if not core.BNRead16(self.handle, result): return None return result.value
[docs] def read32(self): """ ``read32`` returns a four byte integer from offet incrementing the offset by four, using specified endianness. :return: a four byte integer at offset. :rtype: int, or None on failure :Example: >>> br.seek(0x100000000) >>> hex(br.read32()) '0xfeedfacfL' >>> """ result = ctypes.c_uint() if not core.BNRead32(self.handle, result): return None return result.value
[docs] def read64(self): """ ``read64`` returns an eight byte integer from offet incrementing the offset by eight, using specified endianness. :return: an eight byte integer at offset. :rtype: int, or None on failure :Example: >>> br.seek(0x100000000) >>> hex(br.read64()) '0x1000007feedfacfL' >>> """ result = ctypes.c_ulonglong() if not core.BNRead64(self.handle, result): return None return result.value
[docs] def read16le(self): """ ``read16le`` returns a two byte little endian integer from offet incrementing the offset by two. :return: a two byte integer at offset. :rtype: int, or None on failure :Exmaple: >>> br.seek(0x100000000) >>> hex(br.read16le()) '0xfacf' >>> """ result = self.read(2) if (result is None) or (len(result) != 2): return None return struct.unpack("<H", result)[0]
[docs] def read32le(self): """ ``read32le`` returns a four byte little endian integer from offet incrementing the offset by four. :return: a four byte integer at offset. :rtype: int, or None on failure :Example: >>> br.seek(0x100000000) >>> hex(br.read32le()) '0xfeedfacf' >>> """ result = self.read(4) if (result is None) or (len(result) != 4): return None return struct.unpack("<I", result)[0]
[docs] def read64le(self): """ ``read64le`` returns an eight byte little endian integer from offet incrementing the offset by eight. :return: a eight byte integer at offset. :rtype: int, or None on failure :Example: >>> br.seek(0x100000000) >>> hex(br.read64le()) '0x1000007feedfacf' >>> """ result = self.read(8) if (result is None) or (len(result) != 8): return None return struct.unpack("<Q", result)[0]
[docs] def read16be(self): """ ``read16be`` returns a two byte big endian integer from offet incrementing the offset by two. :return: a two byte integer at offset. :rtype: int, or None on failure :Example: >>> br.seek(0x100000000) >>> hex(br.read16be()) '0xcffa' >>> """ result = self.read(2) if (result is None) or (len(result) != 2): return None return struct.unpack(">H", result)[0]
[docs] def read32be(self): """ ``read32be`` returns a four byte big endian integer from offet incrementing the offset by four. :return: a four byte integer at offset. :rtype: int, or None on failure :Example: >>> br.seek(0x100000000) >>> hex(br.read32be()) '0xcffaedfe' >>> """ result = self.read(4) if (result is None) or (len(result) != 4): return None return struct.unpack(">I", result)[0]
[docs] def read64be(self): """ ``read64be`` returns an eight byte big endian integer from offet incrementing the offset by eight. :return: a eight byte integer at offset. :rtype: int, or None on failure :Example: >>> br.seek(0x100000000) >>> hex(br.read64be()) '0xcffaedfe07000001L' """ result = self.read(8) if (result is None) or (len(result) != 8): return None return struct.unpack(">Q", result)[0]
[docs] def seek(self, offset): """ ``seek`` update internal offset to ``offset``. :param int offset: offset to set the internal offset to :rtype: None :Example: >>> hex(br.offset) '0x100000008L' >>> br.seek(0x100000000) >>> hex(br.offset) '0x100000000L' >>> """ core.BNSeekBinaryReader(self.handle, offset)
[docs] def seek_relative(self, offset): """ ``seek_relative`` updates the internal offset by ``offset``. :param int offset: offset to add to the internal offset :rtype: None :Example: >>> hex(br.offset) '0x100000008L' >>> br.seek_relative(-8) >>> hex(br.offset) '0x100000000L' >>> """ core.BNSeekBinaryReaderRelative(self.handle, offset)
def __setattr__(self, name, value): try: object.__setattr__(self, name, value) except AttributeError: raise AttributeError("attribute '%s' is read only" % name)
[docs]class BinaryWriter(object): """ ``class BinaryWriter`` is a convenience class for writing binary data. BinaryWriter can be instantiated as follows and the rest of the document will start from this context :: >>> from binaryninja import * >>> bv = BinaryViewType['Mach-O'].open("/bin/ls") >>> br = BinaryReader(bv) >>> bw = BinaryWriter(bv) >>> Or using the optional endian parameter :: >>> from binaryninja import * >>> br = BinaryReader(bv, Endianness.BigEndian) >>> bw = BinaryWriter(bv, Endianness.BigEndian) >>> """
[docs] def __init__(self, view, endian = None): self.handle = core.BNCreateBinaryWriter(view.handle) if endian is None: core.BNSetBinaryWriterEndianness(self.handle, view.endianness) else: core.BNSetBinaryWriterEndianness(self.handle, endian)
def __del__(self): core.BNFreeBinaryWriter(self.handle) def __eq__(self, value): if not isinstance(value, BinaryWriter): return False return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents) def __ne__(self, value): if not isinstance(value, BinaryWriter): return True return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) @property def endianness(self): """ The Endianness to written data. (read/write) :getter: returns the endianness of the reader :setter: sets the endianness of the reader (BigEndian or LittleEndian) :type: Endianness """ return core.BNGetBinaryWriterEndianness(self.handle) @endianness.setter def endianness(self, value): core.BNSetBinaryWriterEndianness(self.handle, value) @property def offset(self): """ The current write offset (read/write). :getter: returns the current internal offset :setter: sets the internal offset :type: int """ return core.BNGetWriterPosition(self.handle) @offset.setter def offset(self, value): core.BNSeekBinaryWriter(self.handle, value)
[docs] def write(self, value): """ ``write`` writes ``len(value)`` bytes to the internal offset, without regard to endianness. :param str value: bytes to be written at current offset :return: boolean True on success, False on failure. :rtype: bool :Example: >>> bw.write("AAAA") True >>> br.read(4) 'AAAA' >>> """ value = str(value) buf = ctypes.create_string_buffer(len(value)) ctypes.memmove(buf, value, len(value)) return core.BNWriteData(self.handle, buf, len(value))
[docs] def write8(self, value): """ ``write8`` lowest order byte from the integer ``value`` to the current offset. :param str value: bytes to be written at current offset :return: boolean :rtype: int :Example: >>> bw.write8(0x42) True >>> br.read(1) 'B' >>> """ return core.BNWrite8(self.handle, value)
[docs] def write16(self, value): """ ``write16`` writes the lowest order two bytes from the integer ``value`` to the current offset, using internal endianness. :param int value: integer value to write. :return: boolean True on success, False on failure. :rtype: bool """ return core.BNWrite16(self.handle, value)
[docs] def write32(self, value): """ ``write32`` writes the lowest order four bytes from the integer ``value`` to the current offset, using internal endianness. :param int value: integer value to write. :return: boolean True on success, False on failure. :rtype: bool """ return core.BNWrite32(self.handle, value)
[docs] def write64(self, value): """ ``write64`` writes the lowest order eight bytes from the integer ``value`` to the current offset, using internal endianness. :param int value: integer value to write. :return: boolean True on success, False on failure. :rtype: bool """ return core.BNWrite64(self.handle, value)
[docs] def write16le(self, value): """ ``write16le`` writes the lowest order two bytes from the little endian integer ``value`` to the current offset. :param int value: integer value to write. :return: boolean True on success, False on failure. :rtype: bool """ value = struct.pack("<H", value) return self.write(value)
[docs] def write32le(self, value): """ ``write32le`` writes the lowest order four bytes from the little endian integer ``value`` to the current offset. :param int value: integer value to write. :return: boolean True on success, False on failure. :rtype: bool """ value = struct.pack("<I", value) return self.write(value)
[docs] def write64le(self, value): """ ``write64le`` writes the lowest order eight bytes from the little endian integer ``value`` to the current offset. :param int value: integer value to write. :return: boolean True on success, False on failure. :rtype: bool """ value = struct.pack("<Q", value) return self.write(value)
[docs] def write16be(self, value): """ ``write16be`` writes the lowest order two bytes from the big endian integer ``value`` to the current offset. :param int value: integer value to write. :return: boolean True on success, False on failure. :rtype: bool """ value = struct.pack(">H", value) return self.write(value)
[docs] def write32be(self, value): """ ``write32be`` writes the lowest order four bytes from the big endian integer ``value`` to the current offset. :param int value: integer value to write. :return: boolean True on success, False on failure. :rtype: bool """ value = struct.pack(">I", value) return self.write(value)
[docs] def write64be(self, value): """ ``write64be`` writes the lowest order eight bytes from the big endian integer ``value`` to the current offset. :param int value: integer value to write. :return: boolean True on success, False on failure. :rtype: bool """ value = struct.pack(">Q", value) return self.write(value)
[docs] def seek(self, offset): """ ``seek`` update internal offset to ``offset``. :param int offset: offset to set the internal offset to :rtype: None :Example: >>> hex(bw.offset) '0x100000008L' >>> bw.seek(0x100000000) >>> hex(bw.offset) '0x100000000L' >>> """ core.BNSeekBinaryWriter(self.handle, offset)
[docs] def seek_relative(self, offset): """ ``seek_relative`` updates the internal offset by ``offset``. :param int offset: offset to add to the internal offset :rtype: None :Example: >>> hex(bw.offset) '0x100000008L' >>> bw.seek_relative(-8) >>> hex(bw.offset) '0x100000000L' >>> """ core.BNSeekBinaryWriterRelative(self.handle, offset)