diff options
| author | plafosse <peter@vector35.com> | 2016-10-28 20:16:37 -0400 |
|---|---|---|
| committer | plafosse <peter@vector35.com> | 2016-10-28 20:16:37 -0400 |
| commit | 5e4cca1f1796bec109adacdb049d9e34c17656eb (patch) | |
| tree | 68d9effb24f5fae58c1016d992f9af57c18024cc /python/binaryview.py | |
| parent | 253fc58d774f74edd47724eaec0197fc41561112 (diff) | |
Refactor python api into separate files and add Enumeration support. Also fixed bugs found with pyflakes
Diffstat (limited to 'python/binaryview.py')
| -rw-r--r-- | python/binaryview.py | 3515 |
1 files changed, 3515 insertions, 0 deletions
diff --git a/python/binaryview.py b/python/binaryview.py new file mode 100644 index 00000000..76def834 --- /dev/null +++ b/python/binaryview.py @@ -0,0 +1,3515 @@ +# Copyright (c) 2015-2016 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 +import function +import startup +import architecture +import platform +import associateddatastore +import fileaccessor +import filemetadata +import log +import databuffer +import basicblock +import bntype +import lineardisassembly + + +class BinaryDataNotification: + def data_written(self, view, offset, length): + pass + + def data_inserted(self, view, offset, length): + pass + + def data_removed(self, view, offset, length): + pass + + def function_added(self, view, func): + pass + + def function_removed(self, view, func): + pass + + def function_updated(self, view, func): + pass + + def data_var_added(self, view, var): + pass + + def data_var_removed(self, view, var): + pass + + def data_var_updated(self, view, var): + pass + + def string_found(self, view, string_type, offset, length): + pass + + def string_removed(self, view, string_type, offset, length): + pass + + +class StringReference(object): + 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) + + +class AnalysisCompletionEvent(object): + 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 + + def cancel(self): + self.callback = self._empty_callback + core.BNCancelAnalysisCompletionEvent(self.handle) + + +class AnalysisProgress(object): + def __init__(self, state, count, total): + self.state = state + self.count = count + self.total = total + + def __str__(self): + if self.state == core.BNAnalysisState.DisassembleState: + return "Disassembling (%d/%d)" % (self.count, self.total) + if self.state == core.BNAnalysisState.AnalyzeState: + return "Analyzing (%d/%d)" % (self.count, self.total) + return "Idle" + + def __repr__(self): + return "<progress: %s>" % str(self) + + +class DataVariable(object): + 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)) + + +class BinaryDataNotificationCallbacks(object): + 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) + + 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.address + var_type = bntype.Type(core.BNNewTypeReference(var.type)) + auto_discovered = var.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.address + var_type = bntype.Type(core.BNNewTypeReference(var.type)) + auto_discovered = var.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.address + var_type = bntype.Type(core.BNNewTypeReference(var.type)) + auto_discovered = var.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, core.BNStringType(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, core.BNStringType(string_type), offset, length) + 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) + + def __setattr__(self, name, value): + try: + type.__setattr__(self, name, value) + except AttributeError: + raise AttributeError("attribute '%s' is read only" % name) + + +class BinaryViewType(object): + __metaclass__ = _BinaryViewTypeMetaclass + + def __init__(self, handle): + self.handle = core.handle_of_type(handle, core.BNBinaryViewType) + + @property + def name(self): + """Binary View 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 + + 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) + + def open(self, src, file_metadata=None): + data = BinaryView.open(src, file_metadata) + if data is None: + return None + return self.create(data) + + def is_valid_for_data(self, data): + return core.BNIsBinaryViewTypeValidForData(self.handle, data.handle) + + def register_arch(self, ident, endian, arch): + core.BNRegisterArchitectureForViewType(self.handle, ident, endian, arch.handle) + + def get_arch(self, ident, endian): + arch = core.BNGetArchitectureForViewType(self.handle, ident, endian) + if arch is None: + return None + return architecture.Architecture(arch) + + def register_platform(self, ident, arch, plat): + core.BNRegisterPlatformForViewType(self.handle, ident, arch.handle, plat.handle) + + def register_default_platform(self, arch, plat): + core.BNRegisterDefaultPlatformForViewType(self.handle, arch.handle, plat.handle) + + 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) + + def __setattr__(self, name, value): + try: + object.__setattr__(self, name, value) + except AttributeError: + raise AttributeError("attribute '%s' is read only" % name) + + +class Segment(object): + 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 & core.BNSegmentFlag.SegmentReadable) != 0 else "-", + "w" if (self.flags & core.BNSegmentFlag.SegmentWritable) != 0 else "-", + "x" if (self.flags & core.BNSegmentFlag.SegmentExecutable) != 0 else "-") + + +class Section(object): + 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) + + +class AddressRange(object): + 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 = {} + + +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 = {} + + 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)) + 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 + + @classmethod + 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 + 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 + 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 + def set_default_session_data(cls, 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 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 = function.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 = bntype.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): + result[type_list[i].name] = bntype.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 core.BNModificationStatus.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 core.BNEndianness.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 + + def init(self): + return True + + 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, self.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() + + 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, self.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() + + def perform_save(self, accessor): + if self.parent_view is not None: + return self.parent_view.save(accessor) + return False + + @abc.abstractmethod + def perform_get_address_size(self): + raise NotImplementedError + + 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 + + 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 "" + + 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 + + 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 + + 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 + + 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: BNModificationStatus + """ + return core.BNModificationStatus.Original + + 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) + + 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) + + 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) + + def perform_is_offset_executable(self, addr): + """ + ``perform_is_offset_writable`` 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) + + 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 + + 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 + + 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 + + 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 + + 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 ``core.BNEndianness.LittleEndian`` or ``core.BNEndianness.BigEndian`` + :rtype: BNEndianness + """ + return core.BNEndianness.LittleEndian + + def create_database(self, filename, progress_func=None): + """ + ``perform_get_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) + + 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) + + 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) + + 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(bv.arch, 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() + + def add_undo_action(self, action): + core.BNAddUndoAction(self.handle, action.__class__.name, action._cb) + + 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(bv.arch, 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() + + 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(bv.arch, 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() + + 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(bv.arch, 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() + + def navigate(self, view, offset): + self.file.navigate(view, offset) + + 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) + + 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) + + 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) + + 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) + + 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 core.BNModificationStatus. + + :param int addr: virtual address to get modification from + :param int length: optional length of modification + :return: Either core.BNModificationStatus of the byte at ``addr``, or string of modified bytes at ``addr`` + :rtype: core.BNModificationStatus or str + """ + if length is None: + return core.BNGetModification(self.handle, addr) + data = (core.BNModificationStatus * length)() + length = core.BNGetModificationArray(self.handle, addr, data, length) + return data[0:length] + + 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) + + 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) + + 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) + + 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) + + 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)) + + def register_notification(self, notify): + cb = BinaryDataNotificationCallbacks(self, notify) + cb._register() + self.notifications[notify] = cb + + def unregister_notification(self, notify): + if notify in self.notifications: + self.notifications[notify]._unregister() + del self.notifications[notify] + + def add_function(self, plat, addr): + """ + ``add_function`` add a new function of the given ``plat`` at the virtual address ``addr`` + + :param Platform plat: Platform for the function to be added + :param int addr: virtual address of the function to be added + :rtype: None + :Example: + + >>> bv.add_function(bv.plat, 1) + >>> bv.functions + [<func: x86_64@0x1>] + + """ + core.BNAddFunctionForAnalysis(self.handle, plat.handle, addr) + + def add_entry_point(self, plat, addr): + """ + ``add_entry_point`` adds an virtual address to start analysis from for a given plat. + + :param Platform plat: Platform for the entry point analysis + :param int addr: virtual address to start analysis from + :rtype: None + :Example: + >>> bv.add_entry_point(bv.plat, 0xdeadbeef) + >>> + """ + core.BNAddEntryPointForAnalysis(self.handle, plat.handle, addr) + + 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: x86_64@0x1>] + >>> bv.remove_function(bv.functions[0]) + >>> bv.functions + [] + """ + core.BNRemoveAnalysisFunction(self.handle, func.handle) + + def create_user_function(self, plat, addr): + """ + ``create_user_function`` add a new *user* function of the given ``plat`` at the virtual address ``addr`` + + :param Platform plat: Platform for the function to be added + :param int addr: virtual address of the *user* function to be added + :rtype: None + :Example: + + >>> bv.create_user_function(bv.plat, 1) + >>> bv.functions + [<func: x86_64@0x1>] + + """ + core.BNCreateUserFunction(self.handle, plat.handle, addr) + + 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: x86_64@0x1>] + >>> bv.remove_user_function(bv.functions[0]) + >>> bv.functions + [] + """ + core.BNRemoveUserFunction(self.handle, func.handle) + + 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) + + 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: + 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() + + def abort_analysis(self): + """ + ``abort_analysis`` will abort the currently running analysis. + + :rtype: None + """ + core.BNAbortAnalysis(self.handle) + + 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) + + def define_user_data_var(self, addr, var_type): + """ + ``define_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) + + 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) + + def undefine_user_data_var(self, addr): + """ + ``undefine_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) + + 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, type.Type(var.type), var.autoDiscovered) + + def get_function_at(self, plat, addr): + """ + ``get_function_at`` gets a binaryninja.Function object for the function at the virtual address ``addr``: + + :param binaryninja.Platform plat: plat of the desired function + :param int addr: virtual address 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.plat, bv.entry_point) + <func: x86_64@0x100001174> + >>> + """ + func = core.BNGetAnalysisFunction(self.handle, plat.handle, addr) + if func is None: + return None + return function.Function(self, func) + + 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 + + def get_recent_function_at(self, addr): + func = core.BNGetRecentAnalysisFunctionForAddress(self.handle, addr) + if func is None: + return None + return function.Function(self, func) + + 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 + + def get_basic_blocks_starting_at(self, addr): + """ + ``get_basic_blocks_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 + + 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) + + def get_code_refs(self, addr, length=None): + 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 + + 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 bntype.Symbol(None, None, None, handle = sym) + + 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('?testf@Foobar@@SA?AW4foo@1@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 bntype.Symbol(None, None, None, handle = sym) + + 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('?testf@Foobar@@SA?AW4foo@1@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(bntype.Symbol(None, None, None, handle = core.BNNewSymbolReference(syms[i]))) + core.BNFreeSymbolList(syms, count.value) + return result + + 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: "KERNEL32!IsProcessorFeaturePresent@IAT" @ 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(bntype.Symbol(None, None, None, handle = core.BNNewSymbolReference(syms[i]))) + core.BNFreeSymbolList(syms, count.value) + return result + + 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(core.BNSymbolType.ImportAddressSymbol, 0x10002028, 1) + [<ImportAddressSymbol: "KERNEL32!GetCurrentThreadId@IAT" @ 0x10002028>] + >>> + """ + if isinstance(sym_type, str): + sym_type = core.BNSymbolType[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(bntype.Symbol(None, None, None, handle = core.BNNewSymbolReference(syms[i]))) + core.BNFreeSymbolList(syms, count.value) + return result + + def define_auto_symbol(self, sym): + """ + ``define_auto_symbol`` adds a symbol to the internal list of automatically discovered Symbol objects. + + :param Symbol sym: the symbol to define + :rtype: None + """ + core.BNDefineAutoSymbol(self.handle, sym.handle) + + 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) + + def define_user_symbol(self, sym): + """ + ``define_user_symbol`` adds a symbol to the internal list of user added Symbol objects. + + :param Symbol sym: the symbol to define + :rtype: None + """ + core.BNDefineUserSymbol(self.handle, sym.handle) + + 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) + + 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) + + def is_never_branch_patch_available(self, arch, addr): + """ + ``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 Architecture arch: the architecture for the current view + :param int addr: the virtual address of the instruction to be patched + :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(bv.arch, 0x100012ed) + False + >>> bv.get_disassembly(0x100012ef) + 'jg 0x100012f5' + >>> bv.is_never_branch_patch_available(bv.arch, 0x100012ef) + True + >>> + """ + return core.BNIsNeverBranchPatchAvailable(self.handle, arch.handle, addr) + + def is_always_branch_patch_available(self, arch, addr): + """ + ``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 Architecture arch: the architecture for the current view + :param int addr: the virtual address of the instruction to be patched + :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(bv.arch, 0x100012ed) + False + >>> bv.get_disassembly(0x100012ef) + 'jg 0x100012f5' + >>> bv.is_always_branch_patch_available(bv.arch, 0x100012ef) + True + >>> + """ + return core.BNIsAlwaysBranchPatchAvailable(self.handle, arch.handle, addr) + + def is_invert_branch_patch_available(self, arch, addr): + """ + ``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 Architecture arch: the architecture for the current view + :param int addr: the virtual address of the instruction to be patched + :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(bv.arch, 0x100012ed) + False + >>> bv.get_disassembly(0x100012ef) + 'jg 0x100012f5' + >>> bv.is_invert_branch_patch_available(bv.arch, 0x100012ef) + True + >>> + """ + return core.BNIsInvertBranchPatchAvailable(self.handle, arch.handle, addr) + + def is_skip_and_return_zero_patch_available(self, arch, addr): + """ + ``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 Architecture arch: the architecture for the current view + :param int addr: the virtual address of the instruction to be patched + :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(bv.arch, 0x100012f6) + False + >>> bv.get_disassembly(0x100012fb) + 'call 0x10001629' + >>> bv.is_skip_and_return_zero_patch_available(bv.arch, 0x100012fb) + True + >>> + """ + return core.BNIsSkipAndReturnZeroPatchAvailable(self.handle, arch.handle, addr) + + def is_skip_and_return_value_patch_available(self, arch, addr): + """ + ``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 Architecture arch: the architecture for the current view + :param int addr: the virtual address of the instruction to be patched + :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(bv.arch, 0x100012f6) + False + >>> bv.get_disassembly(0x100012fb) + 'call 0x10001629' + >>> bv.is_skip_and_return_value_patch_available(bv.arch, 0x100012fb) + True + >>> + """ + return core.BNIsSkipAndReturnValuePatchAvailable(self.handle, arch.handle, addr) + + def convert_to_nop(self, arch, addr): + """ + ``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 Architecture arch: architecture of the current BinaryView + :param int addr: virtual address of the instruction to conver to nops + :return: True on success, False on falure. + :rtype: bool + :Example: + + >>> bv.get_disassembly(0x100012fb) + 'call 0x10001629' + >>> bv.convert_to_nop(bv.arch, 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' + """ + return core.BNConvertToNop(self.handle, arch.handle, addr) + + def always_branch(self, arch, addr): + """ + ``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 Architecture arch: architecture of the current binary view + :param int addr: virtual address of the instruction to be modified + :return: True on success, False on falure. + :rtype: bool + :Example: + + >>> bv.get_disassembly(0x100012ef) + 'jg 0x100012f5' + >>> bv.always_branch(bv.arch, 0x100012ef) + True + >>> bv.get_disassembly(0x100012ef) + 'jmp 0x100012f5' + >>> + """ + return core.BNAlwaysBranch(self.handle, arch.handle, addr) + + def never_branch(self, arch, addr): + """ + ``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 Architecture arch: architecture of the current binary view + :param int addr: virtual address of the instruction to be modified + :return: True on success, False on falure. + :rtype: bool + :Example: + + >>> bv.get_disassembly(0x1000130e) + 'jne 0x10001317' + >>> bv.never_branch(bv.arch, 0x1000130e) + True + >>> bv.get_disassembly(0x1000130e) + 'nop' + >>> + """ + return core.BNConvertToNop(self.handle, arch.handle, addr) + + def invert_branch(self, arch, addr): + """ + ``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 Architecture arch: architecture of the current binary view + :param int addr: virtual address of the instruction to be modified + :return: True on success, False on falure. + :rtype: bool + :Example: + + >>> bv.get_disassembly(0x1000130e) + 'je 0x10001317' + >>> bv.invert_branch(bv.arch, 0x1000130e) + True + >>> + >>> bv.get_disassembly(0x1000130e) + 'jne 0x10001317' + >>> + """ + return core.BNInvertBranch(self.handle, arch.handle, addr) + + def skip_and_return_value(self, arch, addr, value): + """ + ``skip_and_return_value`` convert the ``call`` instruction of architecture ``arch`` at the virtual address + ``addr`` to the equivilent of returning a value. + + :param Architecture arch: architecture of the current binary view + :param int addr: virtual address of the instruction to be modified + :param int value: value to make the instruction *return* + :return: True on success, False on falure. + :rtype: bool + :Example: + + >>> bv.get_disassembly(0x1000132a) + 'call 0x1000134a' + >>> bv.skip_and_return_value(bv.arch, 0x1000132a, 42) + True + >>> #The return value from x86 functions is stored in eax thus: + >>> bv.get_disassembly(0x1000132a) + 'mov eax, 0x2a' + >>> + """ + return core.BNSkipAndReturnValue(self.handle, arch.handle, addr, value) + + def get_instruction_length(self, arch, addr): + """ + ``get_instruction_length`` returns the number of bytes in the instruction of Architecture ``arch`` at the virtual + address ``addr`` + + :param Architecture arch: architecture of the current binary view + :param int addr: virtual address of the instruction query + :return: Number of bytes in instruction + :rtype: int + :Example: + + >>> bv.get_disassembly(0x100012f1) + 'xor eax, eax' + >>> bv.get_instruction_length(bv.arch, 0x100012f1) + 2L + >>> + """ + return core.BNGetInstructionLength(self.handle, arch.handle, addr) + + def notify_data_written(self, offset, length): + core.BNNotifyDataWritten(self.handle, offset, length) + + def notify_data_inserted(self, offset, length): + core.BNNotifyDataInserted(self.handle, offset, length) + + def notify_data_removed(self, offset, length): + core.BNNotifyDataRemoved(self.handle, offset, length) + + 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(core.BNStringType(strings[i].type), strings[i].start, strings[i].length)) + core.BNFreeStringReferenceList(strings) + return result + + 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) + + 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) + + 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) + + 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) + + 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) + + 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) + + 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) + + 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) + + 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) + + 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) + + 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 = core.BNInstructionTextTokenType(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 + tokens.append(function.InstructionTextToken(token_type, text, value, size, operand)) + 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 + + 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) + + 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) + + 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)) + + 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 string type name + :rtype: tuple(Type, str) + :Example: + + >>> bv.parse_type_string("int foo") + (<type: int32_t>, 'foo') + >>> + """ + result = core.BNNameAndType() + 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 = bntype.Type(core.BNNewTypeReference(result.type)) + name = result.name + core.BNFreeNameAndType(result) + return type_obj, name + + def get_type_by_name(self, name): + """ + ``get_type_by_name`` returns the defined type whose name corresponds with the provided ``name`` + + :param str 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_type(name, type) + >>> bv.get_type_by_name(name) + <type: int32_t> + >>> + """ + obj = core.BNGetAnalysisTypeByName(self.handle, name) + if not obj: + return None + return bntype.Type(obj) + + 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 str 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 + >>> + """ + return core.BNIsAnalysisTypeAutoDefined(self.handle, name) + + def define_type(self, 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`. + + :param str name: Name of the 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_type(name, type) + >>> bv.get_type_by_name(name) + <type: int32_t> + """ + core.BNDefineAnalysisType(self.handle, name, type_obj.handle) + + 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 str 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> + """ + core.BNDefineUserAnalysisType(self.handle, name, type_obj.handle) + + def undefine_type(self, name): + """ + ``undefine_type`` removes a :py:Class:`Type` from the global list of types for the current :py:Class:`BinaryView` + + :param str name: Name of type to be undefined + :rtype: None + :Example: + + >>> type, name = bv.parse_type_string("int foo") + >>> bv.define_type(name, type) + >>> bv.get_type_by_name(name) + <type: int32_t> + >>> bv.undefine_type(name) + >>> bv.get_type_by_name(name) + >>> + """ + core.BNUndefineAnalysisType(self.handle, name) + + 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 str name: Name of user type to be undefined + :rtype: None + :Example: + + >>> type, name = bv.parse_type_string("int foo") + >>> bv.define_type(name, type) + >>> bv.get_type_by_name(name) + <type: int32_t> + >>> bv.undefine_type(name) + >>> bv.get_type_by_name(name) + >>> + """ + core.BNUndefineUserAnalysisType(self.handle, name) + + 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 + + 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) + + def show_plain_text_report(self, title, contents): + core.BNShowPlainTextReport(self.handle, title, contents) + + def show_markdown_report(self, title, contents, plaintext = ""): + core.BNShowMarkdownReport(self.handle, title, contents, plaintext) + + def show_html_report(self, title, contents, plaintext = ""): + core.BNShowHTMLReport(self.handle, title, contents, plaintext) + + 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 + + def add_auto_segment(self, start, length, data_offset, data_length, flags): + core.BNAddAutoSegment(self.handle, start, length, data_offset, data_length, flags) + + def remove_auto_segment(self, start, length): + core.BNRemoveAutoSegment(self.handle, start, length) + + def add_user_segment(self, start, length, data_offset, data_length, flags): + core.BNAddUserSegment(self.handle, start, length, data_offset, data_length, flags) + + def remove_user_segment(self, start, length): + core.BNRemoveUserSegment(self.handle, start, length) + + 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 + + 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) + + def remove_auto_section(self, name): + core.BNRemoveAutoSection(self.handle, name) + + 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) + + def remove_user_section(self, name): + core.BNRemoveUserSection(self.handle, name) + + 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 + + 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 + + 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) + + +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, core.BNEndianness.BigEndian) + >>> hex(br.read32()) + '0xcffaedfeL' + >>> + """ + 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) + + @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) + + 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 + + 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 + + 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 + + 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 + + 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 + + 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] + + 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] + + 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] + + 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] + + 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] + + 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] + + 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) + + 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) + + +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, core.BNEndianness.BigEndian) + >>> bw = BinaryWriter(bv, core.BNEndianness.BigEndian) + >>> + """ + 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) + + @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) + + 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)) + + 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) + + def write16(self, value): + """ + ```` 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) + + def write32(self, value): + """ + ```` 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) + + def write64(self, value): + """ + ```` 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) + + 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) + + 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) + + 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) + + 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) + + 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) + + 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) + + 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(br.offset) + '0x100000000L' + >>> + """ + core.BNSeekBinaryWriter(self.handle, offset) + + 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(br.offset) + '0x100000000L' + >>> + """ + core.BNSeekBinaryWriterRelative(self.handle, offset) |
