diff options
| author | Rusty Wagner <rusty@vector35.com> | 2017-01-16 17:18:54 -0500 |
|---|---|---|
| committer | Rusty Wagner <rusty@vector35.com> | 2017-01-16 17:36:43 -0500 |
| commit | 41acb3d72d6ec23620a744b7f84c8359e425b30b (patch) | |
| tree | df13cbfbd36f1e6f5e306080c0737e5c11be0f60 /python | |
| parent | 7e154a952fe5856b7cf650e2646ebc410b1fb506 (diff) | |
| parent | 8139c1413e6453f188b2ea7fed01cb3ea30d949e (diff) | |
Merge branch 'dev' into type_view
Diffstat (limited to 'python')
41 files changed, 14286 insertions, 11836 deletions
diff --git a/python/__init__.py b/python/__init__.py index c2db468d..a1ea02f5 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -18,10917 +18,40 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. -import _binaryninjacore as core -import abc -import ctypes -import traceback -import json -import struct -import threading -import code -import sys -import copy - -_plugin_init = False -def _init_plugins(): - global _plugin_init - if not _plugin_init: - _plugin_init = True - core.BNInitCorePlugins() - core.BNInitUserPlugins() - if not core.BNIsLicenseValidated(): - raise RuntimeError, "License is not valid. Please supply a valid license." - -class DataBuffer(object): - def __init__(self, contents="", handle=None): - if handle is not None: - self.handle = core.handle_of_type(handle, core.BNDataBuffer) - elif isinstance(contents, int) or isinstance(contents, long): - self.handle = core.BNCreateDataBuffer(None, contents) - elif isinstance(contents, DataBuffer): - self.handle = core.BNDuplicateDataBuffer(contents.handle) - else: - self.handle = core.BNCreateDataBuffer(contents, len(contents)) - - def __del__(self): - core.BNFreeDataBuffer(self.handle) - - def __len__(self): - return int(core.BNGetDataBufferLength(self.handle)) - - def __getitem__(self, i): - if isinstance(i, tuple): - result = "" - source = str(self) - for s in i: - result += source[s] - return result - elif isinstance(i, slice): - if i.step is not None: - i = i.indices(len(self)) - start = i[0] - stop = i[1] - if stop <= start: - return "" - buf = ctypes.create_string_buffer(stop - start) - ctypes.memmove(buf, core.BNGetDataBufferContentsAt(self.handle, start), stop - start) - return buf.raw - else: - return str(self)[i] - elif i < 0: - if i >= -len(self): - return chr(core.BNGetDataBufferByte(self.handle, int(len(self) + i))) - raise IndexError, "index out of range" - elif i < len(self): - return chr(core.BNGetDataBufferByte(self.handle, int(i))) - 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(len(self)) - start = i[0] - stop = i[1] - if stop < start: - stop = start - if len(value) != (stop - start): - data = str(self) - data = data[0:start] + value + data[stop:] - core.BNSetDataBufferContents(self.handle, data, len(data)) - else: - value = str(value) - buf = ctypes.create_string_buffer(value) - ctypes.memmove(core.BNGetDataBufferContentsAt(self.handle, start), buf, len(value)) - elif i < 0: - if i >= -len(self): - if len(value) != 1: - raise ValueError, "expected single byte for assignment" - value = str(value) - buf = ctypes.create_string_buffer(value) - ctypes.memmove(core.BNGetDataBufferContentsAt(self.handle, int(len(self) + i)), buf, 1) - else: - raise IndexError, "index out of range" - elif i < len(self): - if len(value) != 1: - raise ValueError, "expected single byte for assignment" - value = str(value) - buf = ctypes.create_string_buffer(value) - ctypes.memmove(core.BNGetDataBufferContentsAt(self.handle, int(i)), buf, 1) - else: - raise IndexError, "index out of range" - - def __str__(self): - buf = ctypes.create_string_buffer(len(self)) - ctypes.memmove(buf, core.BNGetDataBufferContents(self.handle), len(self)) - return buf.raw - - def __repr__(self): - return repr(str(self)) - - def escape(self): - return core.BNDataBufferToEscapedString(self.handle) - - def unescape(self): - return DataBuffer(handle=core.BNDecodeEscapedString(str(self))) - - def base64_encode(self): - return core.BNDataBufferToBase64(self.handle) - - def base64_decode(self): - return DataBuffer(handle = core.BNDecodeBase64(str(self))) - - def zlib_compress(self): - buf = core.BNZlibCompress(self.handle) - if buf is None: - return None - return DataBuffer(handle = buf) - - def zlib_decompress(self): - buf = core.BNZlibDecompress(self.handle) - if buf is None: - return None - return DataBuffer(handle = buf) - -class NavigationHandler(object): - def _register(self, handle): - self._cb = core.BNNavigationHandler() - self._cb.context = 0 - self._cb.getCurrentView = self._cb.getCurrentView.__class__(self._get_current_view) - self._cb.getCurrentOffset = self._cb.getCurrentOffset.__class__(self._get_current_offset) - self._cb.navigate = self._cb.navigate.__class__(self._navigate) - core.BNSetFileMetadataNavigationHandler(handle, self._cb) - - def _get_current_view(self, ctxt): - try: - view = self.get_current_view() - except: - log_error(traceback.format_exc()) - view = "" - return core.BNAllocString(view) - - def _get_current_offset(self, ctxt): - try: - return self.get_current_offset() - except: - log_error(traceback.format_exc()) - return 0 - - def _navigate(self, ctxt, view, offset): - try: - return self.navigate(view, offset) - except: - log_error(traceback.format_exc()) - return False - -class _AssociatedDataStore(dict): - _defaults = {} - - @classmethod - def set_default(cls, name, value): - cls._defaults[name] = value - - def __getattr__(self, name): - if name in self.__dict__: - return self.__dict__[name] - if name not in self: - if name in self.__class__._defaults: - result = copy.copy(self.__class__._defaults[name]) - self[name] = result - return result - return self.__getitem__(name) - - def __setattr__(self, name, value): - self.__setitem__(name, value) - - def __delattr__(self, name): - self.__delitem__(name) - -class _FileMetadataAssociatedDataStore(_AssociatedDataStore): - _defaults = {} - -class FileMetadata(object): - _associated_data = {} - - """ - ``class FileMetadata`` represents the file being analyzed by Binary Ninja. It is responsible for opening, - closing, creating the database (.bndb) files, and is used to keep track of undoable actions. - """ - def __init__(self, filename = None, handle = None): - """ - Instantiates a new FileMetadata class. - - :param filename: The string path to the file to be opened. Defaults to None. - :param handle: A handle to the underlying C FileMetadata object. Defaults to None. - """ - if handle is not None: - self.handle = core.handle_of_type(handle, core.BNFileMetadata) - else: - _init_plugins() - self.handle = core.BNCreateFileMetadata() - if filename is not None: - core.BNSetFilename(self.handle, str(filename)) - self.nav = None - - def __del__(self): - if self.navigation is not None: - core.BNSetFileMetadataNavigationHandler(self.handle, None) - core.BNFreeFileMetadata(self.handle) - - @classmethod - def _unregister(cls, f): - handle = ctypes.cast(f, 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): - _FileMetadataAssociatedDataStore.set_default(name, value) - - @property - def filename(self): - """The name of the file (read/write)""" - return core.BNGetFilename(self.handle) - - @filename.setter - def filename(self, value): - core.BNSetFilename(self.handle, str(value)) - - @property - def modified(self): - """Boolean result of whether the file is modified (Inverse of 'saved' property) (read/write)""" - return core.BNIsFileModified(self.handle) - - @modified.setter - def modified(self, value): - if value: - core.BNMarkFileModified(self.handle) - else: - core.BNMarkFileSaved(self.handle) - - @property - def analysis_changed(self): - """Boolean result of whether the auto-analysis results have changed (read-only)""" - return core.BNIsAnalysisChanged(self.handle) - - @property - def has_database(self): - """Whether the FileMetadata is backed by a database (read-only)""" - return core.BNIsBackedByDatabase(self.handle) - - @property - def view(self): - return core.BNGetCurrentView(self.handle) - - @view.setter - def view(self, value): - core.BNNavigate(self.handle, str(value), core.BNGetCurrentOffset(self.handle)) - - @property - def offset(self): - """The current offset into the file (read/write)""" - return core.BNGetCurrentOffset(self.handle) - - @offset.setter - def offset(self, value): - core.BNNavigate(self.handle, core.BNGetCurrentView(self.handle), value) - - @property - def raw(self): - """Gets the "Raw" BinaryView of the file""" - view = core.BNGetFileViewOfType(self.handle, "Raw") - if view is None: - return None - return BinaryView(file_metadata = self, handle = view) - - @property - def saved(self): - """Boolean result of whether the file has been saved (Inverse of 'modified' property) (read/write)""" - return not core.BNIsFileModified(self.handle) - - @saved.setter - def saved(self, value): - if value: - core.BNMarkFileSaved(self.handle) - else: - core.BNMarkFileModified(self.handle) - - @property - def navigation(self): - return self.nav - - @navigation.setter - def navigation(self, value): - value._register(self.handle) - self.nav = value - - @property - def session_data(self): - """Dictionary object where plugins can store arbitrary data associated with the file""" - handle = ctypes.cast(self.handle, ctypes.c_void_p) - if handle.value not in FileMetadata._associated_data: - obj = _FileMetadataAssociatedDataStore() - FileMetadata._associated_data[handle.value] = obj - return obj - else: - return FileMetadata._associated_data[handle.value] - - def close(self): - """ - Closes the underlying file handle. It is recommended that this is done in a - `finally` clause to avoid handle leaks. - """ - core.BNCloseFile(self.handle) - - 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' - >>> - """ - core.BNBeginUndoActions(self.handle) - - 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' - >>> - """ - core.BNCommitUndoActions(self.handle) - - 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' - >>> - """ - core.BNUndo(self.handle) - - 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' - >>> - """ - core.BNRedo(self.handle) - - def navigate(self, view, offset): - return core.BNNavigate(self.handle, str(view), offset) - - def create_database(self, filename, progress_func = None): - if progress_func is None: - return core.BNCreateDatabase(self.raw.handle, str(filename)) - else: - return core.BNCreateDatabaseWithProgress(self.raw.handle, str(filename), None, - ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.c_ulonglong, ctypes.c_ulonglong)( - lambda ctxt, cur, total: progress_func(cur, total))) - - def open_existing_database(self, filename, progress_func = None): - if progress_func is None: - view = core.BNOpenExistingDatabase(self.handle, str(filename)) - else: - view = core.BNOpenExistingDatabaseWithProgress(self.handle, str(filename), None, - ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.c_ulonglong, ctypes.c_ulonglong)( - lambda ctxt, cur, total: progress_func(cur, total))) - if view is None: - return None - return BinaryView(file_metadata = self, handle = view) - - def save_auto_snapshot(self, progress_func = None): - if progress_func is None: - return core.BNSaveAutoSnapshot(self.raw.handle) - else: - return core.BNSaveAutoSnapshotWithProgress(self.raw.handle, None, - ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.c_ulonglong, ctypes.c_ulonglong)( - lambda ctxt, cur, total: progress_func(cur, total))) - - def get_view_of_type(self, name): - view = core.BNGetFileViewOfType(self.handle, str(name)) - if view is None: - view_type = core.BNGetBinaryViewTypeByName(str(name)) - if view_type is None: - return None - view = core.BNCreateBinaryViewOfType(view_type, self.raw.handle) - if view is None: - return None - return BinaryView(file_metadata = self, handle = view) - - def __setattr__(self, name, value): - try: - object.__setattr__(self,name,value) - except AttributeError: - raise AttributeError, "attribute '%s' is read only" % name - -class FileAccessor: - def __init__(self): - self._cb = core.BNFileAccessor() - self._cb.context = 0 - self._cb.getLength = self._cb.getLength.__class__(self._get_length) - self._cb.read = self._cb.read.__class__(self._read) - self._cb.write = self._cb.write.__class__(self._write) - - def __len__(self): - return self.get_length() - - def _get_length(self, ctxt): - try: - return self.get_length() - except: - log_error(traceback.format_exc()) - return 0 - - def _read(self, ctxt, dest, offset, length): - try: - data = self.read(offset, length) - if data is None: - return 0 - if len(data) > length: - data = data[0:length] - ctypes.memmove(dest, data, len(data)) - return len(data) - except: - 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.write(offset, data.raw) - except: - log_error(traceback.format_exc()) - return 0 - -class CoreFileAccessor(FileAccessor): - def __init__(self, accessor): - self._cb.context = accessor.context - self._cb.getLength = accessor.getLength - self._cb.read = accessor.read - self._cb.write = accessor.write - - def get_length(self): - return self._cb.getLength(self._cb.context) - - def read(self, offset, length): - data = ctypes.create_string_buffer(length) - length = self._cb.read(self._cb.context, data, offset, length) - return data.raw[0:length] - - def write(self, offset, value): - value = str(value) - data = ctypes.create_string_buffer(value) - return self._cb.write(self._cb.context, offset, data, len(value)) - -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 - - def type_defined(self, view, name, type): - pass - - def type_undefined(self, view, name, type): - pass - -class UndoAction: - name = None - action_type = None - _registered = False - _registered_cb = None - - def __init__(self, view): - self._cb = core.BNUndoAction() - if not self.__class__._registered: - raise TypeError, "undo action type not registered" - action_type = self.__class__.action_type - if isinstance(action_type, str): - self._cb.type = core.BNActionType_by_name[action_type] - else: - self._cb.type = action_type - self._cb.context = 0 - self._cb.undo = self._cb.undo.__class__(self._undo) - self._cb.redo = self._cb.redo.__class__(self._redo) - self._cb.serialize = self._cb.serialize.__class__(self._serialize) - self.view = view - - @classmethod - def register(cls): - _init_plugins() - if cls.name is None: - raise ValueError, "undo action 'name' not defined" - if cls.action_type is None: - raise ValueError, "undo action 'action_type' not defined" - cb_type = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.c_char_p, ctypes.POINTER(core.BNUndoAction)) - cls._registered_cb = cb_type(cls._deserialize) - core.BNRegisterUndoActionType(cls.name, 0, cls._registered_cb) - cls._registered = True - - @classmethod - def _deserialize(cls, ctxt, data, result): - try: - action = cls.deserialize(json.loads(data)) - if action is None: - return False - result.context = action._cb.context - result.undo = action._cb.undo - result.redo = action._cb.redo - result.serialize = action._cb.serialize - return True - except: - log_error(traceback.format_exc()) - return False - - def _undo(self, ctxt, view): - try: - self.undo() - except: - log_error(traceback.format_exc()) - return False - - def _redo(self, ctxt, view): - try: - self.redo() - except: - log_error(traceback.format_exc()) - return False - - def _serialize(self, ctxt): - try: - return json.dumps(self.serialize()) - except: - log_error(traceback.format_exc()) - return "null" - -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 QualifiedName(object): - def __init__(self, name = []): - if isinstance(name, str): - self.name = [name] - elif isinstance(name, QualifiedName): - self.name = name.name - else: - self.name = name - - def __str__(self): - return "::".join(self.name) - - def __repr__(self): - return repr(str(self)) - - def __len__(self): - return len(self.name) - - def __hash__(self): - return hash(str(self)) - - def __eq__(self, other): - if isinstance(other, str): - return str(self) == other - elif isinstance(other, list): - return self.name == other - elif isinstance(other, QualifiedName): - return self.name == other.name - return False - - def __ne__(self, other): - return not (self == other) - - def __lt__(self, other): - if isinstance(other, QualifiedName): - return self.name < other.name - return False - - def __le__(self, other): - if isinstance(other, QualifiedName): - return self.name <= other.name - return False - - def __gt__(self, other): - if isinstance(other, QualifiedName): - return self.name > other.name - return False - - def __ge__(self, other): - if isinstance(other, QualifiedName): - return self.name >= other.name - return False - - def __cmp__(self, other): - if self == other: - return 0 - if self < other: - return -1 - return 1 - - def __getitem__(self, key): - return self.name[key] - - def __iter__(self): - return iter(self.name) - - def _get_core_struct(self): - result = core.BNQualifiedName() - name_list = (ctypes.c_char_p * len(self.name))() - for i in xrange(0, len(self.name)): - name_list[i] = self.name[i] - result.name = name_list - result.nameCount = len(self.name) - return result - - @classmethod - def _from_core_struct(cls, name): - result = [] - for i in xrange(0, name.nameCount): - result.append(name.name[i]) - return QualifiedName(result) - -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) - self._cb.typeDefined = self._cb.typeDefined.__class__(self._type_defined) - self._cb.typeUndefined = self._cb.typeUndefined.__class__(self._type_undefined) - - def _register(self): - core.BNRegisterDataNotification(self.view.handle, self._cb) - - def _unregister(self): - core.BNUnregisterDataNotification(self.view.handle, self._cb) - - def _data_written(self, ctxt, view, offset, length): - try: - self.notify.data_written(self.view, offset, length) - except OSError: - log_error(traceback.format_exc()) - - def _data_inserted(self, ctxt, view, offset, length): - try: - self.notify.data_inserted(self.view, offset, length) - except: - log_error(traceback.format_exc()) - - def _data_removed(self, ctxt, view, offset, length): - try: - self.notify.data_removed(self.view, offset, length) - except: - log_error(traceback.format_exc()) - - def _function_added(self, ctxt, view, func): - try: - self.notify.function_added(self.view, Function(self.view, core.BNNewFunctionReference(func))) - except: - log_error(traceback.format_exc()) - - def _function_removed(self, ctxt, view, func): - try: - self.notify.function_removed(self.view, Function(self.view, core.BNNewFunctionReference(func))) - except: - log_error(traceback.format_exc()) - - def _function_updated(self, ctxt, view, func): - try: - self.notify.function_updated(self.view, Function(self.view, core.BNNewFunctionReference(func))) - except: - log_error(traceback.format_exc()) - - def _data_var_added(self, ctxt, view, var): - try: - address = var.address - var_type = Type(core.BNNewTypeReference(var.type)) - auto_discovered = var.autoDiscovered - self.notify.data_var_added(self.view, DataVariable(address, var_type, auto_discovered)) - except: - log_error(traceback.format_exc()) - - def _data_var_removed(self, ctxt, view, var): - try: - address = var.address - var_type = Type(core.BNNewTypeReference(var.type)) - auto_discovered = var.autoDiscovered - self.notify.data_var_removed(self.view, DataVariable(address, var_type, auto_discovered)) - except: - log_error(traceback.format_exc()) - - def _data_var_updated(self, ctxt, view, var): - try: - address = var.address - var_type = Type(core.BNNewTypeReference(var.type)) - auto_discovered = var.autoDiscovered - self.notify.data_var_updated(self.view, DataVariable(address, var_type, auto_discovered)) - except: - log_error(traceback.format_exc()) - - def _string_found(self, ctxt, view, string_type, offset, length): - try: - self.notify.string_found(self.view, core.BNStringType_names[string_type], offset, length) - except: - log_error(traceback.format_exc()) - - def _string_removed(self, ctxt, view, string_type, offset, length): - try: - self.notify.string_removed(self.view, core.BNStringType_names[string_type], offset, length) - except: - log_error(traceback.format_exc()) - - def _type_defined(self, ctxt, name, type_obj): - try: - qualified_name = QualifiedName._from_core_struct(name[0]) - self.notify.type_defined(self.view, qualified_name, Type(core.BNNewTypeReference(type_obj))) - except: - log_error(traceback.format_exc()) - - def _type_undefined(self, ctxt, name, type_obj): - try: - qualified_name = QualifiedName._from_core_struct(name[0]) - self.notify.type_undefined(self.view, qualified_name, Type(core.BNNewTypeReference(type_obj))) - except: - log_error(traceback.format_exc()) - -class _BinaryViewTypeMetaclass(type): - @property - def list(self): - """List all BinaryView types (read-only)""" - _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): - _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): - _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(arch) - - def register_platform(self, ident, arch, platform): - core.BNRegisterPlatformForViewType(self.handle, ident, arch.handle, platform.handle) - - def register_default_platform(self, arch, platform): - core.BNRegisterDefaultPlatformForViewType(self.handle, arch.handle, platform.handle) - - def get_platform(self, ident, arch): - platform = core.BNGetPlatformForViewType(self.handle, ident, arch.handle) - if platform is None: - return None - return Platform(None, platform) - - def __setattr__(self, name, value): - try: - object.__setattr__(self, name, value) - except AttributeError: - raise AttributeError, "attribute '%s' is read only" % name - -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_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.DisassembleState: - return "Disassembling (%d/%d)" % (self.count, self.total) - if self.state == core.AnalyzeState: - return "Analyzing (%d/%d)" % (self.count, self.total) - return "Idle" - - def __repr__(self): - return "<progress: %s>" % str(self) - -class LinearDisassemblyPosition(object): - """ - ``class LinearDisassemblyPosition`` is a helper object containing the position of the current Linear Disassembly. - - .. note:: This object should not be instantiated directly. Rather call \ - :py:method:`get_linear_disassembly_position_at` which instantiates this object. - """ - def __init__(self, func, block, addr): - self.function = func - self.block = block - self.address = addr - -class LinearDisassemblyLine(object): - def __init__(self, line_type, func, block, line_offset, contents): - self.type = line_type - self.function = func - self.block = block - self.line_offset = line_offset - self.contents = contents - - def __str__(self): - return str(self.contents) - - def __repr__(self): - return repr(self.contents) - -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 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.SegmentReadable) != 0 else "-", - "w" if (self.flags & core.SegmentWritable) != 0 else "-", - "x" if (self.flags & core.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): - _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(handle = core.BNGetFileForView(handle)) - else: - self.file = file_metadata - elif self.__class__ is BinaryView: - _init_plugins() - if file_metadata is None: - file_metadata = FileMetadata() - self.handle = core.BNCreateBinaryDataView(file_metadata.handle) - self.file = FileMetadata(handle = core.BNNewFileReference(file_metadata)) - else: - _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): - _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(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_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_error(traceback.format_exc()) - return False - - @classmethod - def open(cls, src, file_metadata = None): - _init_plugins() - if isinstance(src, FileAccessor): - if file_metadata is None: - file_metadata = FileMetadata() - view = core.BNCreateBinaryDataViewFromFile(file_metadata.handle, src._cb) - else: - if file_metadata is None: - file_metadata = 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): - _init_plugins() - if file_metadata is None: - file_metadata = FileMetadata() - if data is None: - view = core.BNCreateBinaryDataView(file_metadata.handle) - else: - buf = 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(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(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)""" - platform = core.BNGetDefaultPlatform(self.handle) - if platform is None: - return None - return Platform(self.arch, handle = platform) - - @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(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(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 = 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 = Type(core.BNNewTypeReference(var_list[i].type)) - auto_discovered = var_list[i].autoDiscovered - result[addr] = DataVariable(addr, var_type, auto_discovered) - core.BNFreeDataVariables(var_list, count.value) - return result - - @property - def types(self): - """List of defined types (read-only)""" - count = ctypes.c_ulonglong(0) - type_list = core.BNGetAnalysisTypeList(self.handle, count) - result = {} - for i in xrange(0, count.value): - name = QualifiedName._from_core_struct(type_list[i].name) - result[name] = 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_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_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_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_error(traceback.format_exc()) - return 0 - - def _remove(self, ctxt, offset, length): - try: - return self.perform_remove(offset, length) - except: - log_error(traceback.format_exc()) - return 0 - - def _get_modification(self, ctxt, offset): - try: - return self.perform_get_modification(offset) - except: - log_error(traceback.format_exc()) - return core.Original - - def _is_valid_offset(self, ctxt, offset): - try: - return self.perform_is_valid_offset(offset) - except: - log_error(traceback.format_exc()) - return False - - def _is_offset_readable(self, ctxt, offset): - try: - return self.perform_is_offset_readable(offset) - except: - log_error(traceback.format_exc()) - return False - - def _is_offset_writable(self, ctxt, offset): - try: - return self.perform_is_offset_writable(offset) - except: - log_error(traceback.format_exc()) - return False - - def _is_offset_executable(self, ctxt, offset): - try: - return self.perform_is_offset_executable(offset) - except: - 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_error(traceback.format_exc()) - return offset - - def _get_start(self, ctxt): - try: - return self.perform_get_start() - except: - log_error(traceback.format_exc()) - return 0 - - def _get_length(self, ctxt): - try: - return self.perform_get_length() - except: - log_error(traceback.format_exc()) - return 0 - - def _get_entry_point(self, ctxt): - try: - return self.perform_get_entry_point() - except: - log_error(traceback.format_exc()) - return 0 - - def _is_executable(self, ctxt): - try: - return self.perform_is_executable() - except: - log_error(traceback.format_exc()) - return False - - def _get_default_endianness(self, ctxt): - try: - return self.perform_get_default_endianness() - except: - log_error(traceback.format_exc()) - return core.LittleEndian - - def _get_address_size(self, ctxt): - try: - return self.perform_get_address_size() - except: - log_error(traceback.format_exc()) - return 8 - - def _save(self, ctxt, file_accessor): - try: - return self.perform_save(CoreFileAccessor(file_accessor)) - except: - 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.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.LittleEndian`` or ``core.BigEndian`` - :rtype: BNEndianness - """ - return core.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(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(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(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): - 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, platform, addr): - """ - ``add_function`` add a new function of the given ``platform`` at the virtual address ``addr`` - - :param Platform platform: 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.platform, 1) - >>> bv.functions - [<func: x86_64@0x1>] - - """ - core.BNAddFunctionForAnalysis(self.handle, platform.handle, addr) - - def add_entry_point(self, platform, addr): - """ - ``add_entry_point`` adds an virtual address to start analysis from for a given platform. - - :param Platform platform: Platform for the entry point analysis - :param int addr: virtual address to start analysis from - :rtype: None - :Example: - >>> bv.add_entry_point(bv.platform, 0xdeadbeef) - >>> - """ - core.BNAddEntryPointForAnalysis(self.handle, platform.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, platform, addr): - """ - ``create_user_function`` add a new *user* function of the given ``platform`` at the virtual address ``addr`` - - :param Platform platform: 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.platform, 1) - >>> bv.functions - [<func: x86_64@0x1>] - - """ - core.BNCreateUserFunction(self.handle, platform.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() - 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(var.type), var.autoDiscovered) - - def get_function_at(self, platform, addr): - """ - ``get_function_at`` gets a binaryninja.Function object for the function at the virtual address ``addr``: - - :param binaryninja.Platform platform: platform 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.platform, bv.entry_point) - <func: x86_64@0x100001174> - >>> - """ - func = core.BNGetAnalysisFunction(self.handle, platform.handle, addr) - if func is None: - return None - return Function(self, func) - - def get_functions_at(self, addr): - """ - ``get_functions_at`` get a list of binaryninja.Function objects (one for each valid platform) 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(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(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(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(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(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(self, core.BNNewFunctionReference(refs[i].func)) - else: - func = None - if refs[i].arch: - arch = Architecture(refs[i].arch) - else: - arch = None - addr = refs[i].addr - result.append(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 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 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(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(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.ImportAddressSymbol, 0x10002028, 1) - [<ImportAddressSymbol: "KERNEL32!GetCurrentThreadId@IAT" @ 0x10002028>] - >>> - """ - if isinstance(sym_type, str): - sym_type = core.BNSymbolType_by_name[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(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 define_auto_symbol_and_var_or_function(self, sym, sym_type, platform = None): - """ - ``define_auto_symbol`` adds a symbol to the internal list of automatically discovered Symbol objects. - - :param Symbol sym: the symbol to define - :rtype: None - """ - if platform is None: - platform = self.platform - if platform is not None: - platform = platform.handle - if sym_type is not None: - sym_type = sym_type.handle - core.BNDefineAutoSymbolAndVariableOrFunction(self.handle, platform, sym.handle, sym_type) - - 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_names[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(self, pos.function) - if pos.block: - block = BasicBlock(self, pos.block) - return 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(self, core.BNNewFunctionReference(lines[i].function)) - if lines[i].block: - block = 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_names[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(InstructionTextToken(token_type, text, value, size, operand)) - contents = DisassemblyTextLine(addr, tokens) - result.append(LinearDisassemblyLine(lines[i].type, func, block, lines[i].lineOffset, contents)) - - func = None - block = None - if pos_obj.function: - func = Function(self, pos_obj.function) - if pos_obj.block: - block = 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 type name - :rtype: tuple(Type, QualifiedName) - :Example: - - >>> bv.parse_type_string("int foo") - (<type: int32_t>, 'foo') - >>> - """ - result = core.BNQualifiedNameAndType() - errors = ctypes.c_char_p() - if not core.BNParseTypeString(self.handle, text, result, errors): - error_str = errors.value - core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) - raise SyntaxError, error_str - type_obj = Type(core.BNNewTypeReference(result.type)) - name = QualifiedName._from_core_struct(result.name) - core.BNFreeQualifiedNameAndType(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 QualifiedName name: Type name to lookup - :return: A :py:Class:`Type` or None if the type does not exist - :rtype: Type or None - :Example: - - >>> type, name = bv.parse_type_string("int foo") - >>> bv.define_type(name, type) - >>> bv.get_type_by_name(name) - <type: int32_t> - >>> - """ - name = QualifiedName(name)._get_core_struct() - obj = core.BNGetAnalysisTypeByName(self.handle, name) - if not obj: - return None - return 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 QualifiedName name: Name of type to query - :return: True if the type is not a *user* type. False if the type is a *user* type. - :Example: - >>> bv.is_type_auto_defined("foo") - True - >>> bv.define_user_type("foo", bv.parse_type_string("struct {int x,y;}")[0]) - >>> bv.is_type_auto_defined("foo") - False - >>> - """ - name = QualifiedName(name)._get_core_struct() - 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 QualifiedName 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> - """ - name = QualifiedName(name)._get_core_struct() - 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 QualifiedName name: Name of the user type to be registered - :param Type type_obj: Type object to be registered - :rtype: None - :Example: - - >>> type, name = bv.parse_type_string("int foo") - >>> bv.define_user_type(name, type) - >>> bv.get_type_by_name(name) - <type: int32_t> - """ - name = QualifiedName(name)._get_core_struct() - 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 QualifiedName 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) - >>> - """ - name = QualifiedName(name)._get_core_struct() - 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 QualifiedName 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) - >>> - """ - name = QualifiedName(name)._get_core_struct() - 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(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.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.BigEndian) - >>> bw = BinaryWriter(bv, core.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) - -class Symbol(object): - """ - Symbols are defined as one of the following types: - - =========================== ============================================================== - SymbolType Description - =========================== ============================================================== - FunctionSymbol Symbol for Function that exists in the current binary - ImportAddressSymbol Symbol defined in the Import Address Table - ImportedFunctionSymbol Symbol for Function that is not defined in the current binary - DataSymbol Symbol for Data in the current binary - ImportedDataSymbol Symbol for Data that is not defined in the current binary - =========================== ============================================================== - """ - def __init__(self, sym_type, addr, short_name, full_name = None, raw_name = None, handle = None): - if handle is not None: - self.handle = core.handle_of_type(handle, core.BNSymbol) - else: - if isinstance(sym_type, str): - sym_type = core.BNSymbolType_by_name[sym_type] - if full_name is None: - full_name = short_name - if raw_name is None: - raw_name = full_name - self.handle = core.BNCreateSymbol(sym_type, short_name, full_name, raw_name, addr) - - def __del__(self): - core.BNFreeSymbol(self.handle) - - @property - def type(self): - """Symbol type (read-only)""" - return core.BNSymbolType_names[core.BNGetSymbolType(self.handle)] - - @property - def name(self): - """Symbol name (read-only)""" - return core.BNGetSymbolRawName(self.handle) - - @property - def short_name(self): - """Symbol short name (read-only)""" - return core.BNGetSymbolShortName(self.handle) - - @property - def full_name(self): - """Symbol full name (read-only)""" - return core.BNGetSymbolFullName(self.handle) - - @property - def raw_name(self): - """Symbol raw name (read-only)""" - return core.BNGetSymbolRawName(self.handle) - - @property - def address(self): - """Symbol address (read-only)""" - return core.BNGetSymbolAddress(self.handle) - - @property - def auto(self): - return core.BNIsSymbolAutoDefined(self.handle) - - @auto.setter - def auto(self, value): - core.BNSetSymbolAutoDefined(self.handle, value) - - def __repr__(self): - return "<%s: \"%s\" @ %#x>" % (self.type, self.full_name, self.address) - - def __setattr__(self, name, value): - try: - object.__setattr__(self,name,value) - except AttributeError: - raise AttributeError, "attribute '%s' is read only" % name - -class Type(object): - def __init__(self, handle): - self.handle = handle - - def __del__(self): - core.BNFreeType(self.handle) - - @property - def type_class(self): - """Type class (read-only)""" - return core.BNTypeClass_names[core.BNGetTypeClass(self.handle)] - - @property - def width(self): - """Type width (read-only)""" - return core.BNGetTypeWidth(self.handle) - - @property - def alignment(self): - """Type alignment (read-only)""" - return core.BNGetTypeAlignment(self.handle) - - @property - def signed(self): - """Wether type is signed (read-only)""" - return core.BNIsTypeSigned(self.handle) - - @property - def const(self): - """Whether type is const (read-only)""" - return core.BNIsTypeConst(self.handle) - - @property - def modified(self): - """Whether type is modified (read-only)""" - return core.BNIsTypeFloatingPoint(self.handle) - - @property - def target(self): - """Target (read-only)""" - result = core.BNGetChildType(self.handle) - if result is None: - return None - return Type(result) - - @property - def element_type(self): - """Target (read-only)""" - result = core.BNGetChildType(self.handle) - if result is None: - return None - return Type(result) - - @property - def return_value(self): - """Return value (read-only)""" - result = core.BNGetChildType(self.handle) - if result is None: - return None - return Type(result) - - @property - def calling_convention(self): - """Calling convention (read-only)""" - result = core.BNGetTypeCallingConvention(self.handle) - if result is None: - return None - return CallingConvention(None, result) - - @property - def parameters(self): - """Type parameters list (read-only)""" - count = ctypes.c_ulonglong() - params = core.BNGetTypeParameters(self.handle, count) - result = [] - for i in xrange(0, count.value): - result.append((Type(core.BNNewTypeReference(params[i].type)), params[i].name)) - core.BNFreeTypeParameterList(params, count.value) - return result - - @property - def has_variable_arguments(self): - """Whether type has variable arguments (read-only)""" - return core.BNTypeHasVariableArguments(self.handle) - - @property - def can_return(self): - """Whether type can return (read-only)""" - return core.BNFunctionTypeCanReturn(self.handle) - - @property - def structure(self): - """Structure of the type (read-only)""" - result = core.BNGetTypeStructure(self.handle) - if result is None: - return None - return Structure(result) - - @property - def enumeration(self): - """Type enumeration (read-only)""" - result = core.BNGetTypeEnumeration(self.handle) - if result is None: - return None - return Enumeration(result) - - @property - def named_type_reference(self): - """Reference to a named type (read-only)""" - result = core.BNGetTypeNamedTypeReference(self.handle) - if result is None: - return None - return NamedTypeReference(result) - - @property - def count(self): - """Type count (read-only)""" - return core.BNGetTypeElementCount(self.handle) - - def __str__(self): - return core.BNGetTypeString(self.handle) - - def __repr__(self): - return "<type: %s>" % str(self) - - def get_string_before_name(self): - return core.BNGetTypeStringBeforeName(self.handle) - - def get_string_after_name(self): - return core.BNGetTypeStringAfterName(self.handle) - - @classmethod - def void(cls): - return Type(core.BNCreateVoidType()) - - @classmethod - def bool(self): - return Type(core.BNCreateBoolType()) - - @classmethod - def int(self, width, sign = True): - return Type(core.BNCreateIntegerType(width, sign)) - - @classmethod - def float(self, width): - return Type(core.BNCreateFloatType(width)) - - @classmethod - def structure_type(self, structure_type): - return Type(core.BNCreateStructureType(structure_type.handle)) - - @classmethod - def named_type(self, named_type, width = 0, align = 1): - return Type(core.BNCreateNamedTypeReference(named_type.handle, width, align)) - - @classmethod - def named_type_from_type(self, name, t): - name = QualifiedName(name)._get_core_struct() - if t is not None: - t = t.handle - return Type(core.BNCreateNamedTypeReferenceFromType(name, t)) - - @classmethod - def enumeration_type(self, arch, e, width = None): - if width is None: - width = arch.default_int_size - return Type(core.BNCreateEnumerationType(e.handle, width)) - - @classmethod - def pointer(self, arch, t, const = False): - return Type(core.BNCreatePointerType(arch.handle, t.handle, const)) - - @classmethod - def array(self, t, count): - return Type(core.BNCreateArrayType(t.handle, count)) - - @classmethod - def function(self, ret, params, calling_convention = None, variable_arguments = False): - param_buf = (core.BNNameAndType * len(params))() - for i in xrange(0, len(params)): - if isinstance(params[i], Type): - param_buf[i].name = "" - param_buf[i].type = params[i].handle - else: - param_buf[i].name = params[i][1] - param_buf[i].type = params[i][0] - if calling_convention is not None: - calling_convention = calling_convention.handle - return Type(core.BNCreateFunctionType(ret.handle, calling_convention, param_buf, len(params), - variable_arguments)) - - def __setattr__(self, name, value): - try: - object.__setattr__(self,name,value) - except AttributeError: - raise AttributeError, "attribute '%s' is read only" % name - - -class NamedTypeReference(object): - def __init__(self, type_class = core.UnknownNamedTypeClass, name = None, handle = None): - if handle is None: - self.handle = core.BNCreateNamedType() - core.BNSetTypeReferenceClass(self.handle, type_class) - if name is not None: - name = QualifiedName(name)._get_core_struct() - core.BNSetTypeReferenceName(self.handle, name) - else: - self.handle = handle - - def __del__(self): - core.BNFreeNamedTypeReference(self.handle) - - @property - def type_class(self): - return core.BNGetTypeReferenceClass(self.handle) - - @type_class.setter - def type_class(self, value): - core.BNSetTypeReferenceClass(self.handle, value) - - @property - def name(self): - count = ctypes.c_ulonglong() - name = core.BNGetTypeReferenceName(self.handle, count) - result = QualifiedName._from_core_struct(name) - core.BNFreeQualifiedName(name) - return result - - @name.setter - def name(self, value): - value = QualifiedName(value)._get_core_struct() - core.BNSetTypeReferenceName(self.handle, value) - - def __repr__(self): - if self.type_class == core.TypedefNamedTypeClass: - return "<named type: typedef %s>" % str(self.name) - if self.type_class == core.StructNamedTypeClass: - return "<named type: struct %s>" % str(self.name) - if self.type_class == core.UnionNamedTypeClass: - return "<named type: union %s>" % str(self.name) - if self.type_class == core.EnumNamedTypeClass: - return "<named type: enum %s>" % str(self.name) - return "<named type: unknown %s>" % str(self.name) - - -class StructureMember(object): - def __init__(self, t, name, offset): - self.type = t - self.name = name - self.offset = offset - - def __repr__(self): - if len(name) == 0: - return "<member: %s, offset %#x>" % (str(self.type), self.offset) - return "<%s %s%s, offset %#x>" % (self.type.get_string_before_name(), self.name, - self.type.get_string_after_name(), self.offset) - -class Structure(object): - def __init__(self, handle = None): - if handle is None: - self.handle = core.BNCreateStructure() - else: - self.handle = handle - - def __del__(self): - core.BNFreeStructure(self.handle) - - @property - def members(self): - """Structure member list (read-only)""" - count = ctypes.c_ulonglong() - members = core.BNGetStructureMembers(self.handle, count) - result = [] - for i in xrange(0, count.value): - result.append(StructureMember(Type(core.BNNewTypeReference(members[i].type)), - members[i].name, members[i].offset)) - core.BNFreeStructureMemberList(members, count.value) - return result - - @property - def width(self): - """Structure width""" - return core.BNGetStructureWidth(self.handle) - - @width.setter - def width(self, new_width): - core.BNSetStructureWidth(self.handle, new_width) - - @property - def alignment(self): - """Structure alignment""" - return core.BNGetStructureAlignment(self.handle) - - @alignment.setter - def alignment(self, align): - core.BNSetStructureAlignment(self.handle, align) - - @property - def packed(self): - return core.BNIsStructurePacked(self.handle) - - @packed.setter - def packed(self, value): - core.BNSetStructurePacked(self.handle, value) - - @property - def union(self): - return core.BNIsStructureUnion(self.handle) - - @union.setter - def union(self, value): - core.BNSetStructureUnion(self.handle, value) - - def __setattr__(self, name, value): - try: - object.__setattr__(self,name,value) - except AttributeError: - raise AttributeError, "attribute '%s' is read only" % name - - def __repr__(self): - return "<struct: size %#x>" % self.width - - def append(self, t, name = ""): - core.BNAddStructureMember(self.handle, t.handle, name) - - def insert(self, offset, t, name = ""): - core.BNAddStructureMemberAtOffset(self.handle, t.handle, name, offset) - - def remove(self, i): - core.BNRemoveStructureMember(self.handle, i) - -class EnumerationMember(object): - def __init__(self, name, value, default): - self.name = name - self.value = value - self.default = default - - def __repr__(self): - return "<%s = %#x>" % (self.name, self.value) - -class Enumeration(object): - def __init__(self, handle = None): - if handle is None: - self.handle = core.BNCreateEnumeration() - else: - self.handle = handle - - def __del__(self): - core.BNFreeEnumeration(self.handle) - - @property - def members(self): - """Enumeration member list (read-only)""" - count = ctypes.c_ulonglong() - members = core.BNGetEnumerationMembers(self.handle, count) - result = [] - for i in xrange(0, count.value): - result.append(EnumerationMember(members[i].name, members[i].value, members[i].isDefault)) - core.BNFreeEnumerationMemberList(members, count.value) - return result - - def __setattr__(self, name, value): - try: - object.__setattr__(self,name,value) - except AttributeError: - raise AttributeError, "attribute '%s' is read only" % name - - def __repr__(self): - return "<enum: %s>" % repr(self.members) - - def append(self, name, value = None): - if value is None: - core.BNAddEnumerationMember(self.handle, name) - else: - core.BNAddEnumerationMemberWithValue(self.handle, name, value) - -class LookupTableEntry(object): - def __init__(self, from_values, to_value): - self.from_values = from_values - self.to_value = to_value - - def __repr__(self): - return "[%s] -> %#x" % (', '.join(["%#x" % i for i in self.from_values]), self.to_value) - -class RegisterValue(object): - def __init__(self, arch, value): - self.type = value.state - if value.state == core.EntryValue: - self.reg = arch.get_reg_name(value.reg) - elif value.state == core.OffsetFromEntryValue: - self.reg = arch.get_reg_name(value.reg) - self.offset = value.value - elif value.state == core.ConstantValue: - self.value = value.value - elif value.state == core.StackFrameOffset: - self.offset = value.value - elif value.state == core.SignedRangeValue: - self.offset = value.value - self.start = value.rangeStart - self.end = value.rangeEnd - self.step = value.rangeStep - if self.start & (1 << 63): - self.start |= ~((1 << 63) - 1) - if self.end & (1 << 63): - self.end |= ~((1 << 63) - 1) - elif value.state == core.UnsignedRangeValue: - self.offset = value.value - self.start = value.rangeStart - self.end = value.rangeEnd - self.step = value.rangeStep - elif value.state == core.LookupTableValue: - self.table = [] - self.mapping = {} - for i in xrange(0, value.rangeEnd): - from_list = [] - for j in xrange(0, value.table[i].fromCount): - from_list.append(value.table[i].fromValues[j]) - self.mapping[value.table[i].fromValues[j]] = value.table[i].toValue - self.table.append(LookupTableEntry(from_list, value.table[i].toValue)) - elif value.state == core.OffsetFromUndeterminedValue: - self.offset = value.value - - def __repr__(self): - if self.type == core.EntryValue: - return "<entry %s>" % self.reg - if self.type == core.OffsetFromEntryValue: - return "<entry %s + %#x>" % (self.reg, self.offset) - if self.type == core.ConstantValue: - return "<const %#x>" % self.value - if self.type == core.StackFrameOffset: - return "<stack frame offset %#x>" % self.offset - if (self.type == core.SignedRangeValue) or (self.type == core.UnsignedRangeValue): - if self.step == 1: - return "<range: %#x to %#x>" % (self.start, self.end) - return "<range: %#x to %#x, step %#x>" % (self.start, self.end, self.step) - if self.type == core.LookupTableValue: - return "<table: %s>" % ', '.join([repr(i) for i in self.table]) - if self.type == core.OffsetFromUndeterminedValue: - return "<undetermined with offset %#x>" % self.offset - return "<undetermined>" - -class StackVariable(object): - def __init__(self, ofs, name, t): - self.offset = ofs - self.name = name - self.type = t - - def __repr__(self): - return "<var@%x: %s %s>" % (self.offset, self.type, self.name) - - def __str__(self): - return self.name - -class StackVariableReference: - def __init__(self, src_operand, t, name, start_ofs, ref_ofs): - self.source_operand = src_operand - self.type = t - self.name = name - self.starting_offset = start_ofs - self.referenced_offset = ref_ofs - if self.source_operand == 0xffffffff: - self.source_operand = None - - def __repr__(self): - if self.source_operand is None: - if self.referenced_offset != self.starting_offset: - return "<ref to %s%+#x>" % (self.name, self.referenced_offset - self.starting_offset) - return "<ref to %s>" % self.name - if self.referenced_offset != self.starting_offset: - return "<operand %d ref to %s%+#x>" % (self.source_operand, self.name, self.referenced_offset) - return "<operand %d ref to %s>" % (self.source_operand, self.name) - -class ConstantReference: - def __init__(self, val, size): - self.value = val - self.size = size - - def __repr__(self): - if self.size == 0: - return "<constant %#x>" % self.value - return "<constant %#x size %d>" % (self.value, self.size) - -class IndirectBranchInfo: - def __init__(self, source_arch, source_addr, dest_arch, dest_addr, auto_defined): - self.source_arch = source_arch - self.source_addr = source_addr - self.dest_arch = dest_arch - self.dest_addr = dest_addr - self.auto_defined = auto_defined - - def __repr__(self): - return "<branch %s:%#x -> %s:%#x>" % (self.source_arch.name, self.source_addr, self.dest_arch.name, self.dest_addr) - -class HighlightColor(object): - def __init__(self, color = None, mix_color = None, mix = None, red = None, green = None, blue = None, alpha = 255): - if (red is not None) and (green is not None) and (blue is not None): - self.style = core.CustomHighlightColor - self.red = red - self.green = green - self.blue = blue - elif (mix_color is not None) and (mix is not None): - self.style = core.MixedHighlightColor - if color is None: - self.color = core.NoHighlightColor - else: - self.color = color - self.mix_color = mix_color - self.mix = mix - else: - self.style = core.StandardHighlightColor - if color is None: - self.color = core.NoHighlightColor - else: - self.color = color - self.alpha = alpha - - def _standard_color_to_str(self, color): - if color == core.NoHighlightColor: - return "none" - if color == core.BlueHighlightColor: - return "blue" - if color == core.GreenHighlightColor: - return "green" - if color == core.CyanHighlightColor: - return "cyan" - if color == core.RedHighlightColor: - return "red" - if color == core.MagentaHighlightColor: - return "magenta" - if color == core.YellowHighlightColor: - return "yellow" - if color == core.OrangeHighlightColor: - return "orange" - if color == core.WhiteHighlightColor: - return "white" - if color == core.BlackHighlightColor: - return "black" - return "%d" % color - - def __repr__(self): - if self.style == core.StandardHighlightColor: - if self.alpha == 255: - return "<color: %s>" % self._standard_color_to_str(self.color) - return "<color: %s, alpha %d>" % (self._standard_color_to_str(self.color), self.alpha) - if self.style == core.MixedHighlightColor: - if self.alpha == 255: - return "<color: mix %s to %s factor %d>" % (self._standard_color_to_str(self.color), - self._standard_color_to_str(self.mix_color), self.mix) - return "<color: mix %s to %s factor %d, alpha %d>" % (self._standard_color_to_str(self.color), - self._standard_color_to_str(self.mix_color), self.mix, self.alpha) - if self.style == core.CustomHighlightColor: - if self.alpha == 255: - return "<color: #%.2x%.2x%.2x>" % (self.red, self.green, self.blue) - return "<color: #%.2x%.2x%.2x, alpha %d>" % (self.red, self.green, self.blue, self.alpha) - return "<color>" - - def _get_core_struct(self): - result = core.BNHighlightColor() - result.style = self.style - result.color = core.NoHighlightColor - result.mix_color = core.NoHighlightColor - result.mix = 0 - result.r = 0 - result.g = 0 - result.b = 0 - result.alpha = self.alpha - - if self.style == core.StandardHighlightColor: - result.color = self.color - elif self.style == core.MixedHighlightColor: - result.color = self.color - result.mixColor = self.mix_color - result.mix = self.mix - elif self.style == core.CustomHighlightColor: - result.r = self.red - result.g = self.green - result.b = self.blue - - return result - -class _FunctionAssociatedDataStore(_AssociatedDataStore): - _defaults = {} - -class Function(object): - _associated_data = {} - - def __init__(self, view, handle): - self._view = view - self.handle = core.handle_of_type(handle, core.BNFunction) - self._advanced_analysis_requests = 0 - - def __del__(self): - if self._advanced_analysis_requests > 0: - core.BNReleaseAdvancedFunctionAnalysisDataMultiple(self.handle, self._advanced_analysis_requests) - core.BNFreeFunction(self.handle) - - @classmethod - def _unregister(cls, func): - handle = ctypes.cast(func, 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): - _FunctionAssociatedDataStore.set_default(name, value) - - @property - def name(self): - """Symbol name for the function""" - return self.symbol.name - - @name.setter - def name(self,value): - if value is None: - if self.symbol is not None: - self.view.undefine_user_symbol(self.symbol) - else: - symbol = Symbol(core.FunctionSymbol,self.start,value) - self.view.define_user_symbol(symbol) - - @property - def view(self): - """Function view (read-only)""" - return self._view - - @property - def arch(self): - """Function architecture (read-only)""" - arch = core.BNGetFunctionArchitecture(self.handle) - if arch is None: - return None - return Architecture(arch) - - @property - def platform(self): - """Function platform (read-only)""" - platform = core.BNGetFunctionPlatform(self.handle) - if platform is None: - return None - return Platform(None, handle = platform) - - @property - def start(self): - """Function start (read-only)""" - return core.BNGetFunctionStart(self.handle) - - @property - def symbol(self): - """Function symbol(read-only)""" - sym = core.BNGetFunctionSymbol(self.handle) - if sym is None: - return None - return Symbol(None, None, None, handle = sym) - - @property - def auto(self): - """Whether function was automatically discovered (read-only)""" - return core.BNWasFunctionAutomaticallyDiscovered(self.handle) - - @property - def can_return(self): - """Whether function can return (read-only)""" - return core.BNCanFunctionReturn(self.handle) - - @property - def explicitly_defined_type(self): - """Whether function has explicitly defined types (read-only)""" - return core.BNHasExplicitlyDefinedType(self.handle) - - @property - def needs_update(self): - """Whether the function has analysis that needs to be updated (read-only)""" - return core.BNIsFunctionUpdateNeeded(self.handle) - - @property - def basic_blocks(self): - """List of basic blocks (read-only)""" - count = ctypes.c_ulonglong() - blocks = core.BNGetFunctionBasicBlockList(self.handle, count) - result = [] - for i in xrange(0, count.value): - result.append(BasicBlock(self._view, core.BNNewBasicBlockReference(blocks[i]))) - core.BNFreeBasicBlockList(blocks, count.value) - return result - - @property - def comments(self): - """Dict of comments (read-only)""" - count = ctypes.c_ulonglong() - addrs = core.BNGetCommentedAddresses(self.handle, count) - result = {} - for i in xrange(0, count.value): - result[addrs[i]] = self.get_comment_at(addrs[i]) - core.BNFreeAddressList(addrs) - return result - - @property - def low_level_il(self): - """Function low level IL (read-only)""" - return LowLevelILFunction(self.arch, core.BNGetFunctionLowLevelIL(self.handle), self) - - @property - def lifted_il(self): - """Function lifted IL (read-only)""" - return LowLevelILFunction(self.arch, core.BNGetFunctionLiftedIL(self.handle), self) - - @property - def function_type(self): - """Function type""" - return Type(core.BNGetFunctionType(self.handle)) - - @function_type.setter - def function_type(self, value): - self.set_user_type(value) - - @property - def stack_layout(self): - """List of function stack (read-only)""" - count = ctypes.c_ulonglong() - v = core.BNGetStackLayout(self.handle, count) - result = [] - for i in xrange(0, count.value): - result.append(StackVariable(v[i].offset, v[i].name, Type(handle = core.BNNewTypeReference(v[i].type)))) - result.sort(key = lambda x: x.offset) - core.BNFreeStackLayout(v, count.value) - return result - - @property - def indirect_branches(self): - """List of indirect branches (read-only)""" - count = ctypes.c_ulonglong() - branches = core.BNGetIndirectBranches(self.handle, count) - result = [] - for i in xrange(0, count.value): - result.append(IndirectBranchInfo(Architecture(branches[i].sourceArch), branches[i].sourceAddr, Architecture(branches[i].destArch), branches[i].destAddr, branches[i].autoDefined)) - core.BNFreeIndirectBranchList(branches) - return result - - @property - def session_data(self): - """Dictionary object where plugins can store arbitrary data associated with the function""" - handle = ctypes.cast(self.handle, ctypes.c_void_p) - if handle.value not in Function._associated_data: - obj = _FunctionAssociatedDataStore() - Function._associated_data[handle.value] = obj - return obj - else: - return Function._associated_data[handle.value] - - def __iter__(self): - count = ctypes.c_ulonglong() - blocks = core.BNGetFunctionBasicBlockList(self.handle, count) - try: - for i in xrange(0, count.value): - yield BasicBlock(self._view, core.BNNewBasicBlockReference(blocks[i])) - finally: - core.BNFreeBasicBlockList(blocks, count.value) - - def __setattr__(self, name, value): - try: - object.__setattr__(self,name,value) - except AttributeError: - raise AttributeError, "attribute '%s' is read only" % name - - def __repr__(self): - arch = self.arch - if arch: - return "<func: %s@%#x>" % (arch.name, self.start) - else: - return "<func: %#x>" % self.start - - def mark_recent_use(self): - core.BNMarkFunctionAsRecentlyUsed(self.handle) - - def get_comment_at(self, addr): - return core.BNGetCommentForAddress(self.handle, addr) - - def set_comment(self, addr, comment): - core.BNSetCommentForAddress(self.handle, addr, comment) - - def get_low_level_il_at(self, arch, addr): - return core.BNGetLowLevelILForInstruction(self.handle, arch.handle, addr) - - def get_low_level_il_exits_at(self, arch, addr): - count = ctypes.c_ulonglong() - exits = core.BNGetLowLevelILExitsForInstruction(self.handle, arch.handle, addr, count) - result = [] - for i in xrange(0, count.value): - result.append(exits[i]) - core.BNFreeLowLevelILInstructionList(exits) - return result - - def get_reg_value_at(self, arch, addr, reg): - if isinstance(reg, str): - reg = arch.regs[reg].index - value = core.BNGetRegisterValueAtInstruction(self.handle, arch.handle, addr, reg) - result = RegisterValue(arch, value) - core.BNFreeRegisterValue(value) - return result - - def get_reg_value_after(self, arch, addr, reg): - if isinstance(reg, str): - reg = arch.regs[reg].index - value = core.BNGetRegisterValueAfterInstruction(self.handle, arch.handle, addr, reg) - result = RegisterValue(arch, value) - core.BNFreeRegisterValue(value) - return result - - def get_reg_value_at_low_level_il_instruction(self, i, reg): - if isinstance(reg, str): - reg = self.arch.regs[reg].index - value = core.BNGetRegisterValueAtLowLevelILInstruction(self.handle, i, reg) - result = RegisterValue(self.arch, value) - core.BNFreeRegisterValue(value) - return result - - def get_reg_value_after_low_level_il_instruction(self, i, reg): - if isinstance(reg, str): - reg = self.arch.regs[reg].index - value = core.BNGetRegisterValueAfterLowLevelILInstruction(self.handle, i, reg) - result = RegisterValue(self.arch, value) - core.BNFreeRegisterValue(value) - return result - - def get_stack_contents_at(self, arch, addr, offset, size): - value = core.BNGetStackContentsAtInstruction(self.handle, arch.handle, addr, offset, size) - result = RegisterValue(arch, value) - core.BNFreeRegisterValue(value) - return result - - def get_stack_contents_after(self, arch, addr, offset, size): - value = core.BNGetStackContentsAfterInstruction(self.handle, arch.handle, addr, offset, size) - result = RegisterValue(arch, value) - core.BNFreeRegisterValue(value) - return result - - def get_stack_contents_at_low_level_il_instruction(self, i, offset, size): - value = core.BNGetStackContentsAtLowLevelILInstruction(self.handle, i, offset, size) - result = RegisterValue(self.arch, value) - core.BNFreeRegisterValue(value) - return result - - def get_stack_contents_after_low_level_il_instruction(self, i, offset, size): - value = core.BNGetStackContentsAfterInstruction(self.handle, i, offset, size) - result = RegisterValue(self.arch, value) - core.BNFreeRegisterValue(value) - return result - - def get_parameter_at(self, arch, addr, func_type, i): - if func_type is not None: - func_type = func_type.handle - value = core.BNGetParameterValueAtInstruction(self.handle, arch.handle, addr, func_type, i) - result = RegisterValue(arch, value) - core.BNFreeRegisterValue(value) - return result - - def get_parameter_at_low_level_il_instruction(self, instr, func_type, i): - if func_type is not None: - func_type = func_type.handle - value = core.BNGetParameterValueAtLowLevelILInstruction(self.handle, instr, func_type, i) - result = RegisterValue(self.arch, value) - core.BNFreeRegisterValue(value) - return result - - def get_regs_read_by(self, arch, addr): - count = ctypes.c_ulonglong() - regs = core.BNGetRegistersReadByInstruction(self.handle, arch.handle, addr, count) - result = [] - for i in xrange(0, count.value): - result.append(arch.get_reg_name(regs[i])) - core.BNFreeRegisterList(regs) - return result - - def get_regs_written_by(self, arch, addr): - count = ctypes.c_ulonglong() - regs = core.BNGetRegistersWrittenByInstruction(self.handle, arch.handle, addr, count) - result = [] - for i in xrange(0, count.value): - result.append(arch.get_reg_name(regs[i])) - core.BNFreeRegisterList(regs) - return result - - def get_stack_vars_referenced_by(self, arch, addr): - count = ctypes.c_ulonglong() - refs = core.BNGetStackVariablesReferencedByInstruction(self.handle, arch.handle, addr, count) - result = [] - for i in xrange(0, count.value): - result.append(StackVariableReference(refs[i].sourceOperand, Type(core.BNNewTypeReference(refs[i].type)), - refs[i].name, refs[i].startingOffset, refs[i].referencedOffset)) - core.BNFreeStackVariableReferenceList(refs, count.value) - return result - - def get_constants_referenced_by(self, arch, addr): - count = ctypes.c_ulonglong() - refs = core.BNGetConstantsReferencedByInstruction(self.handle, arch.handle, addr, count) - result = [] - for i in xrange(0, count.value): - result.append(ConstantReference(refs[i].value, refs[i].size)) - core.BNFreeConstantReferenceList(refs) - return result - - def get_lifted_il_at(self, arch, addr): - return core.BNGetLiftedILForInstruction(self.handle, arch.handle, addr) - - def get_lifted_il_flag_uses_for_definition(self, i, flag): - if isinstance(flag, str): - flag = self.arch._flags[flag] - count = ctypes.c_ulonglong() - instrs = core.BNGetLiftedILFlagUsesForDefinition(self.handle, i, flag, count) - result = [] - for i in xrange(0, count.value): - result.append(instrs[i]) - core.BNFreeLowLevelILInstructionList(instrs) - return result - - def get_lifted_il_flag_definitions_for_use(self, i, flag): - if isinstance(flag, str): - flag = self.arch._flags[flag] - count = ctypes.c_ulonglong() - instrs = core.BNGetLiftedILFlagDefinitionsForUse(self.handle, i, flag, count) - result = [] - for i in xrange(0, count.value): - result.append(instrs[i]) - core.BNFreeLowLevelILInstructionList(instrs) - return result - - def get_flags_read_by_lifted_il_instruction(self, i): - count = ctypes.c_ulonglong() - flags = core.BNGetFlagsReadByLiftedILInstruction(self.handle, i, count) - result = [] - for i in xrange(0, count.value): - result.append(self.arch._flags_by_index[flags[i]]) - core.BNFreeRegisterList(flags) - return result - - def get_flags_written_by_lifted_il_instruction(self, i): - count = ctypes.c_ulonglong() - flags = core.BNGetFlagsWrittenByLiftedILInstruction(self.handle, i, count) - result = [] - for i in xrange(0, count.value): - result.append(self.arch._flags_by_index[flags[i]]) - core.BNFreeRegisterList(flags) - return result - - def create_graph(self): - return FunctionGraph(self._view, core.BNCreateFunctionGraph(self.handle)) - - def apply_imported_types(self, sym): - core.BNApplyImportedTypes(self.handle, sym.handle) - - def apply_auto_discovered_type(self, func_type): - core.BNApplyAutoDiscoveredFunctionType(self.handle, func_type.handle) - - def set_auto_indirect_branches(self, source_arch, source, branches): - branch_list = (core.BNArchitectureAndAddress * len(branches))() - for i in xrange(len(branches)): - branch_list[i].arch = branches[i][0].handle - branch_list[i].address = branches[i][1] - core.BNSetAutoIndirectBranches(self.handle, source_arch.handle, source, branch_list, len(branches)) - - def set_user_indirect_branches(self, source_arch, source, branches): - branch_list = (core.BNArchitectureAndAddress * len(branches))() - for i in xrange(len(branches)): - branch_list[i].arch = branches[i][0].handle - branch_list[i].address = branches[i][1] - core.BNSetUserIndirectBranches(self.handle, source_arch.handle, source, branch_list, len(branches)) - - def get_indirect_branches_at(self, arch, addr): - count = ctypes.c_ulonglong() - branches = core.BNGetIndirectBranchesAt(self.handle, arch.handle, addr, count) - result = [] - for i in xrange(0, count.value): - result.append(IndirectBranchInfo(Architecture(branches[i].sourceArch), branches[i].sourceAddr, Architecture(branches[i].destArch), branches[i].destAddr, branches[i].autoDefined)) - core.BNFreeIndirectBranchList(branches) - return result - - def get_block_annotations(self, arch, addr): - count = ctypes.c_ulonglong(0) - lines = core.BNGetFunctionBlockAnnotations(self.handle, arch.handle, addr, count) - result = [] - for i in xrange(0, count.value): - tokens = [] - for j in xrange(0, lines[i].count): - token_type = core.BNInstructionTextTokenType_names[lines[i].tokens[j].type] - text = lines[i].tokens[j].text - value = lines[i].tokens[j].value - size = lines[i].tokens[j].size - operand = lines[i].tokens[j].operand - tokens.append(InstructionTextToken(token_type, text, value, size, operand)) - result.append(tokens) - core.BNFreeInstructionTextLines(lines, count.value) - return result - - def set_auto_type(self, value): - core.BNSetFunctionAutoType(self.handle, value.handle) - - def set_user_type(self, value): - core.BNSetFunctionUserType(self.handle, value.handle) - - def get_int_display_type(self, arch, instr_addr, value, operand): - return core.BNGetIntegerConstantDisplayType(self.handle, arch.handle, instr_addr, value, operand) - - def set_int_display_type(self, arch, instr_addr, value, operand, display_type): - if isinstance(display_type, str): - display_type = core.BNIntegerDisplayType_by_name[display_type] - core.BNSetIntegerConstantDisplayType(self.handle, arch.handle, instr_addr, value, operand, display_type) - - def reanalyze(self): - """ - ``reanalyze`` causes this functions to be reanalyzed. This function does not wait for the analysis to finish. - - :rtype: None - """ - core.BNReanalyzeFunction(self.handle) - - def request_advanced_analysis_data(self): - core.BNRequestAdvancedFunctionAnalysisData(self.handle) - self._advanced_analysis_requests += 1 - - def release_advanced_analysis_data(self): - core.BNReleaseAdvancedFunctionAnalysisData(self.handle) - self._advanced_analysis_requests -= 1 - - def get_basic_block_at(self, arch, addr): - block = core.BNGetFunctionBasicBlockAtAddress(self.handle, arch.handle, addr) - if not block: - return None - return BasicBlock(self._view, handle = block) - - def get_instr_highlight(self, arch, addr): - color = core.BNGetInstructionHighlight(self.handle, arch.handle, addr) - if color.style == core.StandardHighlightColor: - return HighlightColor(color = color.color, alpha = color.alpha) - elif color.style == core.MixedHighlightColor: - return HighlightColor(color = color.color, mix_color = color.mixColor, mix = color.mix, alpha = color.alpha) - elif color.style == core.CustomHighlightColor: - return HighlightColor(red = color.r, green = color.g, blue = color.b, alpha = color.alpha) - return HighlightColor(color = core.NoHighlightColor) - - def set_auto_instr_highlight(self, arch, addr, color): - if not isinstance(color, HighlightColor): - color = HighlightColor(color = color) - core.BNSetAutoInstructionHighlight(self.handle, arch.handle, addr, color._get_core_struct()) - - def set_user_instr_highlight(self, arch, addr, color): - if not isinstance(color, HighlightColor): - color = HighlightColor(color = color) - core.BNSetUserInstructionHighlight(self.handle, arch.handle, addr, color._get_core_struct()) - -class AdvancedFunctionAnalysisDataRequestor(object): - def __init__(self, func = None): - self._function = func - if self._function is not None: - self._function.request_advanced_analysis_data() - - def __del__(self): - if self._function is not None: - self._function.release_advanced_analysis_data() - - @property - def function(self): - return self._function - - @function.setter - def function(self, func): - if self._function is not None: - self._function.release_advanced_analysis_data() - self._function = func - if self._function is not None: - self._function.request_advanced_analysis_data() - - def close(self): - if self._function is not None: - self._function.release_advanced_analysis_data() - self._function = None -class BasicBlockEdge(object): - def __init__(self, branch_type, target, arch): - self.type = core.BNBranchType_names[branch_type] - if self.type != "UnresolvedBranch": - self.target = target - self.arch = arch - - def __repr__(self): - if self.type == "UnresolvedBranch": - return "<%s>" % self.type - elif self.arch: - return "<%s: %s@%#x>" % (self.type, self.arch.name, self.target) - else: - return "<%s: %#x>" % (self.type, self.target) - -class BasicBlock(object): - def __init__(self, view, handle): - self.view = view - self.handle = core.handle_of_type(handle, core.BNBasicBlock) - - def __del__(self): - core.BNFreeBasicBlock(self.handle) - - @property - def function(self): - """Basic block function (read-only)""" - func = core.BNGetBasicBlockFunction(self.handle) - if func is None: - return None - return Function(self.view, func) - - @property - def arch(self): - """Basic block architecture (read-only)""" - arch = core.BNGetBasicBlockArchitecture(self.handle) - if arch is None: - return None - return Architecture(arch) - - @property - def start(self): - """Basic block start (read-only)""" - return core.BNGetBasicBlockStart(self.handle) - - @property - def end(self): - """Basic block end (read-only)""" - return core.BNGetBasicBlockEnd(self.handle) - - @property - def length(self): - """Basic block length (read-only)""" - return core.BNGetBasicBlockLength(self.handle) - - @property - def outgoing_edges(self): - """List of basic block outgoing edges (read-only)""" - count = ctypes.c_ulonglong(0) - edges = core.BNGetBasicBlockOutgoingEdges(self.handle, count) - result = [] - for i in xrange(0, count.value): - branch_type = edges[i].type - target = edges[i].target - if edges[i].arch: - arch = Architecture(edges[i].arch) - else: - arch = None - result.append(BasicBlockEdge(branch_type, target, arch)) - core.BNFreeBasicBlockOutgoingEdgeList(edges) - return result - - @property - def has_undetermined_outgoing_edges(self): - """Whether basic block has undetermined outgoing edges (read-only)""" - return core.BNBasicBlockHasUndeterminedOutgoingEdges(self.handle) - - @property - def annotations(self): - """List of automatic annotations for the start of this block (read-only)""" - return self.function.get_block_annotations(self.arch, self.start) - - @property - def disassembly_text(self): - return self.get_disassembly_text() - - @property - def highlight(self): - """Highlight color for basic block""" - color = core.BNGetBasicBlockHighlight(self.handle) - if color.style == core.StandardHighlightColor: - return HighlightColor(color = color.color, alpha = color.alpha) - elif color.style == core.MixedHighlightColor: - return HighlightColor(color = color.color, mix_color = color.mixColor, mix = color.mix, alpha = color.alpha) - elif color.style == core.CustomHighlightColor: - return HighlightColor(red = color.r, green = color.g, blue = color.b, alpha = color.alpha) - return HighlightColor(color = core.NoHighlightColor) - - @highlight.setter - def highlight(self, value): - self.set_user_highlight(value) - - def __setattr__(self, name, value): - try: - object.__setattr__(self,name,value) - except AttributeError: - raise AttributeError, "attribute '%s' is read only" % name - - def __len__(self): - return int(core.BNGetBasicBlockLength(self.handle)) - - def __repr__(self): - arch = self.arch - if arch: - return "<block: %s@%#x-%#x>" % (arch.name, self.start, self.end) - else: - return "<block: %#x-%#x>" % (self.start, self.end) - - def __iter__(self): - start = self.start - end = self.end - - idx = start - while idx < end: - data = self.view.read(idx, 16) - inst_info = self.view.arch.get_instruction_info(data, idx) - inst_text = self.view.arch.get_instruction_text(data, idx) - - yield inst_text - idx += inst_info.length - - def mark_recent_use(self): - core.BNMarkBasicBlockAsRecentlyUsed(self.handle) - - def get_disassembly_text(self, settings = None): - settings_obj = None - if settings: - settings_obj = settings.handle - - count = ctypes.c_ulonglong() - lines = core.BNGetBasicBlockDisassemblyText(self.handle, settings_obj, count) - result = [] - for i in xrange(0, count.value): - addr = lines[i].addr - tokens = [] - for j in xrange(0, lines[i].count): - token_type = core.BNInstructionTextTokenType_names[lines[i].tokens[j].type] - text = lines[i].tokens[j].text - value = lines[i].tokens[j].value - size = lines[i].tokens[j].size - operand = lines[i].tokens[j].operand - tokens.append(InstructionTextToken(token_type, text, value, size, operand)) - result.append(DisassemblyTextLine(addr, tokens)) - core.BNFreeDisassemblyTextLines(lines, count.value) - return result - - def set_auto_highlight(self, color): - if not isinstance(color, HighlightColor): - color = HighlightColor(color = color) - core.BNSetAutoBasicBlockHighlight(self.handle, color._get_core_struct()) - - def set_user_highlight(self, color): - if not isinstance(color, HighlightColor): - color = HighlightColor(color = color) - core.BNSetUserBasicBlockHighlight(self.handle, color._get_core_struct()) - -class LowLevelILBasicBlock(BasicBlock): - def __init__(self, view, handle, owner): - super(LowLevelILBasicBlock, self).__init__(view, handle) - self.il_function = owner - - def __iter__(self): - for idx in xrange(self.start, self.end): - yield self.il_function[idx] - -class DisassemblyTextLine(object): - def __init__(self, addr, tokens): - self.address = addr - self.tokens = tokens - - def __str__(self): - result = "" - for token in self.tokens: - result += token.text - return result - - def __repr__(self): - return "<%#x: %s>" % (self.address, str(self)) - -class FunctionGraphEdge: - def __init__(self, branch_type, arch, target, points): - self.type = branch_type - self.arch = arch - self.target = target - self.points = points - - def __repr__(self): - if self.arch: - return "<%s: %s@%#x>" % (self.type, self.arch.name, self.target) - return "<%s: %#x>" % (self.type, self.target) - -class FunctionGraphBlock(object): - def __init__(self, handle): - self.handle = handle - - def __del__(self): - core.BNFreeFunctionGraphBlock(self.handle) - - @property - def basic_block(self): - """Basic block associated with this part of the funciton graph (read-only)""" - block = core.BNGetFunctionGraphBasicBlock(self.handle) - func = core.BNGetBasicBlockFunction(block) - if func is None: - core.BNFreeBasicBlock(block) - block = None - else: - block = BasicBlock(BinaryView(handle = core.BNGetFunctionData(func)), block) - core.BNFreeFunction(func) - return block - - @property - def arch(self): - """Function graph block architecture (read-only)""" - arch = core.BNGetFunctionGraphBlockArchitecture(self.handle) - if arch is None: - return None - return Architecture(arch) - - @property - def start(self): - """Function graph block start (read-only)""" - return core.BNGetFunctionGraphBlockStart(self.handle) - - @property - def end(self): - """Function graph block end (read-only)""" - return core.BNGetFunctionGraphBlockEnd(self.handle) - - @property - def x(self): - """Function graph block X (read-only)""" - return core.BNGetFunctionGraphBlockX(self.handle) - - @property - def y(self): - """Function graph block Y (read-only)""" - return core.BNGetFunctionGraphBlockY(self.handle) - - @property - def width(self): - """Function graph block width (read-only)""" - return core.BNGetFunctionGraphBlockWidth(self.handle) - - @property - def height(self): - """Function graph block height (read-only)""" - return core.BNGetFunctionGraphBlockHeight(self.handle) - - @property - def lines(self): - """Function graph block list of lines (read-only)""" - count = ctypes.c_ulonglong() - lines = core.BNGetFunctionGraphBlockLines(self.handle, count) - result = [] - for i in xrange(0, count.value): - addr = lines[i].addr - tokens = [] - for j in xrange(0, lines[i].count): - token_type = core.BNInstructionTextTokenType_names[lines[i].tokens[j].type] - text = lines[i].tokens[j].text - value = lines[i].tokens[j].value - size = lines[i].tokens[j].size - operand = lines[i].tokens[j].operand - tokens.append(InstructionTextToken(token_type, text, value, size, operand)) - result.append(DisassemblyTextLine(addr, tokens)) - core.BNFreeDisassemblyTextLines(lines, count.value) - return result - - @property - def outgoing_edges(self): - """Function graph block list of outgoing edges (read-only)""" - count = ctypes.c_ulonglong() - edges = core.BNGetFunctionGraphBlockOutgoingEdges(self.handle, count) - result = [] - for i in xrange(0, count.value): - branch_type = core.BNBranchType_names[edges[i].type] - target = edges[i].target - arch = None - if edges[i].arch is not None: - arch = Architecture(edges[i].arch) - points = [] - for j in xrange(0, edges[i].pointCount): - points.append((edges[i].points[j].x, edges[i].points[j].y)) - result.append(FunctionGraphEdge(branch_type, arch, target, points)) - core.BNFreeFunctionGraphBlockOutgoingEdgeList(edges, count.value) - return result - - def __setattr__(self, name, value): - try: - object.__setattr__(self,name,value) - except AttributeError: - raise AttributeError, "attribute '%s' is read only" % name - - def __repr__(self): - arch = self.arch - if arch: - return "<graph block: %s@%#x-%#x>" % (arch.name, self.start, self.end) - else: - return "<graph block: %#x-%#x>" % (self.start, self.end) - - def __iter__(self): - count = ctypes.c_ulonglong() - lines = core.BNGetFunctionGraphBlockLines(self.handle, count) - try: - for i in xrange(0, count.value): - addr = lines[i].addr - tokens = [] - for j in xrange(0, lines[i].count): - token_type = core.BNInstructionTextTokenType_names[lines[i].tokens[j].type] - text = lines[i].tokens[j].text - value = lines[i].tokens[j].value - size = lines[i].tokens[j].size - operand = lines[i].tokens[j].operand - tokens.append(InstructionTextToken(token_type, text, value, size, operand)) - yield DisassemblyTextLine(addr, tokens) - finally: - core.BNFreeDisassemblyTextLines(lines, count.value) - -class DisassemblySettings(object): - def __init__(self, handle = None): - if handle is None: - self.handle = core.BNCreateDisassemblySettings() - else: - self.handle = handle - - def __del__(self): - core.BNFreeDisassemblySettings(self.handle) - - @property - def width(self): - return core.BNGetDisassemblyWidth(self.handle) - - @width.setter - def width(self, value): - core.BNSetDisassemblyWidth(self.handle, value) - - @property - def max_symbol_width(self): - return core.BNGetDisassemblyMaximumSymbolWidth(self.handle) - - @max_symbol_width.setter - def max_symbol_width(self, value): - core.BNSetDisassemblyMaximumSymbolWidth(self.handle, value) - - def is_option_set(self, option): - if isinstance(option, str): - option = core.BNDisassemblyOption_by_name[option] - return core.BNIsDisassemblySettingsOptionSet(self.handle, option) - - def set_option(self, option, state = True): - if isinstance(option, str): - option = core.BNDisassemblyOption_by_name[option] - core.BNSetDisassemblySettingsOption(self.handle, option, state) - -class FunctionGraph(object): - def __init__(self, view, handle): - self.view = view - self.handle = handle - self._on_complete = None - self._cb = ctypes.CFUNCTYPE(None, ctypes.c_void_p)(self._complete) - - def __del__(self): - self.abort() - core.BNFreeFunctionGraph(self.handle) - - @property - def function(self): - """Function for a function graph (read-only)""" - func = core.BNGetFunctionForFunctionGraph(self.handle) - if func is None: - return None - return Function(self.view, func) - - @property - def complete(self): - """Whether function graph layout is complete (read-only)""" - return core.BNIsFunctionGraphLayoutComplete(self.handle) - - @property - def type(self): - """Function graph type (read-only)""" - return core.BNFunctionGraphType_names[core.BNGetFunctionGraphType(self.handle)] - - @property - def blocks(self): - """List of basic blocks in function (read-only)""" - count = ctypes.c_ulonglong() - blocks = core.BNGetFunctionGraphBlocks(self.handle, count) - result = [] - for i in xrange(0, count.value): - result.append(FunctionGraphBlock(core.BNNewFunctionGraphBlockReference(blocks[i]))) - core.BNFreeFunctionGraphBlockList(blocks, count.value) - return result - - @property - def width(self): - """Function graph width (read-only)""" - return core.BNGetFunctionGraphWidth(self.handle) - - @property - def height(self): - """Function graph height (read-only)""" - return core.BNGetFunctionGraphHeight(self.handle) - - @property - def horizontal_block_margin(self): - return core.BNGetHorizontalFunctionGraphBlockMargin(self.handle) - - @horizontal_block_margin.setter - def horizontal_block_margin(self, value): - core.BNSetFunctionGraphBlockMargins(self.handle, value, self.vertical_block_margin) - - @property - def vertical_block_margin(self): - return core.BNGetVerticalFunctionGraphBlockMargin(self.handle) - - @vertical_block_margin.setter - def vertical_block_margin(self, value): - core.BNSetFunctionGraphBlockMargins(self.handle, self.horizontal_block_margin, value) - - @property - def settings(self): - return DisassemblySettings(core.BNGetFunctionGraphSettings(self.handle)) - - def __setattr__(self, name, value): - try: - object.__setattr__(self,name,value) - except AttributeError: - raise AttributeError, "attribute '%s' is read only" % name - - def __repr__(self): - return "<graph of %s>" % repr(self.function) - - def __iter__(self): - count = ctypes.c_ulonglong() - blocks = core.BNGetFunctionGraphBlocks(self.handle, count) - try: - for i in xrange(0, count.value): - yield FunctionGraphBlock(core.BNNewFunctionGraphBlockReference(blocks[i])) - finally: - core.BNFreeFunctionGraphBlockList(blocks, count.value) - - def _complete(self, ctxt): - try: - if self._on_complete is not None: - self._on_complete() - except: - log_error(traceback.format_exc()) - - def layout(self, graph_type = core.NormalFunctionGraph): - if isinstance(graph_type, str): - graph_type = core.BNFunctionGraphType_by_name[graph_type] - core.BNStartFunctionGraphLayout(self.handle, graph_type) - - def _wait_complete(self): - self._wait_cond.acquire() - self._wait_cond.notify() - self._wait_cond.release() - - def layout_and_wait(self, graph_type = core.NormalFunctionGraph): - self._wait_cond = threading.Condition() - self.on_complete(self._wait_complete) - self.layout(graph_type) - - self._wait_cond.acquire() - while not self.complete: - self._wait_cond.wait() - self._wait_cond.release() - - def on_complete(self, callback): - self._on_complete = callback - core.BNSetFunctionGraphCompleteCallback(self.handle, None, self._cb) - - def abort(self): - core.BNAbortFunctionGraph(self.handle) - - def get_blocks_in_region(self, left, top, right, bottom): - count = ctypes.c_ulonglong() - blocks = core.BNGetFunctionGraphBlocksInRegion(self.handle, left, top, right, bottom, count) - result = [] - for i in xrange(0, count.value): - result.append(FunctionGraphBlock(core.BNNewFunctionGraphBlockReference(blocks[i]))) - core.BNFreeFunctionGraphBlockList(blocks, count.value) - return result - - def is_option_set(self, option): - if isinstance(option, str): - option = core.BNDisassemblyOption_by_name(option) - return core.BNIsFunctionGraphOptionSet(self.handle, option) - - def set_option(self, option, state = True): - if isinstance(option, str): - option = core.BNDisassemblyOption_by_name(option) - core.BNSetFunctionGraphOption(self.handle, option, state) - -class RegisterInfo(object): - def __init__(self, full_width_reg, size, offset = 0, extend = core.NoExtend, index = None): - self.full_width_reg = full_width_reg - self.offset = offset - self.size = size - self.extend = extend - self.index = index - - def __repr__(self): - if (self.extend == core.ZeroExtendToFullWidth) or (self.extend == "ZeroExtendToFullWidth"): - extend = ", zero extend" - elif (self.extend == core.SignExtendToFullWidth) or (self.extend == "SignExtendToFullWidth"): - extend = ", sign extend" - else: - extend = "" - return "<reg: size %d, offset %d in %s%s>" % (self.size, self.offset, self.full_width_reg, extend) - -class InstructionBranch(object): - def __init__(self, branch_type, target = 0, arch = None): - self.type = branch_type - self.target = target - self.arch = arch - - def __repr__(self): - branch_type = self.type - if not isinstance(branch_type, str): - branch_type = core.BNBranchType_names[branch_type] - if self.arch is not None: - return "<%s: %s@%#x>" % (branch_type, self.arch.name, self.target) - return "<%s: %#x>" % (branch_type, self.target) - -class InstructionInfo(object): - def __init__(self): - self.length = 0 - self.branch_delay = False - self.branches = [] - - def add_branch(self, branch_type, target = 0, arch = None): - self.branches.append(InstructionBranch(branch_type, target, arch)) - - def __repr__(self): - branch_delay = "" - if self.branch_delay: - branch_delay = ", delay slot" - return "<instr: %d bytes%s, %s>" % (self.length, branch_delay, repr(self.branches)) - -class InstructionTextToken(object): - """ - ``class InstructionTextToken`` is used to tell the core about the various components in the disassembly views. - - ======================== ============================================ - InstructionTextTokenType Description - ======================== ============================================ - TextToken Text that doesn't fit into the other tokens - InstructionToken The instruction mnemonic - OperandSeparatorToken The comma or whatever else separates tokens - RegisterToken Registers - IntegerToken Integers - PossibleAddressToken Integers that are likely addresses - BeginMemoryOperandToken The start of memory operand - EndMemoryOperandToken The end of a memory operand - FloatingPointToken Floating point number - AnnotationToken **For internal use only** - CodeRelativeAddressToken **For internal use only** - StackVariableTypeToken **For internal use only** - DataVariableTypeToken **For internal use only** - FunctionReturnTypeToken **For internal use only** - FunctionAttributeToken **For internal use only** - ArgumentTypeToken **For internal use only** - ArgumentNameToken **For internal use only** - HexDumpByteValueToken **For internal use only** - HexDumpSkippedByteToken **For internal use only** - HexDumpInvalidByteToken **For internal use only** - HexDumpTextToken **For internal use only** - OpcodeToken **For internal use only** - StringToken **For internal use only** - CharacterConstantToken **For internal use only** - CodeSymbolToken **For internal use only** - DataSymbolToken **For internal use only** - StackVariableToken **For internal use only** - ImportToken **For internal use only** - AddressDisplayToken **For internal use only** - ======================== ============================================ - - """ - def __init__(self, token_type, text, value = 0, size = 0, operand = 0xffffffff): - self.type = token_type - self.text = text - self.value = value - self.size = size - self.operand = operand - - def __str__(self): - return self.text - - def __repr__(self): - return repr(self.text) - -class _ArchitectureMetaClass(type): - @property - def list(self): - _init_plugins() - count = ctypes.c_ulonglong() - archs = core.BNGetArchitectureList(count) - result = [] - for i in xrange(0, count.value): - result.append(Architecture(archs[i])) - core.BNFreeArchitectureList(archs) - return result - - def __iter__(self): - _init_plugins() - count = ctypes.c_ulonglong() - archs = core.BNGetArchitectureList(count) - try: - for i in xrange(0, count.value): - yield Architecture(archs[i]) - finally: - core.BNFreeArchitectureList(archs) - - def __getitem__(cls, name): - _init_plugins() - arch = core.BNGetArchitectureByName(name) - if arch is None: - raise KeyError, "'%s' is not a valid architecture" % str(name) - return Architecture(arch) - - def register(cls): - _init_plugins() - if cls.name is None: - raise ValueError, "architecture 'name' is not defined" - arch = cls() - cls._registered_cb = arch._cb - arch.handle = core.BNRegisterArchitecture(cls.name, arch._cb) - - def __setattr__(self, name, value): - try: - type.__setattr__(self,name,value) - except AttributeError: - raise AttributeError, "attribute '%s' is read only" % name - -class Architecture(object): - """ - ``class Architecture`` is the parent class for all CPU architectures. Subclasses of Architecture implement assembly, - disassembly, IL lifting, and patching. - - ``class Architecture`` has a ``__metaclass__`` with the additional methods ``register``, and supports - iteration:: - - >>> #List the architectures - >>> list(Architecture) - [<arch: aarch64>, <arch: armv7>, <arch: armv7eb>, <arch: mipsel32>, <arch: mips32>, <arch: powerpc>, - <arch: x86>, <arch: x86_64>] - >>> #Register a new Architecture - >>> class MyArch(Architecture): - ... name = "MyArch" - ... - >>> MyArch.register() - >>> list(Architecture) - [<arch: aarch64>, <arch: armv7>, <arch: armv7eb>, <arch: mipsel32>, <arch: mips32>, <arch: powerpc>, - <arch: x86>, <arch: x86_64>, <arch: MyArch>] - >>> - - For the purposes of this documentation the variable ``arch`` will be used in the following context :: - - >>> from binaryninja import * - >>> arch = Architecture['x86'] - """ - name = None - endianness = core.LittleEndian - address_size = 8 - default_int_size = 4 - max_instr_length = 16 - opcode_display_length = 8 - regs = {} - stack_pointer = None - link_reg = None - flags = [] - flag_write_types = [] - flag_roles = {} - flags_required_for_flag_condition = {} - flags_written_by_flag_write_type = {} - __metaclass__ = _ArchitectureMetaClass - next_address = 0 - - def __init__(self, handle = None): - if handle is not None: - self.handle = core.handle_of_type(handle, core.BNArchitecture) - self.__dict__["name"] = core.BNGetArchitectureName(self.handle) - self.__dict__["endianness"] = core.BNEndianness_names[core.BNGetArchitectureEndianness(self.handle)] - self.__dict__["address_size"] = core.BNGetArchitectureAddressSize(self.handle) - self.__dict__["default_int_size"] = core.BNGetArchitectureDefaultIntegerSize(self.handle) - self.__dict__["max_instr_length"] = core.BNGetArchitectureMaxInstructionLength(self.handle) - self.__dict__["opcode_display_length"] = core.BNGetArchitectureOpcodeDisplayLength(self.handle) - self.__dict__["stack_pointer"] = core.BNGetArchitectureRegisterName(self.handle, - core.BNGetArchitectureStackPointerRegister(self.handle)) - self.__dict__["link_reg"] = core.BNGetArchitectureRegisterName(self.handle, - core.BNGetArchitectureLinkRegister(self.handle)) - - count = ctypes.c_ulonglong() - regs = core.BNGetAllArchitectureRegisters(self.handle, count) - self.__dict__["regs"] = {} - for i in xrange(0, count.value): - name = core.BNGetArchitectureRegisterName(self.handle, regs[i]) - info = core.BNGetArchitectureRegisterInfo(self.handle, regs[i]) - full_width_reg = core.BNGetArchitectureRegisterName(self.handle, info.fullWidthRegister) - self.regs[name] = RegisterInfo(full_width_reg, info.size, info.offset, - core.BNImplicitRegisterExtend_names[info.extend], regs[i]) - core.BNFreeRegisterList(regs) - - count = ctypes.c_ulonglong() - flags = core.BNGetAllArchitectureFlags(self.handle, count) - self._flags = {} - self._flags_by_index = {} - self.__dict__["flags"] = [] - for i in xrange(0, count.value): - name = core.BNGetArchitectureFlagName(self.handle, flags[i]) - self._flags[name] = flags[i] - self._flags_by_index[flags[i]] = name - self.flags.append(name) - core.BNFreeRegisterList(flags) - - count = ctypes.c_ulonglong() - types = core.BNGetAllArchitectureFlagWriteTypes(self.handle, count) - self._flag_write_types = {} - self._flag_write_types_by_index = {} - self.__dict__["flag_write_types"] = [] - for i in xrange(0, count.value): - name = core.BNGetArchitectureFlagWriteTypeName(self.handle, types[i]) - self._flag_write_types[name] = types[i] - self._flag_write_types_by_index[types[i]] = name - self.flag_write_types.append(name) - core.BNFreeRegisterList(types) - - self._flag_roles = {} - self.__dict__["flag_roles"] = {} - for flag in self.__dict__["flags"]: - role = core.BNGetArchitectureFlagRole(self.handle, self._flags[flag]) - self.__dict__["flag_roles"][flag] = role - self._flag_roles[self._flags[flag]] = role - - self._flags_required_for_flag_condition = {} - self.__dict__["flags_required_for_flag_condition"] = {} - for cond in core.BNLowLevelILFlagCondition_names: - count = ctypes.c_ulonglong() - flags = core.BNGetArchitectureFlagsRequiredForFlagCondition(self.handle, cond, count) - flag_indexes = [] - flag_names = [] - for i in xrange(0, count.value): - flag_indexes.append(flags[i]) - flag_names.append(self._flags_by_index[flags[i]]) - core.BNFreeRegisterList(flags) - self._flags_required_for_flag_condition[cond] = flag_indexes - self.__dict__["flags_required_for_flag_condition"][cond] = flag_names - - self._flags_written_by_flag_write_type = {} - self.__dict__["flags_written_by_flag_write_type"] = {} - for write_type in self.flag_write_types: - count = ctypes.c_ulonglong() - flags = core.BNGetArchitectureFlagsWrittenByFlagWriteType(self.handle, - self._flag_write_types[write_type], count) - flag_indexes = [] - flag_names = [] - for i in xrange(0, count.value): - flag_indexes.append(flags[i]) - flag_names.append(self._flags_by_index[flags[i]]) - core.BNFreeRegisterList(flags) - self._flags_written_by_flag_write_type[self._flag_write_types[write_type]] = flag_indexes - self.__dict__["flags_written_by_flag_write_type"][write_type] = flag_names - else: - _init_plugins() - - if self.__class__.opcode_display_length > self.__class__.max_instr_length: - self.__class__.opcode_display_length = self.__class__.max_instr_length - - self._cb = core.BNCustomArchitecture() - self._cb.context = 0 - self._cb.init = self._cb.init.__class__(self._init) - self._cb.getEndianness = self._cb.getEndianness.__class__(self._get_endianness) - self._cb.getAddressSize = self._cb.getAddressSize.__class__(self._get_address_size) - self._cb.getDefaultIntegerSize = self._cb.getDefaultIntegerSize.__class__(self._get_default_integer_size) - self._cb.getMaxInstructionLength = self._cb.getMaxInstructionLength.__class__(self._get_max_instruction_length) - self._cb.getOpcodeDisplayLength = self._cb.getOpcodeDisplayLength.__class__(self._get_opcode_display_length) - self._cb.getAssociatedArchitectureByAddress = self._cb.getAssociatedArchitectureByAddress.__class__( - self._get_associated_arch_by_address) - self._cb.getInstructionInfo = self._cb.getInstructionInfo.__class__(self._get_instruction_info) - self._cb.getInstructionText = self._cb.getInstructionText.__class__(self._get_instruction_text) - self._cb.freeInstructionText = self._cb.freeInstructionText.__class__(self._free_instruction_text) - self._cb.getInstructionLowLevelIL = self._cb.getInstructionLowLevelIL.__class__( - self._get_instruction_low_level_il) - self._cb.getRegisterName = self._cb.getRegisterName.__class__(self._get_register_name) - self._cb.getFlagName = self._cb.getFlagName.__class__(self._get_flag_name) - self._cb.getFlagWriteTypeName = self._cb.getFlagWriteTypeName.__class__(self._get_flag_write_type_name) - self._cb.getFullWidthRegisters = self._cb.getFullWidthRegisters.__class__(self._get_full_width_registers) - self._cb.getAllRegisters = self._cb.getAllRegisters.__class__(self._get_all_registers) - self._cb.getAllFlags = self._cb.getAllRegisters.__class__(self._get_all_flags) - self._cb.getAllFlagWriteTypes = self._cb.getAllRegisters.__class__(self._get_all_flag_write_types) - self._cb.getFlagRole = self._cb.getFlagRole.__class__(self._get_flag_role) - self._cb.getFlagsRequiredForFlagCondition = self._cb.getFlagsRequiredForFlagCondition.__class__( - self._get_flags_required_for_flag_condition) - self._cb.getFlagsWrittenByFlagWriteType = self._cb.getFlagsWrittenByFlagWriteType.__class__( - self._get_flags_written_by_flag_write_type) - self._cb.getFlagWriteLowLevelIL = self._cb.getFlagWriteLowLevelIL.__class__( - self._get_flag_write_low_level_il) - self._cb.getFlagConditionLowLevelIL = self._cb.getFlagConditionLowLevelIL.__class__( - self._get_flag_condition_low_level_il) - self._cb.freeRegisterList = self._cb.freeRegisterList.__class__(self._free_register_list) - self._cb.getRegisterInfo = self._cb.getRegisterInfo.__class__(self._get_register_info) - self._cb.getStackPointerRegister = self._cb.getStackPointerRegister.__class__( - self._get_stack_pointer_register) - self._cb.getLinkRegister = self._cb.getLinkRegister.__class__(self._get_link_register) - self._cb.assemble = self._cb.assemble.__class__(self._assemble) - self._cb.isNeverBranchPatchAvailable = self._cb.isNeverBranchPatchAvailable.__class__( - self._is_never_branch_patch_available) - self._cb.isAlwaysBranchPatchAvailable = self._cb.isAlwaysBranchPatchAvailable.__class__( - self._is_always_branch_patch_available) - self._cb.isInvertBranchPatchAvailable = self._cb.isInvertBranchPatchAvailable.__class__( - self._is_invert_branch_patch_available) - self._cb.isSkipAndReturnZeroPatchAvailable = self._cb.isSkipAndReturnZeroPatchAvailable.__class__( - self._is_skip_and_return_zero_patch_available) - self._cb.isSkipAndReturnValuePatchAvailable = self._cb.isSkipAndReturnValuePatchAvailable.__class__( - self._is_skip_and_return_value_patch_available) - self._cb.convertToNop = self._cb.convertToNop.__class__(self._convert_to_nop) - self._cb.alwaysBranch = self._cb.alwaysBranch.__class__(self._always_branch) - self._cb.invertBranch = self._cb.invertBranch.__class__(self._invert_branch) - self._cb.skipAndReturnValue = self._cb.skipAndReturnValue.__class__(self._skip_and_return_value) - - self._all_regs = {} - self._full_width_regs = {} - self._regs_by_index = {} - self.__dict__["regs"] = self.__class__.regs - reg_index = 0 - for reg in self.regs: - info = self.regs[reg] - if reg not in self._all_regs: - self._all_regs[reg] = reg_index - self._regs_by_index[reg_index] = reg - self.regs[reg].index = reg_index - reg_index += 1 - if info.full_width_reg not in self._all_regs: - self._all_regs[info.full_width_reg] = reg_index - self._regs_by_index[reg_index] = info.full_width_reg - self.regs[info.full_width_reg].index = reg_index - reg_index += 1 - if info.full_width_reg not in self._full_width_regs: - self._full_width_regs[info.full_width_reg] = self._all_regs[info.full_width_reg] - - self._flags = {} - self._flags_by_index = {} - self.__dict__["flags"] = self.__class__.flags - flag_index = 0 - for flag in self.__class__.flags: - if flag not in self._flags: - self._flags[flag] = flag_index - self._flags_by_index[flag_index] = flag - flag_index += 1 - - self._flag_write_types = {} - self._flag_write_types_by_index = {} - self.__dict__["flag_write_types"] = self.__class__.flag_write_types - write_type_index = 0 - for write_type in self.__class__.flag_write_types: - if write_type not in self._flag_write_types: - self._flag_write_types[write_type] = write_type_index - self._flag_write_types_by_index[write_type_index] = write_type - write_type_index += 1 - - self._flag_roles = {} - self.__dict__["flag_roles"] = self.__class__.flag_roles - for flag in self.__class__.flag_roles: - role = self.__class__.flag_roles[flag] - if isinstance(role, str): - role = core.BNFlagRole_by_name[role] - self._flag_roles[self._flags[flag]] = role - - self._flags_required_for_flag_condition = {} - self.__dict__["flags_required_for_flag_condition"] = self.__class__.flags_required_for_flag_condition - for cond in self.__class__.flags_required_for_flag_condition: - flags = [] - for flag in self.__class__.flags_required_for_flag_condition[cond]: - flags.append(self._flags[flag]) - self._flags_required_for_flag_condition[cond] = flags - - self._flags_written_by_flag_write_type = {} - self.__dict__["flags_written_by_flag_write_type"] = self.__class__.flags_written_by_flag_write_type - for write_type in self.__class__.flags_written_by_flag_write_type: - flags = [] - for flag in self.__class__.flags_written_by_flag_write_type[write_type]: - flags.append(self._flags[flag]) - self._flags_written_by_flag_write_type[self._flag_write_types[write_type]] = flags - - self._pending_reg_lists = {} - self._pending_token_lists = {} - - @property - def full_width_regs(self): - """List of full width register strings (read-only)""" - count = ctypes.c_ulonglong() - regs = core.BNGetFullWidthArchitectureRegisters(self.handle, count) - result = [] - for i in xrange(0, count.value): - result.append(core.BNGetArchitectureRegisterName(self.handle, regs[i])) - core.BNFreeRegisterList(regs) - return result - - @property - def calling_conventions(self): - """Dict of CallingConvention objects (read-only)""" - count = ctypes.c_ulonglong() - cc = core.BNGetArchitectureCallingConventions(self.handle, count) - result = {} - for i in xrange(0, count.value): - obj = CallingConvention(None, core.BNNewCallingConventionReference(cc[i])) - result[obj.name] = obj - core.BNFreeCallingConventionList(cc, count) - return result - - @property - def standalone_platform(self): - """Architecture standalone platform (read-only)""" - pl = core.BNGetArchitectureStandalonePlatform(self.handle) - return Platform(self, pl) - - def __setattr__(self, name, value): - if ((name == "name") or (name == "endianness") or (name == "address_size") or - (name == "default_int_size") or (name == "regs") or (name == "get_max_instruction_length")): - raise AttributeError, "attribute '%s' is read only" % name - else: - try: - object.__setattr__(self,name,value) - except AttributeError: - raise AttributeError, "attribute '%s' is read only" % name - - def __repr__(self): - return "<arch: %s>" % self.name - - def _init(self, ctxt, handle): - self.handle = handle - - def _get_endianness(self, ctxt): - try: - return self.__class__.endianness - except: - log_error(traceback.format_exc()) - return core.LittleEndian - - def _get_address_size(self, ctxt): - try: - return self.__class__.address_size - except: - log_error(traceback.format_exc()) - return 8 - - def _get_default_integer_size(self, ctxt): - try: - return self.__class__.default_int_size - except: - log_error(traceback.format_exc()) - return 4 - - def _get_max_instruction_length(self, ctxt): - try: - return self.__class__.max_instr_length - except: - log_error(traceback.format_exc()) - return 16 - - def _get_opcode_display_length(self, ctxt): - try: - return self.__class__.opcode_display_length - except: - log_error(traceback.format_exc()) - return 8 - - def _get_associated_arch_by_address(self, ctxt, addr): - try: - result, new_addr = self.perform_get_associated_arch_by_address(addr[0]) - addr[0] = new_addr - return ctypes.cast(result.handle, ctypes.c_void_p).value - except: - log_error(traceback.format_exc()) - return ctypes.cast(self.handle, ctypes.c_void_p).value - - def _get_instruction_info(self, ctxt, data, addr, max_len, result): - try: - buf = ctypes.create_string_buffer(max_len) - ctypes.memmove(buf, data, max_len) - info = self.perform_get_instruction_info(buf.raw, addr) - if info is None: - return False - result[0].length = info.length - result[0].branchDelay = info.branch_delay - result[0].branchCount = len(info.branches) - for i in xrange(0, len(info.branches)): - if isinstance(info.branches[i].type, str): - result[0].branchType[i] = core.BNBranchType_by_name[info.branches[i].type] - else: - result[0].branchType[i] = info.branches[i].type - result[0].branchTarget[i] = info.branches[i].target - if info.branches[i].arch is None: - result[0].branchArch[i] = None - else: - result[0].branchArch[i] = info.branches[i].arch.handle - return True - except (KeyError, OSError): - log_error(traceback.format_exc()) - return False - - def _get_instruction_text(self, ctxt, data, addr, length, result, count): - try: - buf = ctypes.create_string_buffer(length[0]) - ctypes.memmove(buf, data, length[0]) - info = self.perform_get_instruction_text(buf.raw, addr) - if info is None: - return False - tokens = info[0] - length[0] = info[1] - count[0] = len(tokens) - token_buf = (core.BNInstructionTextToken * len(tokens))() - for i in xrange(0, len(tokens)): - if isinstance(tokens[i].type, str): - token_buf[i].type = core.BNInstructionTextTokenType_by_name[tokens[i].type] - else: - token_buf[i].type = tokens[i].type - token_buf[i].text = tokens[i].text - token_buf[i].value = tokens[i].value - token_buf[i].size = tokens[i].size - token_buf[i].operand = tokens[i].operand - result[0] = token_buf - ptr = ctypes.cast(token_buf, ctypes.c_void_p) - self._pending_token_lists[ptr.value] = (ptr.value, token_buf) - return True - except (KeyError, OSError): - log_error(traceback.format_exc()) - return False - - def _free_instruction_text(self, tokens, count): - try: - buf = ctypes.cast(tokens, ctypes.c_void_p) - if buf.value not in self._pending_token_lists: - raise ValueError, "freeing token list that wasn't allocated" - del self._pending_token_lists[buf.value] - except KeyError: - log_error(traceback.format_exc()) - - def _get_instruction_low_level_il(self, ctxt, data, addr, length, il): - try: - buf = ctypes.create_string_buffer(length[0]) - ctypes.memmove(buf, data, length[0]) - result = self.perform_get_instruction_low_level_il(buf.raw, addr, - LowLevelILFunction(self, core.BNNewLowLevelILFunctionReference(il))) - if result is None: - return False - length[0] = result - return True - except OSError: - log_error(traceback.format_exc()) - return False - - def _get_register_name(self, ctxt, reg): - try: - if reg in self._regs_by_index: - return core.BNAllocString(self._regs_by_index[reg]) - return core.BNAllocString("") - except (KeyError, OSError): - log_error(traceback.format_exc()) - return core.BNAllocString("") - - def _get_flag_name(self, ctxt, flag): - try: - if flag in self._flags_by_index: - return core.BNAllocString(self._flags_by_index[flag]) - return core.BNAllocString("") - except (KeyError, OSError): - log_error(traceback.format_exc()) - return core.BNAllocString("") - - def _get_flag_write_type_name(self, ctxt, write_type): - try: - if write_type in self._flag_write_types_by_index: - return core.BNAllocString(self._flag_write_types_by_index[write_type]) - return core.BNAllocString("") - except (KeyError, OSError): - log_error(traceback.format_exc()) - return core.BNAllocString("") - - def _get_full_width_registers(self, ctxt, count): - try: - regs = self._full_width_regs.values() - count[0] = len(regs) - reg_buf = (ctypes.c_uint * len(regs))() - for i in xrange(0, len(regs)): - reg_buf[i] = regs[i] - result = ctypes.cast(reg_buf, ctypes.c_void_p) - self._pending_reg_lists[result.value] = (result, reg_buf) - return result.value - except KeyError: - log_error(traceback.format_exc()) - count[0] = 0 - return None - - def _get_all_registers(self, ctxt, count): - try: - regs = self._regs_by_index.keys() - count[0] = len(regs) - reg_buf = (ctypes.c_uint * len(regs))() - for i in xrange(0, len(regs)): - reg_buf[i] = regs[i] - result = ctypes.cast(reg_buf, ctypes.c_void_p) - self._pending_reg_lists[result.value] = (result, reg_buf) - return result.value - except KeyError: - log_error(traceback.format_exc()) - count[0] = 0 - return None - - def _get_all_flags(self, ctxt, count): - try: - flags = self._flags_by_index.keys() - count[0] = len(flags) - flag_buf = (ctypes.c_uint * len(flags))() - for i in xrange(0, len(flags)): - flag_buf[i] = flags[i] - result = ctypes.cast(flag_buf, ctypes.c_void_p) - self._pending_reg_lists[result.value] = (result, flag_buf) - return result.value - except KeyError: - log_error(traceback.format_exc()) - count[0] = 0 - return None - - def _get_all_flag_write_types(self, ctxt, count): - try: - types = self._flag_write_types_by_index.keys() - count[0] = len(types) - type_buf = (ctypes.c_uint * len(types))() - for i in xrange(0, len(types)): - type_buf[i] = types[i] - result = ctypes.cast(type_buf, ctypes.c_void_p) - self._pending_reg_lists[result.value] = (result, type_buf) - return result.value - except KeyError: - log_error(traceback.format_exc()) - count[0] = 0 - return None - - def _get_flag_role(self, ctxt, flag): - try: - if flag in self._flag_roles: - return self._flag_roles[flag] - return core.SpecialFlagRole - except KeyError: - log_error(traceback.format_exc()) - return None - - def _get_flags_required_for_flag_condition(self, ctxt, cond, count): - try: - if cond in self._flags_required_for_flag_condition: - flags = self._flags_required_for_flag_condition[cond] - else: - flags = [] - count[0] = len(flags) - flag_buf = (ctypes.c_uint * len(flags))() - for i in xrange(0, len(flags)): - flag_buf[i] = flags[i] - result = ctypes.cast(flag_buf, ctypes.c_void_p) - self._pending_reg_lists[result.value] = (result, flag_buf) - return result.value - except KeyError: - log_error(traceback.format_exc()) - count[0] = 0 - return None - - def _get_flags_written_by_flag_write_type(self, ctxt, write_type, count): - try: - if write_type in self._flags_written_by_flag_write_type: - flags = self._flags_written_by_flag_write_type[write_type] - else: - flags = [] - count[0] = len(flags) - flag_buf = (ctypes.c_uint * len(flags))() - for i in xrange(0, len(flags)): - flag_buf[i] = flags[i] - result = ctypes.cast(flag_buf, ctypes.c_void_p) - self._pending_reg_lists[result.value] = (result, flag_buf) - return result.value - except (KeyError, OSError): - log_error(traceback.format_exc()) - count[0] = 0 - return None - - def _get_flag_write_low_level_il(self, ctxt, op, size, write_type, flag, operands, operand_count, il): - try: - write_type_name = None - if write_type != 0: - write_type_name = self._flag_write_types_by_index[write_type] - flag_name = self._flags_by_index[flag] - operand_list = [] - for i in xrange(operand_count): - if operands[i].constant: - operand_list.append(("const", operands[i].value)) - elif LLIL_REG_IS_TEMP(operands[i].reg): - operand_list.append(("reg", operands[i].reg)) - else: - operand_list.append(("reg", self._regs_by_index[operands[i].reg])) - return self.perform_get_flag_write_low_level_il(op, size, write_type_name, flag_name, operand_list, - LowLevelILFunction(self, core.BNNewLowLevelILFunctionReference(il))).index - except (KeyError, OSError): - log_error(traceback.format_exc()) - return False - - def _get_flag_condition_low_level_il(self, ctxt, cond, il): - try: - return self.perform_get_flag_condition_low_level_il(cond, - LowLevelILFunction(self, core.BNNewLowLevelILFunctionReference(il))).index - except OSError: - log_error(traceback.format_exc()) - return 0 - - def _free_register_list(self, ctxt, regs): - try: - buf = ctypes.cast(regs, ctypes.c_void_p) - if buf.value not in self._pending_reg_lists: - raise ValueError, "freeing register list that wasn't allocated" - del self._pending_reg_lists[buf.value] - except (ValueError, KeyError): - log_error(traceback.format_exc()) - - def _get_register_info(self, ctxt, reg, result): - try: - if reg not in self._regs_by_index: - result[0].fullWidthRegister = 0 - result[0].offset = 0 - result[0].size = 0 - result[0].extend = core.NoExtend - return - info = self.__class__.regs[self._regs_by_index[reg]] - result[0].fullWidthRegister = self._all_regs[info.full_width_reg] - result[0].offset = info.offset - result[0].size = info.size - if isinstance(info.extend, str): - result[0].extend = core.BNImplicitRegisterExtend_by_name[info.extend] - else: - result[0].extend = info.extend - except KeyError: - log_error(traceback.format_exc()) - result[0].fullWidthRegister = 0 - result[0].offset = 0 - result[0].size = 0 - result[0].extend = core.NoExtend - - def _get_stack_pointer_register(self, ctxt): - try: - return self._all_regs[self.__class__.stack_pointer] - except KeyError: - log_error(traceback.format_exc()) - return 0 - - def _get_link_register(self, ctxt): - try: - if self.__class__.link_reg is None: - return 0xffffffff - return self._all_regs[self.__class__.link_reg] - except KeyError: - log_error(traceback.format_exc()) - return 0 - - def _assemble(self, ctxt, code, addr, result, errors): - try: - data, error_str = self.perform_assemble(code, addr) - errors[0] = core.BNAllocString(str(error_str)) - if data is None: - return False - data = str(data) - buf = ctypes.create_string_buffer(len(data)) - ctypes.memmove(buf, data, len(data)) - core.BNSetDataBufferContents(result, buf, len(data)) - return True - except: - log_error(traceback.format_exc()) - errors[0] = core.BNAllocString("Unhandled exception during assembly.\n") - return False - - def _is_never_branch_patch_available(self, ctxt, data, addr, length): - try: - buf = ctypes.create_string_buffer(length) - ctypes.memmove(buf, data, length) - return self.perform_is_never_branch_patch_available(buf.raw, addr) - except: - log_error(traceback.format_exc()) - return False - - def _is_always_branch_patch_available(self, ctxt, data, addr, length): - try: - buf = ctypes.create_string_buffer(length) - ctypes.memmove(buf, data, length) - return self.perform_is_always_branch_patch_available(buf.raw, addr) - except: - log_error(traceback.format_exc()) - return False - - def _is_invert_branch_patch_available(self, ctxt, data, addr, length): - try: - buf = ctypes.create_string_buffer(length) - ctypes.memmove(buf, data, length) - return self.perform_is_invert_branch_patch_available(buf.raw, addr) - except: - log_error(traceback.format_exc()) - return False - - def _is_skip_and_return_zero_patch_available(self, ctxt, data, addr, length): - try: - buf = ctypes.create_string_buffer(length) - ctypes.memmove(buf, data, length) - return self.perform_is_skip_and_return_zero_patch_available(buf.raw, addr) - except: - log_error(traceback.format_exc()) - return False - - def _is_skip_and_return_value_patch_available(self, ctxt, data, addr, length): - try: - buf = ctypes.create_string_buffer(length) - ctypes.memmove(buf, data, length) - return self.perform_is_skip_and_return_value_patch_available(buf.raw, addr) - except: - log_error(traceback.format_exc()) - return False - - def _convert_to_nop(self, ctxt, data, addr, length): - try: - buf = ctypes.create_string_buffer(length) - ctypes.memmove(buf, data, length) - result = self.perform_convert_to_nop(buf.raw, addr) - if result is None: - return False - result = str(result) - if len(result) > length: - result = result[0:length] - ctypes.memmove(data, result, len(result)) - return True - except: - log_error(traceback.format_exc()) - return False - - def _always_branch(self, ctxt, data, addr, length): - try: - buf = ctypes.create_string_buffer(length) - ctypes.memmove(buf, data, length) - result = self.perform_always_branch(buf.raw, addr) - if result is None: - return False - result = str(result) - if len(result) > length: - result = result[0:length] - ctypes.memmove(data, result, len(result)) - return True - except: - log_error(traceback.format_exc()) - return False - - def _invert_branch(self, ctxt, data, addr, length): - try: - buf = ctypes.create_string_buffer(length) - ctypes.memmove(buf, data, length) - result = self.perform_invert_branch(buf.raw, addr) - if result is None: - return False - result = str(result) - if len(result) > length: - result = result[0:length] - ctypes.memmove(data, result, len(result)) - return True - except: - log_error(traceback.format_exc()) - return False - - def _skip_and_return_value(self, ctxt, data, addr, length, value): - try: - buf = ctypes.create_string_buffer(length) - ctypes.memmove(buf, data, length) - result = self.perform_skip_and_return_value(buf.raw, addr, value) - if result is None: - return False - result = str(result) - if len(result) > length: - result = result[0:length] - ctypes.memmove(data, result, len(result)) - return True - except: - log_error(traceback.format_exc()) - return False - - def perform_get_associated_arch_by_address(self, addr): - return self, addr - - @abc.abstractmethod - def perform_get_instruction_info(self, data, addr): - """ - ``perform_get_instruction_info`` implements a method which interpretes the bytes passed in ``data`` as an - :py:Class:`InstructionInfo` object. The InstructionInfo object should have the length of the current instruction. - If the instruction is a branch instruction the method should add a branch of the proper type: - - ===================== =================================================== - BranchType Description - ===================== =================================================== - UnconditionalBranch Branch will always be taken - FalseBranch False branch condition - TrueBranch True branch condition - CallDestination Branch is a call instruction (Branch with Link) - FunctionReturn Branch returns from a function - SystemCall System call instruction - IndirectBranch Branch destination is a memory address or register - UnresolvedBranch Call instruction that isn't - ===================== =================================================== - - :param str data: bytes to decode - :param int addr: virtual address of the byte to be decoded - :return: a :py:class:`InstructionInfo` object containing the length and branche types for the given instruction - :rtype: InstructionInfo - """ - raise NotImplementedError - - @abc.abstractmethod - def perform_get_instruction_text(self, data, addr): - """ - ``perform_get_instruction_text`` implements a method which interpretes the bytes passed in ``data`` as a - list of :py:class:`InstructionTextToken` objects. - - :param str data: bytes to decode - :param int addr: virtual address of the byte to be decoded - :return: a tuple of list(InstructionTextToken) and length of instruction decoded - :rtype: tuple(list(InstructionTextToken), int) - """ - raise NotImplementedError - - @abc.abstractmethod - def perform_get_instruction_low_level_il(self, data, addr, il): - """ - ``perform_get_instruction_low_level_il`` implements a method to interpret the bytes passed in ``data`` to - low-level IL instructions. The il instructions must be appended to the :py:class:`LowLevelILFunction`. - - .. note:: Architecture subclasses should implement this method. - - :param str data: bytes to be interpreted as low-level IL instructions - :param int addr: virtual address of start of ``data`` - :param LowLevelILFunction il: LowLevelILFunction object to append LowLevelILExpr objects to - :rtype: None - """ - raise NotImplementedError - - @abc.abstractmethod - def perform_get_flag_write_low_level_il(self, op, size, write_type, flag, operands, il): - """ - .. note:: Architecture subclasses should implement this method. - .. warning:: This method should never be called directly. - - :param LowLevelILOperation op: - :param int size: - :param int write_type: - :param int flag: - :param list(int_or_str): - :param LowLevelILFunction il: - :rtype: LowLevelILExpr - """ - return il.unimplemented() - - @abc.abstractmethod - def perform_get_flag_condition_low_level_il(self, cond, il): - """ - .. note:: Architecture subclasses should implement this method. - .. warning:: This method should never be called directly. - - :param LowLevelILFlagCondition cond: - :param LowLevelILFunction il: - :rtype: LowLevelILExpr - """ - return il.unimplemented() - - @abc.abstractmethod - def perform_assemble(self, code, addr): - """ - ``perform_assemble`` implements a method to convert the string of assembly instructions ``code`` loaded at - virtual address ``addr`` to the byte representation of those instructions. This can be done by simply shelling - out to an assembler like yasm or llvm-mc, since this method isn't performance sensitive. - - .. note:: Architecture subclasses should implement this method. - .. note :: It is important that the assembler used accepts a syntax identical to the one emitted by the \ - disassembler. This will prevent confusing the user. - .. warning:: This method should never be called directly. - - :param str code: string representation of the instructions to be assembled - :param int addr: virtual address that the instructions will be loaded at - :return: the bytes for the assembled instructions or error string - :rtype: (a tuple of instructions and empty string) or (or None and error string) - """ - return None, "Architecture does not implement an assembler.\n" - - @abc.abstractmethod - def perform_is_never_branch_patch_available(self, data, addr): - """ - ``perform_is_never_branch_patch_available`` implements a check to determine if the instruction represented by - the bytes contained in ``data`` at address addr is a branch instruction that can be made to never branch. - - .. note:: Architecture subclasses should implement this method. - .. warning:: This method should never be called directly. - - :param str data: bytes to be checked - :param int addr: the virtual address of the instruction to be patched - :return: True if the instruction can be patched, False otherwise - :rtype: bool - """ - return False - - @abc.abstractmethod - def perform_is_always_branch_patch_available(self, data, addr): - """ - ``perform_is_always_branch_patch_available`` implements a check to determine if the instruction represented by - the bytes contained in ``data`` at address addr is a conditional branch that can be made unconditional. - - .. note:: Architecture subclasses should implement this method. - .. warning:: This method should never be called directly. - - :param str data: bytes to be checked - :param int addr: the virtual address of the instruction to be patched - :return: True if the instruction can be patched, False otherwise - :rtype: bool - """ - return False - - @abc.abstractmethod - def perform_is_invert_branch_patch_available(self, data, addr): - """ - ``perform_is_invert_branch_patch_available`` implements a check to determine if the instruction represented by - the bytes contained in ``data`` at address addr is a conditional branch which can be inverted. - - .. note:: Architecture subclasses should implement this method. - .. warning:: This method should never be called directly. - - :param int addr: the virtual address of the instruction to be patched - :return: True if the instruction can be patched, False otherwise - :rtype: bool - """ - return False - - @abc.abstractmethod - def perform_is_skip_and_return_zero_patch_available(self, data, addr): - """ - ``perform_is_skip_and_return_zero_patch_available`` implements a check to determine if the instruction represented by - the bytes contained in ``data`` at address addr is a *call-like* instruction which can made into instructions - that are equivilent to "return 0". For example if ``data`` was the x86 instruction ``call eax`` which could be - converted into ``xor eax,eax`` thus this function would return True. - - .. note:: Architecture subclasses should implement this method. - .. warning:: This method should never be called directly. - - :param str data: bytes to be checked - :param int addr: the virtual address of the instruction to be patched - :return: True if the instruction can be patched, False otherwise - :rtype: bool - """ - return False - - @abc.abstractmethod - def perform_is_skip_and_return_value_patch_available(self, data, addr): - """ - ``perform_is_skip_and_return_value_patch_available`` implements a check to determine if the instruction represented by - the bytes contained in ``data`` at address addr is a *call-like* instruction which can made into instructions - that are equivilent to "return 0". For example if ``data`` was the x86 instruction ``call 0xdeadbeef`` which could be - converted into ``mov eax, 42`` thus this function would return True. - - .. note:: Architecture subclasses should implement this method. - .. warning:: This method should never be called directly. - - :param str data: bytes to be checked - :param int addr: the virtual address of the instruction to be patched - :return: True if the instruction can be patched, False otherwise - :rtype: bool - """ - return False - - @abc.abstractmethod - def perform_convert_to_nop(self, data, addr): - """ - ``perform_convert_to_nop`` implements a method which returns a nop sequence of len(data) bytes long. - - .. note:: Architecture subclasses should implement this method. - .. warning:: This method should never be called directly. - - :param str data: bytes at virtual address ``addr`` - :param int addr: the virtual address of the instruction to be patched - :return: nop sequence of same length as ``data`` or None - :rtype: str or None - """ - return None - - @abc.abstractmethod - def perform_always_branch(self, data, addr): - """ - ``perform_always_branch`` implements a method which converts the branch represented by the bytes in ``data`` to - at ``addr`` to an unconditional branch. - - .. note:: Architecture subclasses should implement this method. - .. warning:: This method should never be called directly. - - :param str data: bytes to be checked - :param int addr: the virtual address of the instruction to be patched - :return: The bytes of the replacement unconditional branch instruction - :rtype: str - """ - return None - - @abc.abstractmethod - def perform_invert_branch(self, data, addr): - """ - ``perform_invert_branch`` implements a method which inverts the branch represented by the bytes in ``data`` to - at ``addr``. - - .. note:: Architecture subclasses should implement this method. - .. warning:: This method should never be called directly. - - :param str data: bytes to be checked - :param int addr: the virtual address of the instruction to be patched - :return: The bytes of the replacement unconditional branch instruction - :rtype: str - """ - return None - - @abc.abstractmethod - def perform_skip_and_return_value(self, data, addr, value): - """ - ``perform_skip_and_return_value`` implements a method which converts a *call-like* instruction represented by - the bytes in ``data`` at ``addr`` to one or more instructions that are equivilent to a function returning a - value. - - .. note:: Architecture subclasses should implement this method. - .. warning:: This method should never be called directly. - - :param str data: bytes to be checked - :param int addr: the virtual address of the instruction to be patched - :param int value: value to be returned - :return: The bytes of the replacement unconditional branch instruction - :rtype: str - """ - return None - - def get_associated_arch_by_address(self, addr): - new_addr = ctypes.c_ulonglong() - new_addr.value = addr - result = core.BNGetAssociatedArchitectureByAddress(self.handle, new_addr) - return Architecture(handle = result), new_addr.value - - def get_instruction_info(self, data, addr): - """ - ``get_instruction_info`` returns an InstructionInfo object for the instruction at the given virtual address - ``addr`` with data ``data``. - - .. note :: The instruction info object should always set the InstructionInfo.length to the instruction length, \ - and the branches of the proper types shoulde be added if the instruction is a branch. - - :param str data: max_instruction_length bytes from the binary at virtual address ``addr`` - :param int addr: virtual address of bytes in ``data`` - :return: the InstructionInfo for the current instruction - :rtype: InstructionInfo - """ - info = core.BNInstructionInfo() - data = str(data) - buf = (ctypes.c_ubyte * len(data))() - ctypes.memmove(buf, data, len(data)) - if not core.BNGetInstructionInfo(self.handle, buf, addr, len(data), info): - return None - result = InstructionInfo() - result.length = info.length - result.branch_delay = info.branchDelay - for i in xrange(0, info.branchCount): - branch_type = core.BNBranchType_names[info.branchType[i]] - target = info.branchTarget[i] - if info.branchArch[i]: - arch = Architecture(info.branchArch[i]) - else: - arch = None - result.add_branch(branch_type, target, arch) - return result - - def get_instruction_text(self, data, addr): - """ - ``get_instruction_text`` returns a list of InstructionTextToken objects for the instruction at the given virtual - address ``addr`` with data ``data``. - - :param str data: max_instruction_length bytes from the binary at virtual address ``addr`` - :param int addr: virtual address of bytes in ``data`` - :return: an InstructionTextToken list for the current instruction - :rtype: list(InstructionTextToken) - """ - data = str(data) - count = ctypes.c_ulonglong() - length = ctypes.c_ulonglong() - length.value = len(data) - buf = (ctypes.c_ubyte * len(data))() - ctypes.memmove(buf, data, len(data)) - tokens = ctypes.POINTER(core.BNInstructionTextToken)() - if not core.BNGetInstructionText(self.handle, buf, addr, length, tokens, count): - return None, 0 - result = [] - for i in xrange(0, count.value): - token_type = core.BNInstructionTextTokenType_names[tokens[i].type] - text = tokens[i].text - value = tokens[i].value - size = tokens[i].size - operand = tokens[i].operand - result.append(InstructionTextToken(token_type, text, value, size, operand)) - core.BNFreeInstructionText(tokens, count.value) - return result, length.value - - def get_instruction_low_level_il(self, data, addr, il): - """ - ``get_instruction_low_level_il`` appends LowLevelILExpr objects for the instruction at the given virtual - address ``addr`` with data ``data``. - - :param str data: max_instruction_length bytes from the binary at virtual address ``addr`` - :param int addr: virtual address of bytes in ``data`` - :param LowLevelILFunction il: The function the current instruction belongs to - :return: the length of the current instruction - :rtype: int - """ - data = str(data) - length = ctypes.c_ulonglong() - length.value = len(data) - buf = (ctypes.c_ubyte * len(data))() - ctypes.memmove(buf, data, len(data)) - core.BNGetInstructionLowLevelIL(self.handle, buf, addr, length, il.handle) - return length.value - - def get_reg_name(self, reg): - """ - ``get_reg_name`` gets a register name from a register number. - - :param int reg: register number - :return: the corresponding register string - :rtype: str - """ - return core.BNGetArchitectureRegisterName(self.handle, reg) - - def get_flag_name(self, flag): - """ - ``get_flag_name`` gets a flag name from a flag number. - - :param int reg: register number - :return: the corresponding register string - :rtype: str - """ - return core.BNGetArchitectureFlagName(self.handle, flag) - - def get_flag_write_type_name(self, write_type): - """ - ``get_flag_write_type_name`` gets the flag write type name for the given flag. - - :param int write_type: flag - :return: flag write type name - :rtype: str - """ - return core.BNGetArchitectureFlagWriteTypeName(self.handle, write_type) - - def get_flag_by_name(self, flag): - """ - ``get_flag_by_name`` get flag name for flag index. - - :param int flag: flag index - :return: flag name for flag index - :rtype: str - """ - return self._flags[flag] - - def get_flag_write_type_by_name(self, write_type): - """ - ``get_flag_write_type_by_name`` gets the flag write type name for the flage write type. - - :param int write_type: flag write type - :return: flag write type - :rtype: str - """ - return self._flag_write_types[write_type] - - def get_flag_write_low_level_il(self, op, size, write_type, operands, il): - """ - :param LowLevelILOperation op: - :param int size: - :param str write_type: - :param list(str or int) operands: a list of either items that are either string register names or constant \ - integer values - :param LowLevelILFunction il: - :rtype: LowLevelILExpr - """ - operand_list = (core.BNRegisterOrConstant * len(operands))() - for i in xrange(len(operands)): - if isinstance(operands[i], str): - operand_list[i].constant = False - operand_list[i].reg = self._flags[operands[i]] - else: - operand_list[i].constant = True - operand_list[i].value = operands[i] - return LowLevelILExpr(core.BNGetArchitectureFlagWriteLowLevelIL(self.handle, op, size, - self._flag_write_types[write_type], operand_list, len(operand_list), il.handle)) - - def get_default_flag_write_low_level_il(self, op, size, write_type, operands, il): - """ - :param LowLevelILOperation op: - :param int size: - :param str write_type: - :param list(str or int) operands: a list of either items that are either string register names or constant \ - integer values - :param LowLevelILFunction il: - :rtype: LowLevelILExpr index - """ - operand_list = (core.BNRegisterOrConstant * len(operands))() - for i in xrange(len(operands)): - if isinstance(operands[i], str): - operand_list[i].constant = False - operand_list[i].reg = self._flags[operands[i]] - else: - operand_list[i].constant = True - operand_list[i].value = operands[i] - return LowLevelILExpr(core.BNGetDefaultArchitectureFlagWriteLowLevelIL(self.handle, op, size, - self._flag_write_types[write_type], operand_list, len(operand_list), il.handle)) - - def get_flag_condition_low_level_il(self, cond, il): - """ - :param LowLevelILFlagCondition cond: - :param LowLevelILFunction il: - :rtype: LowLevelILExpr - """ - return LowLevelILExpr(core.BNGetArchitectureFlagConditionLowLevelIL(self.handle, cond, il.handle)) - - def get_modified_regs_on_write(self, reg): - """ - ``get_modified_regs_on_write`` returns a list of register names that are modified when ``reg`` is written. - - :param str reg: string register name - :return: list of register names - :rtype: list(str) - """ - reg = core.BNGetArchitectureRegisterByName(self.handle, str(reg)) - count = ctypes.c_ulonglong() - regs = core.BNGetModifiedArchitectureRegistersOnWrite(self.handle, reg, count) - result = [] - for i in xrange(0, count.value): - result.append(core.BNGetArchitectureRegisterName(self.handle, regs[i])) - core.BNFreeRegisterList(regs) - return result - - def assemble(self, code, addr = 0): - """ - ``assemble`` converts the string of assembly instructions ``code`` loaded at virtual address ``addr`` to the - byte representation of those instructions. - - :param str code: string representation of the instructions to be assembled - :param int addr: virtual address that the instructions will be loaded at - :return: the bytes for the assembled instructions or error string - :rtype: (a tuple of instructions and empty string) or (or None and error string) - :Example: - - >>> arch.assemble("je 10") - ('\\x0f\\x84\\x04\\x00\\x00\\x00', '') - >>> - """ - result = DataBuffer() - errors = ctypes.c_char_p() - if not core.BNAssemble(self.handle, code, addr, result.handle, errors): - return None, errors.value - return str(result), errors.value - - def is_never_branch_patch_available(self, data, addr): - """ - ``is_never_branch_patch_available`` determines if the instruction ``data`` at ``addr`` can be made to **never branch**. - - :param str data: bytes for the instruction to be checked - :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: - - >>> arch.is_never_branch_patch_available(arch.assemble("je 10")[0], 0) - True - >>> arch.is_never_branch_patch_available(arch.assemble("nop")[0], 0) - False - >>> - """ - data = str(data) - buf = (ctypes.c_ubyte * len(data))() - ctypes.memmove(buf, data, len(data)) - return core.BNIsArchitectureNeverBranchPatchAvailable(self.handle, buf, addr, len(data)) - - def is_always_branch_patch_available(self, data, addr): - """ - ``is_always_branch_patch_available`` determines if the instruction ``data`` at ``addr`` can be made to - **always branch**. - - :param str data: bytes for the instruction to be checked - :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: - - >>> arch.is_always_branch_patch_available(arch.assemble("je 10")[0], 0) - True - >>> arch.is_always_branch_patch_available(arch.assemble("nop")[0], 0) - False - >>> - """ - data = str(data) - buf = (ctypes.c_ubyte * len(data))() - ctypes.memmove(buf, data, len(data)) - return core.BNIsArchitectureAlwaysBranchPatchAvailable(self.handle, buf, addr, len(data)) - - def is_invert_branch_patch_available(self, data, addr): - """ - ``is_always_branch_patch_available`` determines if the instruction ``data`` at ``addr`` can be inverted. - - :param str data: bytes for the instruction to be checked - :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: - - >>> arch.is_invert_branch_patch_available(arch.assemble("je 10")[0], 0) - True - >>> arch.is_invert_branch_patch_available(arch.assemble("nop")[0], 0) - False - >>> - """ - data = str(data) - buf = (ctypes.c_ubyte * len(data))() - ctypes.memmove(buf, data, len(data)) - return core.BNIsArchitectureInvertBranchPatchAvailable(self.handle, buf, addr, len(data)) - - def is_skip_and_return_zero_patch_available(self, data, addr): - """ - ``is_skip_and_return_zero_patch_available`` determines if the instruction ``data`` at ``addr`` is a *call-like* - instruction that can be made into an instruction *returns zero*. - - :param str data: bytes for the instruction to be checked - :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: - - >>> arch.is_skip_and_return_zero_patch_available(arch.assemble("call 0")[0], 0) - True - >>> arch.is_skip_and_return_zero_patch_available(arch.assemble("call eax")[0], 0) - True - >>> arch.is_skip_and_return_zero_patch_available(arch.assemble("jmp eax")[0], 0) - False - >>> - """ - data = str(data) - buf = (ctypes.c_ubyte * len(data))() - ctypes.memmove(buf, data, len(data)) - return core.BNIsArchitectureSkipAndReturnZeroPatchAvailable(self.handle, buf, addr, len(data)) - - def is_skip_and_return_value_patch_available(self, data, addr): - """ - ``is_skip_and_return_zero_patch_available`` determines if the instruction ``data`` at ``addr`` is a *call-like* - instruction that can be made into an instruction *returns a value*. - - :param str data: bytes for the instruction to be checked - :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: - - >>> arch.is_skip_and_return_zero_patch_available(arch.assemble("call 0")[0], 0) - True - >>> arch.is_skip_and_return_zero_patch_available(arch.assemble("jmp eax")[0], 0) - False - >>> - """ - data = str(data) - buf = (ctypes.c_ubyte * len(data))() - ctypes.memmove(buf, data, len(data)) - return core.BNIsArchitectureSkipAndReturnValuePatchAvailable(self.handle, buf, addr, len(data)) - - def convert_to_nop(self, data, addr): - """ - ``convert_to_nop`` reads the instruction(s) in ``data`` at virtual address ``addr`` and returns a string of nop - instructions of the same length as data. - - :param str data: bytes for the instruction to be converted - :param int addr: the virtual address of the instruction to be patched - :return: string containing len(data) worth of no-operation instructions - :rtype: str - :Example: - - >>> arch.convert_to_nop("\\x00\\x00", 0) - '\\x90\\x90' - >>> - """ - data = str(data) - buf = (ctypes.c_ubyte * len(data))() - ctypes.memmove(buf, data, len(data)) - if not core.BNArchitectureConvertToNop(self.handle, buf, addr, len(data)): - return None - result = ctypes.create_string_buffer(len(data)) - ctypes.memmove(result, buf, len(data)) - return result.raw - - def always_branch(self, data, addr): - """ - ``always_branch`` reads the instruction(s) in ``data`` at virtual address ``addr`` and returns a string of bytes - of the same length which always branches. - - :param str data: bytes for the instruction to be converted - :param int addr: the virtual address of the instruction to be patched - :return: string containing len(data) which always branches to the same location as the provided instruction - :rtype: str - :Example: - - >>> bytes = arch.always_branch(arch.assemble("je 10")[0], 0) - >>> arch.get_instruction_text(bytes, 0) - (['nop '], 1L) - >>> arch.get_instruction_text(bytes[1:], 0) - (['jmp ', '0x9'], 5L) - >>> - """ - data = str(data) - buf = (ctypes.c_ubyte * len(data))() - ctypes.memmove(buf, data, len(data)) - if not core.BNArchitectureAlwaysBranch(self.handle, buf, addr, len(data)): - return None - result = ctypes.create_string_buffer(len(data)) - ctypes.memmove(result, buf, len(data)) - return result.raw - - def invert_branch(self, data, addr): - """ - ``invert_branch`` reads the instruction(s) in ``data`` at virtual address ``addr`` and returns a string of bytes - of the same length which inverts the branch of provided instruction. - - :param str data: bytes for the instruction to be converted - :param int addr: the virtual address of the instruction to be patched - :return: string containing len(data) which always branches to the same location as the provided instruction - :rtype: str - :Example: - - >>> arch.get_instruction_text(arch.invert_branch(arch.assemble("je 10")[0], 0), 0) - (['jne ', '0xa'], 6L) - >>> arch.get_instruction_text(arch.invert_branch(arch.assemble("jo 10")[0], 0), 0) - (['jno ', '0xa'], 6L) - >>> arch.get_instruction_text(arch.invert_branch(arch.assemble("jge 10")[0], 0), 0) - (['jl ', '0xa'], 6L) - >>> - """ - data = str(data) - buf = (ctypes.c_ubyte * len(data))() - ctypes.memmove(buf, data, len(data)) - if not core.BNArchitectureInvertBranch(self.handle, buf, addr, len(data)): - return None - result = ctypes.create_string_buffer(len(data)) - ctypes.memmove(result, buf, len(data)) - return result.raw - - def skip_and_return_value(self, data, addr, value): - """ - ``skip_and_return_value`` reads the instruction(s) in ``data`` at virtual address ``addr`` and returns a string of - bytes of the same length which doesn't call and instead *return a value*. - - :param str data: bytes for the instruction to be converted - :param int addr: the virtual address of the instruction to be patched - :return: string containing len(data) which always branches to the same location as the provided instruction - :rtype: str - :Example: - - >>> arch.get_instruction_text(arch.skip_and_return_value(arch.assemble("call 10")[0], 0, 0), 0) - (['mov ', 'eax', ', ', '0x0'], 5L) - >>> - """ - data = str(data) - buf = (ctypes.c_ubyte * len(data))() - ctypes.memmove(buf, data, len(data)) - if not core.BNArchitectureSkipAndReturnValue(self.handle, buf, addr, len(data), value): - return None - result = ctypes.create_string_buffer(len(data)) - ctypes.memmove(result, buf, len(data)) - return result.raw - - def is_view_type_constant_defined(self, type_name, const_name): - """ - - :param str type_name: the BinaryView type name of the constant to query - :param str const_name: the constant name to query - :rtype: None - :Example: - - >>> arch.set_view_type_constant("ELF", "R_COPY", ELF_RELOC_COPY) - >>> arch.is_view_type_constant_defined("ELF", "R_COPY") - True - >>> arch.is_view_type_constant_defined("ELF", "NOT_THERE") - False - >>> - """ - return core.BNIsBinaryViewTypeArchitectureConstantDefined(self.handle, type_name, const_name) - - def get_view_type_constant(self, type_name, const_name, default_value = 0): - """ - ``get_view_type_constant`` retrieves the view type constant for the given type_name and const_name. - - :param str type_name: the BinaryView type name of the constant to be retrieved - :param str const_name: the constant name to retrieved - :param int value: optional default value if the type_name is not present. default value is zero. - :return: The BinaryView type constant or the default_value if not found - :rtype: int - :Example: - - >>> ELF_RELOC_COPY = 5 - >>> arch.set_view_type_constant("ELF", "R_COPY", ELF_RELOC_COPY) - >>> arch.get_view_type_constant("ELF", "R_COPY") - 5L - >>> arch.get_view_type_constant("ELF", "NOT_HERE", 100) - 100L - """ - return core.BNGetBinaryViewTypeArchitectureConstant(self.handle, type_name, const_name, default_value) - - def set_view_type_constant(self, type_name, const_name, value): - """ - ``set_view_type_constant`` creates a new binaryview type constant. - - :param str type_name: the BinaryView type name of the constant to be registered - :param str const_name: the constant name to register - :param int value: the value of the constant - :rtype: None - :Example: - - >>> ELF_RELOC_COPY = 5 - >>> arch.set_view_type_constant("ELF", "R_COPY", ELF_RELOC_COPY) - >>> - """ - core.BNSetBinaryViewTypeArchitectureConstant(self.handle, type_name, const_name, value) - - def parse_types_from_source(self, source, filename = None, include_dirs = []): - """ - ``parse_types_from_source`` parses the source string and any needed headers searching for them in - the optional list of directories provided in ``include_dirs``. - - :param str source: source string to be parsed - :param str filename: optional source filename - :param list(str) include_dirs: optional list of string filename include directories - :return: py:class:`TypeParserResult` (a SyntaxError is thrown on parse error) - :rtype: TypeParserResult - :Example: - - >>> arch.parse_types_from_source('int foo;\\nint bar(int x);\\nstruct bas{int x,y;};\\n') - ({types: {'bas': <type: struct bas>}, variables: {'foo': <type: int32_t>}, functions:{'bar': - <type: int32_t(int32_t x)>}}, '') - >>> - """ - - if filename is None: - filename = "input" - dir_buf = (ctypes.c_char_p * len(include_dirs))() - for i in xrange(0, len(include_dirs)): - dir_buf[i] = str(include_dirs[i]) - parse = core.BNTypeParserResult() - errors = ctypes.c_char_p() - result = core.BNParseTypesFromSource(self.handle, source, filename, parse, errors, dir_buf, len(include_dirs)) - error_str = errors.value - core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) - if not result: - raise SyntaxError, error_str - types = {} - variables = {} - functions = {} - for i in xrange(0, parse.typeCount): - name = QualifiedName._from_core_struct(parse.types[i].name) - types[name] = Type(core.BNNewTypeReference(parse.types[i].type)) - for i in xrange(0, parse.variableCount): - name = QualifiedName._from_core_struct(parse.variables[i].name) - variables[name] = Type(core.BNNewTypeReference(parse.variables[i].type)) - for i in xrange(0, parse.functionCount): - name = QualifiedName._from_core_struct(parse.functions[i].name) - functions[name] = Type(core.BNNewTypeReference(parse.functions[i].type)) - core.BNFreeTypeParserResult(parse) - return TypeParserResult(types, variables, functions) - - def parse_types_from_source_file(self, filename, include_dirs = []): - """ - ``parse_types_from_source_file`` parses the source file ``filename`` and any needed headers searching for them in - the optional list of directories provided in ``include_dirs``. - - :param str filename: filename of file to be parsed - :param list(str) include_dirs: optional list of string filename include directories - :return: py:class:`TypeParserResult` (a SyntaxError is thrown on parse error) - :rtype: TypeParserResult - :Example: - - >>> file = "/Users/binja/tmp.c" - >>> open(file).read() - 'int foo;\\nint bar(int x);\\nstruct bas{int x,y;};\\n' - >>> arch.parse_types_from_source_file(file) - ({types: {'bas': <type: struct bas>}, variables: {'foo': <type: int32_t>}, functions: - {'bar': <type: int32_t(int32_t x)>}}, '') - >>> - """ - dir_buf = (ctypes.c_char_p * len(include_dirs))() - for i in xrange(0, len(include_dirs)): - dir_buf[i] = str(include_dirs[i]) - parse = core.BNTypeParserResult() - errors = ctypes.c_char_p() - result = core.BNParseTypesFromSourceFile(self.handle, filename, parse, errors, dir_buf, len(include_dirs)) - error_str = errors.value - core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) - if not result: - raise SyntaxError, error_str - types = {} - variables = {} - functions = {} - for i in xrange(0, parse.typeCount): - name = QualifiedName._from_core_struct(parse.types[i].name) - types[name] = Type(core.BNNewTypeReference(parse.types[i].type)) - for i in xrange(0, parse.variableCount): - name = QualifiedName._from_core_struct(parse.variables[i].name) - variables[name] = Type(core.BNNewTypeReference(parse.variables[i].type)) - for i in xrange(0, parse.functionCount): - name = QualifiedName._from_core_struct(parse.functions[i].name) - functions[name] = Type(core.BNNewTypeReference(parse.functions[i].type)) - core.BNFreeTypeParserResult(parse) - return TypeParserResult(types, variables, functions) - - def register_calling_convention(self, cc): - """ - ``register_calling_convention`` registers a new calling convention for the Architecture. - - :param CallingConvention cc: CallingConvention object to be registered - :rtype: None - """ - core.BNRegisterCallingConvention(self.handle, cc.handle) - -class ReferenceSource(object): - def __init__(self, func, arch, addr): - self.function = func - self.arch = arch - self.address = addr - - def __repr__(self): - if self.arch: - return "<ref: %s@%#x>" % (self.arch.name, self.address) - else: - return "<ref: %#x>" % self.address - -class LowLevelILLabel(object): - def __init__(self, handle = None): - if handle is None: - self.handle = (core.BNLowLevelILLabel * 1)() - core.BNLowLevelILInitLabel(self.handle) - else: - self.handle = handle - -class LowLevelILInstruction(object): - """ - ``class LowLevelILInstruction`` Low Level Intermediate Language Instructions are infinite length tree-based - instructions. Tree-based instructions use infix notation with the left hand operand being the destination operand. - Infix notation is thus more natural to read than other notations (e.g. x86 ``mov eax, 0`` vs. LLIL ``eax = 0``). - """ - - ILOperations = { - core.LLIL_NOP: [], - core.LLIL_SET_REG: [("dest", "reg"), ("src", "expr")], - core.LLIL_SET_REG_SPLIT: [("hi", "reg"), ("lo", "reg"), ("src", "expr")], - core.LLIL_SET_FLAG: [("dest", "flag"), ("src", "expr")], - core.LLIL_LOAD: [("src", "expr")], - core.LLIL_STORE: [("dest", "expr"), ("src", "expr")], - core.LLIL_PUSH: [("src", "expr")], - core.LLIL_POP: [], - core.LLIL_REG: [("src", "reg")], - core.LLIL_CONST: [("value", "int")], - core.LLIL_FLAG: [("src", "flag")], - core.LLIL_FLAG_BIT: [("src", "flag"), ("bit", "int")], - core.LLIL_ADD: [("left", "expr"), ("right", "expr")], - core.LLIL_ADC: [("left", "expr"), ("right", "expr")], - core.LLIL_SUB: [("left", "expr"), ("right", "expr")], - core.LLIL_SBB: [("left", "expr"), ("right", "expr")], - core.LLIL_AND: [("left", "expr"), ("right", "expr")], - core.LLIL_OR: [("left", "expr"), ("right", "expr")], - core.LLIL_XOR: [("left", "expr"), ("right", "expr")], - core.LLIL_LSL: [("left", "expr"), ("right", "expr")], - core.LLIL_LSR: [("left", "expr"), ("right", "expr")], - core.LLIL_ASR: [("left", "expr"), ("right", "expr")], - core.LLIL_ROL: [("left", "expr"), ("right", "expr")], - core.LLIL_RLC: [("left", "expr"), ("right", "expr")], - core.LLIL_ROR: [("left", "expr"), ("right", "expr")], - core.LLIL_RRC: [("left", "expr"), ("right", "expr")], - core.LLIL_MUL: [("left", "expr"), ("right", "expr")], - core.LLIL_MULU_DP: [("left", "expr"), ("right", "expr")], - core.LLIL_MULS_DP: [("left", "expr"), ("right", "expr")], - core.LLIL_DIVU: [("left", "expr"), ("right", "expr")], - core.LLIL_DIVU_DP: [("hi", "expr"), ("lo", "expr"), ("right", "expr")], - core.LLIL_DIVS: [("left", "expr"), ("right", "expr")], - core.LLIL_DIVS_DP: [("hi", "expr"), ("lo", "expr"), ("right", "expr")], - core.LLIL_MODU: [("left", "expr"), ("right", "expr")], - core.LLIL_MODU_DP: [("hi", "expr"), ("lo", "expr"), ("right", "expr")], - core.LLIL_MODS: [("left", "expr"), ("right", "expr")], - core.LLIL_MODS_DP: [("hi", "expr"), ("lo", "expr"), ("right", "expr")], - core.LLIL_NEG: [("src", "expr")], - core.LLIL_NOT: [("src", "expr")], - core.LLIL_SX: [("src", "expr")], - core.LLIL_ZX: [("src", "expr")], - core.LLIL_JUMP: [("dest", "expr")], - core.LLIL_JUMP_TO: [("dest", "expr"), ("targets", "int_list")], - core.LLIL_CALL: [("dest", "expr")], - core.LLIL_RET: [("dest", "expr")], - core.LLIL_NORET: [], - core.LLIL_IF: [("condition", "expr"), ("true", "int"), ("false", "int")], - core.LLIL_GOTO: [("dest", "int")], - core.LLIL_FLAG_COND: [("condition", "cond")], - core.LLIL_CMP_E: [("left", "expr"), ("right", "expr")], - core.LLIL_CMP_NE: [("left", "expr"), ("right", "expr")], - core.LLIL_CMP_SLT: [("left", "expr"), ("right", "expr")], - core.LLIL_CMP_ULT: [("left", "expr"), ("right", "expr")], - core.LLIL_CMP_SLE: [("left", "expr"), ("right", "expr")], - core.LLIL_CMP_ULE: [("left", "expr"), ("right", "expr")], - core.LLIL_CMP_SGE: [("left", "expr"), ("right", "expr")], - core.LLIL_CMP_UGE: [("left", "expr"), ("right", "expr")], - core.LLIL_CMP_SGT: [("left", "expr"), ("right", "expr")], - core.LLIL_CMP_UGT: [("left", "expr"), ("right", "expr")], - core.LLIL_TEST_BIT: [("left", "expr"), ("right", "expr")], - core.LLIL_BOOL_TO_INT: [("src", "expr")], - core.LLIL_SYSCALL: [], - core.LLIL_BP: [], - core.LLIL_TRAP: [("value", "int")], - core.LLIL_UNDEF: [], - core.LLIL_UNIMPL: [], - core.LLIL_UNIMPL_MEM: [("src", "expr")] - } - - def __init__(self, func, expr_index, instr_index = None): - instr = core.BNGetLowLevelILByIndex(func.handle, expr_index) - self.function = func - self.expr_index = expr_index - self.instr_index = instr_index - self.operation = instr.operation - self.operation_name = core.BNLowLevelILOperation_names[instr.operation] - self.size = instr.size - self.address = instr.address - self.source_operand = instr.sourceOperand - if instr.flags == 0: - self.flags = None - else: - self.flags = func.arch.get_flag_write_type_name(instr.flags) - if self.source_operand == 0xffffffff: - self.source_operand = None - operands = LowLevelILInstruction.ILOperations[instr.operation] - self.operands = [] - for i in xrange(0, len(operands)): - name, operand_type = operands[i] - if operand_type == "int": - value = instr.operands[i] - elif operand_type == "expr": - value = LowLevelILInstruction(func, instr.operands[i]) - elif operand_type == "reg": - if (instr.operands[i] & 0x80000000) != 0: - value = instr.operands[i] - else: - value = func.arch.get_reg_name(instr.operands[i]) - elif operand_type == "flag": - value = func.arch.get_flag_name(instr.operands[i]) - elif operand_type == "cond": - value = core.BNLowLevelILFlagCondition_names[instr.operands[i]] - elif operand_type == "int_list": - count = ctypes.c_ulonglong() - operands = core.BNLowLevelILGetOperandList(func.handle, self.expr_index, i, count) - value = [] - for i in xrange(count.value): - value.append(operands[i]) - core.BNLowLevelILFreeOperandList(operands) - self.operands.append(value) - self.__dict__[name] = value - - def __str__(self): - tokens = self.tokens - if tokens is None: - return "invalid" - result = "" - for token in tokens: - result += token.text - return result - - def __repr__(self): - return "<il: %s>" % str(self) - - @property - def tokens(self): - """LLIL tokens (read-only)""" - count = ctypes.c_ulonglong() - tokens = ctypes.POINTER(core.BNInstructionTextToken)() - if (self.instr_index is not None) and (self.function.source_function is not None): - if not core.BNGetLowLevelILInstructionText(self.function.handle, self.function.source_function.handle, - self.function.arch.handle, self.instr_index, tokens, count): - return None - else: - if not core.BNGetLowLevelILExprText(self.function.handle, self.function.arch.handle, - self.expr_index, tokens, count): - return None - result = [] - for i in xrange(0, count.value): - token_type = core.BNInstructionTextTokenType_names[tokens[i].type] - text = tokens[i].text - value = tokens[i].value - size = tokens[i].size - operand = tokens[i].operand - result.append(InstructionTextToken(token_type, text, value, size, operand)) - core.BNFreeInstructionText(tokens, count.value) - return result - - def __setattr__(self, name, value): - try: - object.__setattr__(self,name,value) - except AttributeError: - raise AttributeError, "attribute '%s' is read only" % name - -class LowLevelILExpr(object): - """ - ``class LowLevelILExpr`` hold the index of IL Expressions. - - .. note:: This class shouldn't be instantiated directly. Rather the helper members of LowLevelILFunction should be \ - used instead. - """ - def __init__(self, index): - self.index = index - -class LowLevelILFunction(object): - """ - ``class LowLevelILFunction`` contains the list of LowLevelILExpr objects that make up a function. LowLevelILExpr - objects can be added to the LowLevelILFunction by calling ``append`` and passing the result of the various class - methods which return LowLevelILExpr objects. - - - LowLevelILFlagCondition values used as parameters in the ``flag_condition`` method. - - ======================= ========== =============================== - LowLevelILFlagCondition Operator Description - ======================= ========== =============================== - LLFC_E == Equal - LLFC_NE != Not equal - LLFC_SLT s< Signed less than - LLFC_ULT u< Unsigned less than - LLFC_SLE s<= Signed less than or equal - LLFC_ULE u<= Unsigned less than or equal - LLFC_SGE s>= Signed greater than or equal - LLFC_UGE u>= Unsigned greater than or equal - LLFC_SGT s> Signed greather than - LLFC_UGT u> Unsigned greater than - LLFC_NEG - Negative - LLFC_POS + Positive - LLFC_O overflow Overflow - LLFC_NO !overflow No overflow - ======================= ========== =============================== - """ - def __init__(self, arch, handle = None, source_func = None): - self.arch = arch - self.source_function = source_func - if handle is not None: - self.handle = core.handle_of_type(handle, core.BNLowLevelILFunction) - else: - func_handle = None - if self.source_function is not None: - func_handle = self.source_function.handle - self.handle = core.BNCreateLowLevelILFunction(arch.handle, func_handle) - - def __del__(self): - core.BNFreeLowLevelILFunction(self.handle) - - @property - def current_address(self): - """Current IL Address (read/write)""" - return core.BNLowLevelILGetCurrentAddress(self.handle) - - @current_address.setter - def current_address(self, value): - core.BNLowLevelILSetCurrentAddress(self.handle, value) - - @property - def temp_reg_count(self): - """Number of temporary registers (read-only)""" - return core.BNGetLowLevelILTemporaryRegisterCount(self.handle) - - @property - def temp_flag_count(self): - """Number of temporary flags (read-only)""" - return core.BNGetLowLevelILTemporaryFlagCount(self.handle) - - @property - def basic_blocks(self): - """list of LowLevelILBasicBlock objects (read-only)""" - count = ctypes.c_ulonglong() - blocks = core.BNGetLowLevelILBasicBlockList(self.handle, count) - result = [] - view = None - if self.source_function is not None: - view = self.source_function.view - for i in xrange(0, count.value): - result.append(LowLevelILBasicBlock(view, core.BNNewBasicBlockReference(blocks[i]), self)) - core.BNFreeBasicBlockList(blocks, count.value) - return result - - def __setattr__(self, name, value): - try: - object.__setattr__(self,name,value) - except AttributeError: - raise AttributeError, "attribute '%s' is read only" % name - - def __len__(self): - return int(core.BNGetLowLevelILInstructionCount(self.handle)) - - def __getitem__(self, i): - if isinstance(i, slice) or isinstance(i, tuple): - raise IndexError, "expected integer instruction index" - if isinstance(i, LowLevelILExpr): - return LowLevelILInstruction(self, i.index) - if (i < 0) or (i >= len(self)): - raise IndexError, "index out of range" - return LowLevelILInstruction(self, core.BNGetLowLevelILIndexForInstruction(self.handle, i), i) - - def __setitem__(self, i, j): - raise IndexError, "instruction modification not implemented" - - def __iter__(self): - count = ctypes.c_ulonglong() - blocks = core.BNGetLowLevelILBasicBlockList(self.handle, count) - view = None - if self.source_function is not None: - view = self.source_function.view - try: - for i in xrange(0, count.value): - yield LowLevelILBasicBlock(view, core.BNNewBasicBlockReference(blocks[i]), self) - finally: - core.BNFreeBasicBlockList(blocks, count.value) - - def clear_indirect_branches(self): - core.BNLowLevelILClearIndirectBranches(self.handle) - - def set_indirect_branches(self, branches): - branch_list = (core.BNArchitectureAndAddress * len(branches))() - for i in xrange(len(branches)): - branch_list[i].arch = branches[i][0].handle - branch_list[i].address = branches[i][1] - core.BNLowLevelILSetIndirectBranches(self.handle, branch_list, len(branches)) - - def expr(self, operation, a = 0, b = 0, c = 0, d = 0, size = 0, flags = None): - if isinstance(operation, str): - operation = core.BNLowLevelILOperation_by_name[operation] - if isinstance(flags, str): - flags = self.arch.get_flag_write_type_by_name(flags) - elif flags is None: - flags = 0 - return LowLevelILExpr(core.BNLowLevelILAddExpr(self.handle, operation, size, flags, a, b, c, d)) - - def append(self, expr): - """ - ``append`` adds the LowLevelILExpr ``expr`` to the current LowLevelILFunction. - - :param LowLevelILExpr expr: the LowLevelILExpr to add to the current LowLevelILFunction - :return: number of LowLevelILExpr in the current function - :rtype: int - """ - return core.BNLowLevelILAddInstruction(self.handle, expr.index) - - def nop(self): - """ - ``nop`` no operation, this instruction does nothing - - :return: The no operation expression - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_NOP) - - def set_reg(self, size, reg, value, flags = 0): - """ - ``set_reg`` sets the register ``reg`` of size ``size`` to the expression ``value`` - - :param int size: size of the register parameter in bytes - :param str reg: the register name - :param LowLevelILExpr value: an expression to set the register to - :param str flags: which flags are set by this operation - :return: The expression ``reg = value`` - :rtype: LowLevelILExpr - """ - if isinstance(reg, str): - reg = self.arch.regs[reg].index - return self.expr(core.LLIL_SET_REG, reg, value.index, size = size, flags = flags) - - def set_reg_split(self, size, hi, lo, value, flags = 0): - """ - ``set_reg_split`` uses ``hi`` and ``lo`` as a single extended register setting ``hi:lo`` to the expression - ``value``. - - :param int size: size of the register parameter in bytes - :param str hi: the high register name - :param str lo: the low register name - :param LowLevelILExpr value: an expression to set the split regiters to - :param str flags: which flags are set by this operation - :return: The expression ``hi:lo = value`` - :rtype: LowLevelILExpr - """ - if isinstance(hi, str): - hi = self.arch.regs[hi].index - if isinstance(lo, str): - lo = self.arch.regs[lo].index - return self.expr(core.LLIL_SET_REG_SPLIT, hi, lo, value.index, size = size, flags = flags) - - def set_flag(self, flag, value): - """ - ``set_flag`` sets the flag ``flag`` to the LowLevelILExpr ``value`` - - :param str flag: the low register name - :param LowLevelILExpr value: an expression to set the flag to - :return: The expression FLAG.flag = value - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_SET_FLAG, self.arch.get_flag_by_name(flag), value.index) - - def load(self, size, addr): - """ - ``laod`` Reads ``size`` bytes from the expression ``addr`` - - :param int size: number of bytes to read - :param LowLevelILExpr addr: the expression to read memory from - :return: The expression ``[addr].size`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_LOAD, addr.index, size = size) - - def store(self, size, addr, value): - """ - ``store`` Writes ``size`` bytes to expression ``addr`` read from expression ``value`` - - :param int size: number of bytes to write - :param LowLevelILExpr addr: the expression to write to - :param LowLevelILExpr value: the expression to be written - :return: The expression ``[addr].size = value`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_STORE, addr.index, value.index, size = size) - - def push(self, size, value): - """ - ``push`` writes ``size`` bytes from expression ``value`` to the stack, adjusting the stack by ``size``. - - :param int size: number of bytes to write and adjust the stack by - :param LowLevelILExpr value: the expression to write - :return: The expression push(value) - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_PUSH, value.index, size = size) - - def pop(self, size): - """ - ``pop`` reads ``size`` bytes from the stack, adjusting the stack by ``size``. - - :param int size: number of bytes to read from the stack - :return: The expression ``pop`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_POP, size = size) - - def reg(self, size, reg): - """ - ``reg`` returns a register of size ``size`` with name ``name`` - - :param int size: the size of the register in bytes - :param str reg: the name of the register - :return: A register expression for the given string - :rtype: LowLevelILExpr - """ - if isinstance(reg, str): - reg = self.arch.regs[reg].index - return self.expr(core.LLIL_REG, reg, size = size) - - def const(self, size, value): - """ - ``const`` returns an expression for the constant integer ``value`` with size ``size`` - - :param int size: the size of the constant in bytes - :param int value: integer value of the constant - :return: A constant expression of given value and size - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_CONST, value, size = size) - - def flag(self, reg): - """ - ``flag`` returns a flag expression for the given flag name. - - :param str reg: name of the flag expression to retrieve - :return: A flag expression of given flag name - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_FLAG, self.arch.get_flag_by_name(reg)) - - def flag_bit(self, size, reg, bit): - """ - ``flag_bit`` sets the flag named ``reg`` and size ``size`` to the constant integer value ``bit`` - - :param int size: the size of the flag - :param str reg: flag value - :param int bit: integer value to set the bit to - :return: A constant expression of given value and size ``FLAG.reg = bit`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_FLAG_BIT, self.arch.get_flag_by_name(reg), bit, size = size) - - def add(self, size, a, b, flags = None): - """ - ``add`` adds expression ``a`` to expression ``b`` potentially setting flags ``flags`` and returning - an expression of ``size`` bytes. - - :param int size: the size of the result in bytes - :param LowLevelILExpr a: LHS expression - :param LowLevelILExpr b: RHS expression - :param str flags: flags to set - :return: The expression ``add.<size>{<flags>}(a, b)`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_ADD, a.index, b.index, size = size, flags = flags) - - def add_carry(self, size, a, b, flags = None): - """ - ``add_carry`` adds with carry expression ``a`` to expression ``b`` potentially setting flags ``flags`` and returning - an expression of ``size`` bytes. - - :param int size: the size of the result in bytes - :param LowLevelILExpr a: LHS expression - :param LowLevelILExpr b: RHS expression - :param str flags: flags to set - :return: The expression ``adc.<size>{<flags>}(a, b)`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_ADC, a.index, b.index, size = size, flags = flags) - - def sub(self, size, a, b, flags = None): - """ - ``sub`` subtracts expression ``b`` from expression ``a`` potentially setting flags ``flags`` and returning - an expression of ``size`` bytes. - - :param int size: the size of the result in bytes - :param LowLevelILExpr a: LHS expression - :param LowLevelILExpr b: RHS expression - :param str flags: flags to set - :return: The expression ``sub.<size>{<flags>}(a, b)`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_SUB, a.index, b.index, size = size, flags = flags) - - def sub_borrow(self, size, a, b, flags = None): - """ - ``sub_borrow`` subtracts with borrow expression ``b`` from expression ``a`` potentially setting flags ``flags`` - and returning an expression of ``size`` bytes. - - :param int size: the size of the result in bytes - :param LowLevelILExpr a: LHS expression - :param LowLevelILExpr b: RHS expression - :param str flags: flags to set - :return: The expression ``sbc.<size>{<flags>}(a, b)`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_SBB, a.index, b.index, size = size, flags = flags) - - def and_expr(self, size, a, b, flags = None): - """ - ``and_expr`` bitwise and's expression ``a`` and expression ``b`` potentially setting flags ``flags`` - and returning an expression of ``size`` bytes. - - :param int size: the size of the result in bytes - :param LowLevelILExpr a: LHS expression - :param LowLevelILExpr b: RHS expression - :param str flags: optional, flags to set - :return: The expression ``and.<size>{<flags>}(a, b)`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_AND, a.index, b.index, size = size, flags = flags) - - def or_expr(self, size, a, b, flags = None): - """ - ``or_expr`` bitwise or's expression ``a`` and expression ``b`` potentially setting flags ``flags`` - and returning an expression of ``size`` bytes. - - :param int size: the size of the result in bytes - :param LowLevelILExpr a: LHS expression - :param LowLevelILExpr b: RHS expression - :param str flags: optional, flags to set - :return: The expression ``or.<size>{<flags>}(a, b)`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_OR, a.index, b.index, size = size, flags = flags) - - def xor_expr(self, size, a, b, flags = None): - """ - ``xor_expr`` xor's expression ``a`` with expression ``b`` potentially setting flags ``flags`` - and returning an expression of ``size`` bytes. - - :param int size: the size of the result in bytes - :param LowLevelILExpr a: LHS expression - :param LowLevelILExpr b: RHS expression - :param str flags: optional, flags to set - :return: The expression ``xor.<size>{<flags>}(a, b)`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_XOR, a.index, b.index, size = size, flags = flags) - - def shift_left(self, size, a, b, flags = None): - """ - ``shift_left`` subtracts with borrow expression ``b`` from expression ``a`` potentially setting flags ``flags`` - and returning an expression of ``size`` bytes. - - :param int size: the size of the result in bytes - :param LowLevelILExpr a: LHS expression - :param LowLevelILExpr b: RHS expression - :param str flags: optional, flags to set - :return: The expression ``lsl.<size>{<flags>}(a, b)`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_LSL, a.index, b.index, size = size, flags = flags) - - def logical_shift_right(self, size, a, b, flags = None): - """ - ``logical_shift_right`` shifts logically right expression ``a`` by expression ``b`` potentially setting flags - ``flags``and returning an expression of ``size`` bytes. - - :param int size: the size of the result in bytes - :param LowLevelILExpr a: LHS expression - :param LowLevelILExpr b: RHS expression - :param str flags: optional, flags to set - :return: The expression ``lsr.<size>{<flags>}(a, b)`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_LSR, a.index, b.index, size = size, flags = flags) - - def arith_shift_right(self, size, a, b, flags = None): - """ - ``arith_shift_right`` shifts arithmatic right expression ``a`` by expression ``b`` potentially setting flags - ``flags`` and returning an expression of ``size`` bytes. - - :param int size: the size of the result in bytes - :param LowLevelILExpr a: LHS expression - :param LowLevelILExpr b: RHS expression - :param str flags: optional, flags to set - :return: The expression ``asr.<size>{<flags>}(a, b)`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_ASR, a.index, b.index, size = size, flags = flags) - - def rotate_left(self, size, a, b, flags = None): - """ - ``rotate_left`` bitwise rotates left expression ``a`` by expression ``b`` potentially setting flags ``flags`` - and returning an expression of ``size`` bytes. - - :param int size: the size of the result in bytes - :param LowLevelILExpr a: LHS expression - :param LowLevelILExpr b: RHS expression - :param str flags: optional, flags to set - :return: The expression ``rol.<size>{<flags>}(a, b)`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_ROL, a.index, b.index, size = size, flags = flags) - - def rotate_left_carry(self, size, a, b, flags = None): - """ - ``rotate_left_carry`` bitwise rotates left with carry expression ``a`` by expression ``b`` potentially setting - flags ``flags`` and returning an expression of ``size`` bytes. - - :param int size: the size of the result in bytes - :param LowLevelILExpr a: LHS expression - :param LowLevelILExpr b: RHS expression - :param str flags: optional, flags to set - :return: The expression ``rcl.<size>{<flags>}(a, b)`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_RLC, a.index, b.index, size = size, flags = flags) - - def rotate_right(self, size, a, b, flags = None): - """ - ``rotate_right`` bitwise rotates right expression ``a`` by expression ``b`` potentially setting flags ``flags`` - and returning an expression of ``size`` bytes. - - :param int size: the size of the result in bytes - :param LowLevelILExpr a: LHS expression - :param LowLevelILExpr b: RHS expression - :param str flags: optional, flags to set - :return: The expression ``ror.<size>{<flags>}(a, b)`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_ROR, a.index, b.index, size = size, flags = flags) - - def rotate_right_carry(self, size, a, b, flags = None): - """ - ``rotate_right_carry`` bitwise rotates right with carry expression ``a`` by expression ``b`` potentially setting - flags ``flags`` and returning an expression of ``size`` bytes. - - :param int size: the size of the result in bytes - :param LowLevelILExpr a: LHS expression - :param LowLevelILExpr b: RHS expression - :param str flags: optional, flags to set - :return: The expression ``rcr.<size>{<flags>}(a, b)`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_RRC, a.index, b.index, size = size, flags = flags) - - def mult(self, size, a, b, flags = None): - """ - ``mult`` multiplies expression ``a`` by expression ``b`` potentially setting flags ``flags`` and returning an - expression of ``size`` bytes. - - :param int size: the size of the result in bytes - :param LowLevelILExpr a: LHS expression - :param LowLevelILExpr b: RHS expression - :param str flags: optional, flags to set - :return: The expression ``sbc.<size>{<flags>}(a, b)`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_MUL, a.index, b.index, size = size, flags = flags) - - def mult_double_prec_signed(self, size, a, b, flags = None): - """ - ``mult_double_prec_signed`` multiplies signed with double precision expression ``a`` by expression ``b`` - potentially setting flags ``flags`` and returning an expression of ``size`` bytes. - - :param int size: the size of the result in bytes - :param LowLevelILExpr a: LHS expression - :param LowLevelILExpr b: RHS expression - :param str flags: optional, flags to set - :return: The expression ``muls.dp.<size>{<flags>}(a, b)`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_MULS_DP, a.index, b.index, size = size, flags = flags) - - def mult_double_prec_unsigned(self, size, a, b, flags = None): - """ - ``mult_double_prec_unsigned`` multiplies unsigned with double precision expression ``a`` by expression ``b`` - potentially setting flags ``flags`` and returning an expression of ``size`` bytes. - - :param int size: the size of the result in bytes - :param LowLevelILExpr a: LHS expression - :param LowLevelILExpr b: RHS expression - :param str flags: optional, flags to set - :return: The expression ``muls.dp.<size>{<flags>}(a, b)`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_MULU_DP, a.index, b.index, size = size, flags = flags) - - def div_signed(self, size, a, b, flags = None): - """ - ``div_signed`` signed divide expression ``a`` by expression ``b`` potentially setting flags ``flags`` - and returning an expression of ``size`` bytes. - - :param int size: the size of the result in bytes - :param LowLevelILExpr a: LHS expression - :param LowLevelILExpr b: RHS expression - :param str flags: optional, flags to set - :return: The expression ``divs.<size>{<flags>}(a, b)`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_DIVS, a.index, b.index, size = size, flags = flags) - - def div_double_prec_signed(self, size, hi, lo, b, flags = None): - """ - ``div_double_prec_signed`` signed double precision divide using expression ``hi`` and expression ``lo`` as a single - double precision register by expression ``b`` potentially setting flags ``flags`` and returning an expression - of ``size`` bytes. - - :param int size: the size of the result in bytes - :param LowLevelILExpr hi: high LHS expression - :param LowLevelILExpr lo: low LHS expression - :param LowLevelILExpr b: RHS expression - :param str flags: optional, flags to set - :return: The expression ``divs.dp.<size>{<flags>}(hi:lo, b)`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_DIVS_DP, hi.index, lo.index, b.index, size = size, flags = flags) - - def div_unsigned(self, size, a, b, flags = None): - """ - ``div_unsigned`` unsigned divide expression ``a`` by expression ``b`` potentially setting flags ``flags`` - and returning an expression of ``size`` bytes. - - :param int size: the size of the result in bytes - :param LowLevelILExpr a: LHS expression - :param LowLevelILExpr b: RHS expression - :param str flags: optional, flags to set - :return: The expression ``divs.<size>{<flags>}(a, b)`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_DIVS, a.index, b.index, size = size, flags = flags) - - def div_double_prec_unsigned(self, size, hi, lo, b, flags = None): - """ - ``div_double_prec_unsigned`` unsigned double precision divide using expression ``hi`` and expression ``lo`` as - a single double precision register by expression ``b`` potentially setting flags ``flags`` and returning an - expression of ``size`` bytes. - - :param int size: the size of the result in bytes - :param LowLevelILExpr hi: high LHS expression - :param LowLevelILExpr lo: low LHS expression - :param LowLevelILExpr b: RHS expression - :param str flags: optional, flags to set - :return: The expression ``divs.dp.<size>{<flags>}(hi:lo, b)`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_DIVS_DP, hi.index, lo.index, b.index, size = size, flags = flags) - - def mod_signed(self, size, a, b, flags = None): - """ - ``mod_signed`` signed modulus expression ``a`` by expression ``b`` potentially setting flags ``flags`` - and returning an expression of ``size`` bytes. - - :param int size: the size of the result in bytes - :param LowLevelILExpr a: LHS expression - :param LowLevelILExpr b: RHS expression - :param str flags: optional, flags to set - :return: The expression ``mods.<size>{<flags>}(a, b)`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_MODS, a.index, b.index, size = size, flags = flags) - - def mod_double_prec_signed(self, size, hi, lo, b, flags = None): - """ - ``mod_double_prec_signed`` signed double precision modulus using expression ``hi`` and expression ``lo`` as a single - double precision register by expression ``b`` potentially setting flags ``flags`` and returning an expression - of ``size`` bytes. - - :param int size: the size of the result in bytes - :param LowLevelILExpr hi: high LHS expression - :param LowLevelILExpr lo: low LHS expression - :param LowLevelILExpr b: RHS expression - :param str flags: optional, flags to set - :return: The expression ``mods.dp.<size>{<flags>}(hi:lo, b)`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_MODS_DP, hi.index, lo.index, b.index, size = size, flags = flags) - - def mod_unsigned(self, size, a, b, flags = None): - """ - ``mod_unsigned`` unsigned modulus expression ``a`` by expression ``b`` potentially setting flags ``flags`` - and returning an expression of ``size`` bytes. - - :param int size: the size of the result in bytes - :param LowLevelILExpr a: LHS expression - :param LowLevelILExpr b: RHS expression - :param str flags: optional, flags to set - :return: The expression ``modu.<size>{<flags>}(a, b)`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_MODS, a.index, b.index, size = size, flags = flags) - - def mod_double_prec_unsigned(self, size, hi, lo, b, flags = None): - """ - ``mod_double_prec_unsigned`` unsigned double precision modulus using expression ``hi`` and expression ``lo`` as - a single double precision register by expression ``b`` potentially setting flags ``flags`` and returning an - expression of ``size`` bytes. - - :param int size: the size of the result in bytes - :param LowLevelILExpr hi: high LHS expression - :param LowLevelILExpr lo: low LHS expression - :param LowLevelILExpr b: RHS expression - :param str flags: optional, flags to set - :return: The expression ``modu.dp.<size>{<flags>}(hi:lo, b)`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_MODS_DP, hi.index, lo.index, b.index, size = size, flags = flags) - - def neg_expr(self, size, value, flags = None): - """ - ``neg_expr`` two's complement sign negation of expression ``value`` of size ``size`` potentially setting flags - - :param int size: the size of the result in bytes - :param LowLevelILExpr value: the expression to negate - :param str flags: optional, flags to set - :return: The expression ``neg.<size>{<flags>}(value)`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_NEG, value.index, size = size, flags = flags) - - def not_expr(self, size, value, flags = None): - """ - ``not_expr`` bitwise inverse of expression ``value`` of size ``size`` potentially setting flags - - :param int size: the size of the result in bytes - :param LowLevelILExpr value: the expression to bitwise invert - :param str flags: optional, flags to set - :return: The expression ``not.<size>{<flags>}(value)`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_NOT, value.index, size = size, flags = flags) - - def sign_extend(self, size, value): - """ - ``sign_extend`` two's complement sign-extends the expression in ``value`` to ``size`` bytes - - :param int size: the size of the result in bytes - :param LowLevelILExpr value: the expression to sign extend - :return: The expression ``sx.<size>(value)`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_SX, value.index, size = size) - - def zero_extend(self, size, value): - """ - ``sign_extend`` zero-extends the expression in ``value`` to ``size`` bytes - - :param int size: the size of the result in bytes - :param LowLevelILExpr value: the expression to zero extend - :return: The expression ``sx.<size>(value)`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_ZX, value.index, size = size) - - def jump(self, dest): - """ - ``jump`` returns an expression which jumps (branches) to the expression ``dest`` - - :param LowLevelILExpr dest: the expression to jump to - :return: The expression ``jump(dest)`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_JUMP, dest.index) - - def call(self, dest): - """ - ``call`` returns an expression which first pushes the address of the next instruction onto the stack then jumps - (branches) to the expression ``dest`` - - :param LowLevelILExpr dest: the expression to call - :return: The expression ``call(dest)`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_CALL, dest.index) - - def ret(self, dest): - """ - ``ret`` returns an expression which jumps (branches) to the expression ``dest``. ``ret`` is a special alias for - jump that makes the disassembler top disassembling. - - :param LowLevelILExpr dest: the expression to jump to - :return: The expression ``jump(dest)`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_RET, dest.index) - - def no_ret(self): - """ - ``no_ret`` returns an expression halts disassembly - - :return: The expression ``noreturn`` - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_NORET) - - def flag_condition(self, cond): - """ - ``flag_condition`` returns a flag_condition expression for the given LowLevelILFlagCondition - - :param LowLevelILFlagCondition cond: Flag condition expression to retrieve - :return: A flag_condition expression - :rtype: LowLevelILExpr - """ - if isinstance(cond, str): - cond = core.BNLowLevelILFlagCondition_by_name[cond] - return self.expr(core.LLIL_FLAG_COND, cond) - - def compare_equal(self, size, a, b): - """ - ``compare_equal`` returns comparison expression of size ``size`` checking if expression ``a`` is equal to - expression ``b`` - - :param int size: size in bytes - :param LowLevelILExpr a: LHS of comparison - :param LowLevelILExpr b: RHS of comparison - :return: a comparison expression. - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_CMP_E, a.index, b.index, size = size) - - def compare_not_equal(self, size, a, b): - """ - ``compare_not_equal`` returns comparison expression of size ``size`` checking if expression ``a`` is not equal to - expression ``b`` - - :param int size: size in bytes - :param LowLevelILExpr a: LHS of comparison - :param LowLevelILExpr b: RHS of comparison - :return: a comparison expression. - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_CMP_NE, a.index, b.index, size = size) - - def compare_signed_less_than(self, size, a, b): - """ - ``compare_signed_less_than`` returns comparison expression of size ``size`` checking if expression ``a`` is - signed less than expression ``b`` - - :param int size: size in bytes - :param LowLevelILExpr a: LHS of comparison - :param LowLevelILExpr b: RHS of comparison - :return: a comparison expression. - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_CMP_SLT, a.index, b.index, size = size) - - def compare_unsigned_less_than(self, size, a, b): - """ - ``compare_unsigned_less_than`` returns comparison expression of size ``size`` checking if expression ``a`` is - unsigned less than expression ``b`` - - :param int size: size in bytes - :param LowLevelILExpr a: LHS of comparison - :param LowLevelILExpr b: RHS of comparison - :return: a comparison expression. - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_CMP_ULT, a.index, b.index, size = size) - - def compare_signed_less_equal(self, size, a, b): - """ - ``compare_signed_less_equal`` returns comparison expression of size ``size`` checking if expression ``a`` is - signed less than or equal to expression ``b`` - - :param int size: size in bytes - :param LowLevelILExpr a: LHS of comparison - :param LowLevelILExpr b: RHS of comparison - :return: a comparison expression. - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_CMP_SLE, a.index, b.index, size = size) - - def compare_unsigned_less_equal(self, size, a, b): - """ - ``compare_unsigned_less_equal`` returns comparison expression of size ``size`` checking if expression ``a`` is - unsigned less than or equal to expression ``b`` - - :param int size: size in bytes - :param LowLevelILExpr a: LHS of comparison - :param LowLevelILExpr b: RHS of comparison - :return: a comparison expression. - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_CMP_ULE, a.index, b.index, size = size) - - def compare_signed_greater_equal(self, size, a, b): - """ - ``compare_signed_greater_equal`` returns comparison expression of size ``size`` checking if expression ``a`` is - signed greater than or equal toexpression ``b`` - - :param int size: size in bytes - :param LowLevelILExpr a: LHS of comparison - :param LowLevelILExpr b: RHS of comparison - :return: a comparison expression. - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_CMP_SGE, a.index, b.index, size = size) - - def compare_unsigned_greater_equal(self, size, a, b): - """ - ``compare_unsigned_greater_equal`` returns comparison expression of size ``size`` checking if expression ``a`` - is unsigned greater than or equal to expression ``b`` - - :param int size: size in bytes - :param LowLevelILExpr a: LHS of comparison - :param LowLevelILExpr b: RHS of comparison - :return: a comparison expression. - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_CMP_UGE, a.index, b.index, size = size) - - def compare_signed_greater_than(self, size, a, b): - """ - ``compare_signed_greater_than`` returns comparison expression of size ``size`` checking if expression ``a`` is - signed greater than or equal to expression ``b`` - - :param int size: size in bytes - :param LowLevelILExpr a: LHS of comparison - :param LowLevelILExpr b: RHS of comparison - :return: a comparison expression. - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_CMP_SGT, a.index, b.index, size = size) - - def compare_unsigned_greater_than(self, size, a, b): - """ - ``compare_unsigned_greater_than`` returns comparison expression of size ``size`` checking if expression ``a`` is - unsigned greater than or equal to expression ``b`` - - :param int size: size in bytes - :param LowLevelILExpr a: LHS of comparison - :param LowLevelILExpr b: RHS of comparison - :return: a comparison expression. - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_CMP_UGT, a.index, b.index, size = size) - - def test_bit(self, size, a, b): - return self.expr(core.LLIL_TEST_BIT, a.index, b.index, size = size) - - def system_call(self): - """ - ``system_call`` return a system call expression. - - :return: a system call expression. - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_SYSCALL) - - def breakpoint(self): - """ - ``breakpoint`` returns a processor breakpoint expression. - - :return: a breakpoint expression. - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_BP) - - def trap(self, value): - """ - ``trap`` returns a processor trap (interrupt) expression of the given integer ``value``. - - :param int value: trap (interrupt) number - :return: a trap expression. - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_TRAP, value) - - def undefined(self): - """ - ``undefined`` returns the undefined expression. This should be used for instructions which perform functions but - aren't important for dataflow or partial emulation purposes. - - :return: the unimplemented expression. - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_UNDEF) - - def unimplemented(self): - """ - ``unimplemented`` returns the unimplemented expression. This should be used for all instructions which aren't - implemented. - - :return: the unimplemented expression. - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_UNIMPL) - - def unimplemented_memory_ref(self, size, addr): - """ - ``unimplemented_memory_ref`` a memory reference to expression ``addr`` of size ``size`` with unimplemented operation. - - :param int size: size in bytes of the memory reference - :param LowLevelILExpr addr: expression to reference memory - :return: the unimplemented memory reference expression. - :rtype: LowLevelILExpr - """ - return self.expr(core.LLIL_UNIMPL_MEM, addr.index, size = size) - - def goto(self, label): - """ - ``goto`` returns a goto expression which jumps to the provided LowLevelILLabel. - - :param LowLevelILLabel label: Label to jump to - :return: the LowLevelILExpr that jumps to the provided label - :rtype: LowLevelILExpr - """ - return LowLevelILExpr(core.BNLowLevelILGoto(self.handle, label.handle)) - - def if_expr(self, operand, t, f): - """ - ``if_expr`` returns the ``if`` expression which depending on condition ``operand`` jumps to the LowLevelILLabel - ``t`` when the condition expression ``operand`` is non-zero and ``f`` when it's zero. - - :param LowLevelILExpr operand: comparison expression to evaluate. - :param LowLevelILLabel t: Label for the true branch - :param LowLevelILLabel f: Label for the false branch - :return: the LowLevelILExpr for the if expression - :rtype: LowLevelILExpr - """ - return LowLevelILExpr(core.BNLowLevelILIf(self.handle, operand.index, t.handle, f.handle)) - - def mark_label(self, label): - """ - ``mark_label`` assigns a LowLevelILLabel to the current IL address. - - :param LowLevelILLabel label: - :rtype: None - """ - core.BNLowLevelILMarkLabel(self.handle, label.handle) - - def add_label_list(self, labels): - """ - ``add_label_list`` returns a label list expression for the given list of LowLevelILLabel objects. - - :param list(LowLevelILLabel) lables: the list of LowLevelILLabel to get a label list expression from - :return: the label list expression - :rtype: LowLevelILExpr - """ - label_list = (ctypes.POINTER(core.BNLowLevelILLabel) * len(labels))() - for i in xrange(len(labels)): - label_list[i] = labels[i].handle - return LowLevelILExpr(core.BNLowLevelILAddLabelList(self.handle, label_list, len(labels))) - - def add_operand_list(self, operands): - """ - ``add_operand_list`` returns an operand list expression for the given list of integer operands. - - :param list(int) operands: list of operand numbers - :return: an operand list expression - :rtype: LowLevelILExpr - """ - operand_list = (ctypes.c_ulonglong * len(operands))() - for i in xrange(len(operands)): - operand_list[i] = operands[i] - return LowLevelILExpr(core.BNLowLevelILAddOperandList(self.handle, operand_list, len(operands))) - - def operand(self, n, expr): - """ - ``operand`` sets the operand number of the expression ``expr`` and passes back ``expr`` without modification. - - :param int n: - :param LowLevelILExpr expr: - :return: returns the expression ``expr`` unmodified - :rtype: LowLevelILExpr - """ - core.BNLowLevelILSetExprSourceOperand(self.handle, expr.index, n) - return expr - - def finalize(self): - """ - ``finalize`` ends the function and computes the list of basic blocks. - - :rtype: None - """ - core.BNFinalizeLowLevelILFunction(self.handle) - - def add_label_for_address(self, arch, addr): - """ - ``add_label_for_address`` adds a low-level IL label for the given architecture ``arch`` at the given virtual - address ``addr`` - - :param Architecture arch: Architecture to add labels for - :param int addr: the IL address to add a label at - """ - if arch is not None: - arch = arch.handle - core.BNAddLowLevelILLabelForAddress(self.handle, arch, addr) - - def get_label_for_address(self, arch, addr): - """ - ``get_label_for_address`` returns the LowLevelILLabel for the given Architecture ``arch`` and IL address ``addr``. - - :param Architecture arch: - :param int addr: IL Address label to retrieve - :return: the LowLevelILLabel for the given IL address - :rtype: LowLevelILLabel - """ - if arch is not None: - arch = arch.handle - label = core.BNGetLowLevelILLabelForAddress(self.handle, arch, addr) - if label is None: - return None - return LowLevelILLabel(label) - -class TypeParserResult(object): - def __init__(self, types, variables, functions): - self.types = types - self.variables = variables - self.functions = functions - - def __repr__(self): - return "{types: %s, variables: %s, functions: %s}" % (self.types, self.variables, self.functions) - -class _TransformMetaClass(type): - @property - def list(self): - _init_plugins() - count = ctypes.c_ulonglong() - xforms = core.BNGetTransformTypeList(count) - result = [] - for i in xrange(0, count.value): - result.append(Transform(xforms[i])) - core.BNFreeTransformTypeList(xforms) - return result - - def __iter__(self): - _init_plugins() - count = ctypes.c_ulonglong() - xforms = core.BNGetTransformTypeList(count) - try: - for i in xrange(0, count.value): - yield Transform(xforms[i]) - finally: - core.BNFreeTransformTypeList(xforms) - - def __setattr__(self, name, value): - try: - type.__setattr__(self,name,value) - except AttributeError: - raise AttributeError, "attribute '%s' is read only" % name - - def __getitem__(cls, name): - _init_plugins() - xform = core.BNGetTransformByName(name) - if xform is None: - raise KeyError, "'%s' is not a valid transform" % str(name) - return Transform(xform) - - def register(cls): - _init_plugins() - if cls.name is None: - raise ValueError, "transform 'name' is not defined" - if cls.long_name is None: - cls.long_name = cls.name - if cls.transform_type is None: - raise ValueError, "transform 'transform_type' is not defined" - if cls.group is None: - cls.group = "" - xform = cls(None) - cls._registered_cb = xform._cb - xform.handle = core.BNRegisterTransformType(cls.transform_type, cls.name, cls.long_name, cls.group, xform._cb) - -class TransformParameter(object): - def __init__(self, name, long_name = None, fixed_length = 0): - self.name = name - if long_name is None: - self.long_name = name - else: - self.long_name = long_name - self.fixed_length = fixed_length - -class Transform: - transform_type = None - name = None - long_name = None - group = None - parameters = [] - _registered_cb = None - __metaclass__ = _TransformMetaClass - - def __init__(self, handle): - if handle is None: - self._cb = core.BNCustomTransform() - self._cb.context = 0 - self._cb.getParameters = self._cb.getParameters.__class__(self._get_parameters) - self._cb.freeParameters = self._cb.freeParameters.__class__(self._free_parameters) - self._cb.decode = self._cb.decode.__class__(self._decode) - self._cb.encode = self._cb.encode.__class__(self._encode) - self._pending_param_lists = {} - self.type = self.__class__.transform_type - if not isinstance(self.type, str): - self.type = core.BNTransformType_names[self.type] - self.name = self.__class__.name - self.long_name = self.__class__.long_name - self.group = self.__class__.group - self.parameters = self.__class__.parameters - else: - self.handle = handle - self.type = core.BNTransformType_names[core.BNGetTransformType(self.handle)] - self.name = core.BNGetTransformName(self.handle) - self.long_name = core.BNGetTransformLongName(self.handle) - self.group = core.BNGetTransformGroup(self.handle) - count = ctypes.c_ulonglong() - params = core.BNGetTransformParameterList(self.handle, count) - self.parameters = [] - for i in xrange(0, count.value): - self.parameters.append(TransformParameter(params[i].name, params[i].longName, params[i].fixedLength)) - core.BNFreeTransformParameterList(params, count.value) - - def __repr__(self): - return "<transform: %s>" % self.name - - def _get_parameters(self, ctxt, count): - try: - count[0] = len(self.parameters) - param_buf = (core.BNTransformParameterInfo * len(self.parameters))() - for i in xrange(0, len(self.parameters)): - param_buf[i].name = self.parameters[i].name - param_buf[i].longName = self.parameters[i].long_name - param_buf[i].fixedLength = self.parameters[i].fixed_length - result = ctypes.cast(param_buf, ctypes.c_void_p) - self._pending_param_lists[result.value] = (result, param_buf) - return result.value - except: - log_error(traceback.format_exc()) - count[0] = 0 - return None - - def _free_parameters(self, params, count): - try: - buf = ctypes.cast(params, ctypes.c_void_p) - if buf.value not in self._pending_param_lists: - raise ValueError, "freeing parameter list that wasn't allocated" - del self._pending_param_lists[buf.value] - except: - log_error(traceback.format_exc()) - - def _decode(self, ctxt, input_buf, output_buf, params, count): - try: - input_obj = DataBuffer(handle = core.BNDuplicateDataBuffer(input_buf)) - param_map = {} - for i in xrange(0, count): - data = DataBuffer(handle = core.BNDuplicateDataBuffer(params[i].value)) - param_map[params[i].name] = str(data) - result = self.perform_decode(str(input_obj), param_map) - if result is None: - return False - result = str(result) - core.BNSetDataBufferContents(output_buf, result, len(result)) - return True - except: - log_error(traceback.format_exc()) - return False - - def _encode(self, ctxt, input_buf, output_buf, params, count): - try: - input_obj = DataBuffer(handle = core.BNDuplicateDataBuffer(input_buf)) - param_map = {} - for i in xrange(0, count): - data = DataBuffer(handle = core.BNDuplicateDataBuffer(params[i].value)) - param_map[params[i].name] = str(data) - result = self.perform_encode(str(input_obj), param_map) - if result is None: - return False - result = str(result) - core.BNSetDataBufferContents(output_buf, result, len(result)) - return True - except: - log_error(traceback.format_exc()) - return False - - @abc.abstractmethod - def perform_decode(self, data, params): - if self.type == "InvertingTransform": - return self.perform_encode(data, params) - return None - - @abc.abstractmethod - def perform_encode(self, data, params): - return None - - def decode(self, input_buf, params = {}): - input_buf = DataBuffer(input_buf) - output_buf = DataBuffer() - keys = params.keys() - param_buf = (core.BNTransformParameter * len(keys))() - param_data = [] - for i in xrange(0, len(keys)): - data = DataBuffer(params[keys[i]]) - param_buf[i].name = keys[i] - param_buf[i].value = data.handle - if not core.BNDecode(self.handle, input_buf.handle, output_buf.handle, param_buf, len(keys)): - return None - return str(output_buf) - - def encode(self, input_buf, params = {}): - input_buf = DataBuffer(input_buf) - output_buf = DataBuffer() - keys = params.keys() - param_buf = (core.BNTransformParameter * len(keys))() - param_data = [] - for i in xrange(0, len(keys)): - data = DataBuffer(params[keys[i]]) - param_buf[i].name = keys[i] - param_buf[i].value = data.handle - if not core.BNEncode(self.handle, input_buf.handle, output_buf.handle, param_buf, len(keys)): - return None - return str(output_buf) - -class FunctionRecognizer(object): - _instance = None - - def __init__(self): - self._cb = core.BNFunctionRecognizer() - self._cb.context = 0 - self._cb.recognizeLowLevelIL = self._cb.recognizeLowLevelIL.__class__(self._recognize_low_level_il) - - @classmethod - def register_global(cls): - if cls._instance is None: - cls._instance = cls() - core.BNRegisterGlobalFunctionRecognizer(cls._instance._cb) - - @classmethod - def register_arch(cls, arch): - if cls._instance is None: - cls._instance = cls() - core.BNRegisterArchitectureFunctionRecognizer(arch.handle, cls._instance._cb) - - def _recognize_low_level_il(self, ctxt, data, func, il): - try: - file_metadata = FileMetadata(handle = core.BNGetFileForView(data)) - view = BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(data)) - func = Function(view, handle = core.BNNewFunctionReference(func)) - il = LowLevelILFunction(func.arch, handle = core.BNNewLowLevelILFunctionReference(il)) - return self.recognize_low_level_il(view, func, il) - except: - log_error(traceback.format_exc()) - return False - - def recognize_low_level_il(self, data, func, il): - return False - -class _UpdateChannelMetaClass(type): - @property - def list(self): - _init_plugins() - count = ctypes.c_ulonglong() - errors = ctypes.c_char_p() - channels = core.BNGetUpdateChannels(count, errors) - if errors: - error_str = errors.value - core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) - raise IOError, error_str - result = [] - for i in xrange(0, count.value): - result.append(UpdateChannel(channels[i].name, channels[i].description, channels[i].latestVersion)) - core.BNFreeUpdateChannelList(channels, count.value) - return result - - @property - def active(self): - return core.BNGetActiveUpdateChannel() - - @active.setter - def active(self, value): - return core.BNSetActiveUpdateChannel(value) - - def __iter__(self): - _init_plugins() - count = ctypes.c_ulonglong() - errors = ctypes.c_char_p() - channels = core.BNGetUpdateChannels(count, errors) - if errors: - error_str = errors.value - core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) - raise IOError, error_str - try: - for i in xrange(0, count.value): - yield UpdateChannel(channels[i].name, channels[i].description, channels[i].latestVersion) - finally: - core.BNFreeUpdateChannelList(channels, count.value) - - def __setattr__(self, name, value): - try: - type.__setattr__(self,name,value) - except AttributeError: - raise AttributeError, "attribute '%s' is read only" % name - - def __getitem__(cls, name): - _init_plugins() - count = ctypes.c_ulonglong() - errors = ctypes.c_char_p() - channels = core.BNGetUpdateChannels(count, errors) - if errors: - error_str = errors.value - core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) - raise IOError, error_str - result = None - for i in xrange(0, count.value): - if channels[i].name == str(name): - result = UpdateChannel(channels[i].name, channels[i].description, channels[i].latestVersion) - break - core.BNFreeUpdateChannelList(channels, count.value) - if result is None: - raise KeyError, "'%s' is not a valid channel" % str(name) - return result - -class UpdateProgressCallback(object): - def __init__(self, func): - self.cb = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.c_ulonglong, ctypes.c_ulonglong)(self.callback) - self.func = func - - def callback(self, ctxt, progress, total): - try: - if self.func is not None: - return self.func(progress, total) - return True - except: - log_error(traceback.format_exc()) - -class UpdateChannel(object): - __metaclass__ = _UpdateChannelMetaClass - - def __init__(self, name, desc, ver): - self.name = name - self.description = desc - self.latest_version_num = ver - - @property - def versions(self): - """List of versions (read-only)""" - count = ctypes.c_ulonglong() - errors = ctypes.c_char_p() - versions = core.BNGetUpdateChannelVersions(self.name, count, errors) - if errors: - error_str = errors.value - core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) - raise IOError, error_str - result = [] - for i in xrange(0, count.value): - result.append(UpdateVersion(self, versions[i].version, versions[i].notes, versions[i].time)) - core.BNFreeUpdateChannelVersionList(versions, count.value) - return result - - @property - def latest_version(self): - """Latest version (read-only)""" - count = ctypes.c_ulonglong() - errors = ctypes.c_char_p() - versions = core.BNGetUpdateChannelVersions(self.name, count, errors) - if errors: - error_str = errors.value - core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) - raise IOError, error_str - result = None - for i in xrange(0, count.value): - if versions[i].version == self.latest_version_num: - result = UpdateVersion(self, versions[i].version, versions[i].notes, versions[i].time) - break - core.BNFreeUpdateChannelVersionList(versions, count.value) - return result - - @property - def updates_available(self): - """Whether updates are available (read-only)""" - errors = ctypes.c_char_p() - result = core.BNAreUpdatesAvailable(self.name, errors) - if errors: - error_str = errors.value - core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) - raise IOError, error_str - return result - - def __setattr__(self, name, value): - try: - object.__setattr__(self,name,value) - except AttributeError: - raise AttributeError, "attribute '%s' is read only" % name - - def __repr__(self): - return "<channel: %s>" % self.name - - def __str__(self): - return self.name - - def update_to_latest(self, progress = None): - cb = UpdateProgressCallback(progress) - errors = ctypes.c_char_p() - result = core.BNUpdateToLatestVersion(self.name, errors, cb.cb, None) - if errors: - error_str = errors.value - core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) - raise IOError, error_str - return core.BNUpdateResult_names[result] - -class UpdateVersion(object): - def __init__(self, channel, ver, notes, t): - self.channel = channel - self.version = ver - self.notes = notes - self.time = t - - def __repr__(self): - return "<version: %s>" % self.version - - def __str__(self): - return self.version - - def update(self, progress = None): - cb = UpdateProgressCallback(progress) - errors = ctypes.c_char_p() - result = core.BNUpdateToVersion(self.channel.name, self.version, errors, cb.cb, None) - if errors: - error_str = errors.value - core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) - raise IOError, error_str - return core.BNUpdateResult_names[result] - -class PluginCommandContext(object): - def __init__(self, view): - self.view = view - self.address = 0 - self.length = 0 - self.function = None - -class _PluginCommandMetaClass(type): - @property - def list(self): - _init_plugins() - count = ctypes.c_ulonglong() - commands = core.BNGetAllPluginCommands(count) - result = [] - for i in xrange(0, count.value): - result.append(PluginCommand(commands[i])) - core.BNFreePluginCommandList(commands) - return result - - def __iter__(self): - _init_plugins() - count = ctypes.c_ulonglong() - commands = core.BNGetAllPluginCommands(count) - try: - for i in xrange(0, count.value): - yield PluginCommand(commands[i]) - finally: - core.BNFreePluginCommandList(commands) - - def __setattr__(self, name, value): - try: - type.__setattr__(self,name,value) - except AttributeError: - raise AttributeError, "attribute '%s' is read only" % name - -class PluginCommand: - _registered_commands = [] - __metaclass__ = _PluginCommandMetaClass - - def __init__(self, cmd): - self.command = core.BNPluginCommand() - ctypes.memmove(ctypes.byref(self.command), ctypes.byref(cmd), ctypes.sizeof(core.BNPluginCommand)) - self.name = str(cmd.name) - self.description = str(cmd.description) - self.type = core.BNPluginCommandType_names[cmd.type] - - @classmethod - def _default_action(cls, view, action): - try: - file_metadata = FileMetadata(handle = core.BNGetFileForView(view)) - view_obj = BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) - action(view_obj) - except: - log_error(traceback.format_exc()) - - @classmethod - def _address_action(cls, view, addr, action): - try: - file_metadata = FileMetadata(handle = core.BNGetFileForView(view)) - view_obj = BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) - action(view_obj, addr) - except: - log_error(traceback.format_exc()) - - @classmethod - def _range_action(cls, view, addr, length, action): - try: - file_metadata = FileMetadata(handle = core.BNGetFileForView(view)) - view_obj = BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) - action(view_obj, addr, length) - except: - log_error(traceback.format_exc()) - - @classmethod - def _function_action(cls, view, func, action): - try: - file_metadata = FileMetadata(handle = core.BNGetFileForView(view)) - view_obj = BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) - func_obj = Function(view_obj, core.BNNewFunctionReference(func)) - action(view_obj, func_obj) - except: - log_error(traceback.format_exc()) - - @classmethod - def _default_is_valid(cls, view, is_valid): - try: - if is_valid is None: - return True - file_metadata = FileMetadata(handle = core.BNGetFileForView(view)) - view_obj = BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) - return is_valid(view_obj) - except: - log_error(traceback.format_exc()) - return False - - @classmethod - def _address_is_valid(cls, view, addr, is_valid): - try: - if is_valid is None: - return True - file_metadata = FileMetadata(handle = core.BNGetFileForView(view)) - view_obj = BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) - return is_valid(view_obj, addr) - except: - log_error(traceback.format_exc()) - return False - - @classmethod - def _range_is_valid(cls, view, addr, length, is_valid): - try: - if is_valid is None: - return True - file_metadata = FileMetadata(handle = core.BNGetFileForView(view)) - view_obj = BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) - return is_valid(view_obj, addr, length) - except: - log_error(traceback.format_exc()) - return False - - @classmethod - def _function_is_valid(cls, view, func, is_valid): - try: - if is_valid is None: - return True - file_metadata = FileMetadata(handle = core.BNGetFileForView(view)) - view_obj = BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) - func_obj = Function(view_obj, core.BNNewFunctionReference(func)) - return is_valid(view_obj, func_obj) - except: - log_error(traceback.format_exc()) - return False - - @classmethod - def register(cls, name, description, action, is_valid = None): - _init_plugins() - action_obj = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView))(lambda ctxt, view: cls._default_action(view, action)) - is_valid_obj = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView))(lambda ctxt, view: cls._default_is_valid(view, is_valid)) - cls._registered_commands.append((action_obj, is_valid_obj)) - core.BNRegisterPluginCommand(name, description, action_obj, is_valid_obj, None) - - @classmethod - def register_for_address(cls, name, description, action, is_valid = None): - _init_plugins() - action_obj = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView), ctypes.c_ulonglong)(lambda ctxt, view, addr: cls._address_action(view, addr, action)) - is_valid_obj = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView), ctypes.c_ulonglong)(lambda ctxt, view, addr: cls._address_is_valid(view, addr, is_valid)) - cls._registered_commands.append((action_obj, is_valid_obj)) - core.BNRegisterPluginCommandForAddress(name, description, action_obj, is_valid_obj, None) - - @classmethod - def register_for_range(cls, name, description, action, is_valid = None): - _init_plugins() - action_obj = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView), ctypes.c_ulonglong, ctypes.c_ulonglong)(lambda ctxt, view, addr, length: cls._range_action(view, addr, length, action)) - is_valid_obj = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView), ctypes.c_ulonglong, ctypes.c_ulonglong)(lambda ctxt, view, addr, length: cls._range_is_valid(view, addr, length, is_valid)) - cls._registered_commands.append((action_obj, is_valid_obj)) - core.BNRegisterPluginCommandForRange(name, description, action_obj, is_valid_obj, None) - - @classmethod - def register_for_function(cls, name, description, action, is_valid = None): - _init_plugins() - action_obj = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView), ctypes.POINTER(core.BNFunction))(lambda ctxt, view, func: cls._function_action(view, func, action)) - is_valid_obj = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView), ctypes.POINTER(core.BNFunction))(lambda ctxt, view, func: cls._function_is_valid(view, func, is_valid)) - cls._registered_commands.append((action_obj, is_valid_obj)) - core.BNRegisterPluginCommandForFunction(name, description, action_obj, is_valid_obj, None) - - @classmethod - def get_valid_list(cls, context): - commands = cls.list - result = [] - for cmd in commands: - if cmd.is_valid(context): - result.append(cmd) - return result - - def is_valid(self, context): - if context.view is None: - return False - if self.command.type == core.DefaultPluginCommand: - if not self.command.defaultIsValid: - return True - return self.command.defaultIsValid(self.command.context, context.view.handle) - elif self.command.type == core.AddressPluginCommand: - if not self.command.addressIsValid: - return True - return self.command.addressIsValid(self.command.context, context.view.handle, context.address) - elif self.command.type == core.RangePluginCommand: - if context.length == 0: - return False - if not self.command.rangeIsValid: - return True - return self.command.rangeIsValid(self.command.context, context.view.handle, context.address, context.length) - elif self.command.type == core.FunctionPluginCommand: - if context.function is None: - return False - if not self.command.functionIsValid: - return True - return self.command.functionIsValid(self.command.context, context.view.handle, context.function.handle) - return False - - def execute(self, context): - if not self.is_valid(context): - return - if self.command.type == core.DefaultPluginCommand: - self.command.defaultCommand(self.command.context, context.view.handle) - elif self.command.type == core.AddressPluginCommand: - self.command.addressCommand(self.command.context, context.view.handle, context.address) - elif self.command.type == core.RangePluginCommand: - self.command.rangeCommand(self.command.context, context.view.handle, context.address, context.length) - elif self.command.type == core.FunctionPluginCommand: - self.command.functionCommand(self.command.context, context.view.handle, context.function.handle) - - def __repr__(self): - return "<PluginCommand: %s>" % self.name - -class CallingConvention(object): - name = None - caller_saved_regs = [] - int_arg_regs = [] - float_arg_regs = [] - arg_regs_share_index = False - stack_reserved_for_arg_regs = False - int_return_reg = None - high_int_return_reg = None - float_return_reg = None - - _registered_calling_conventions = [] - - def __init__(self, arch, handle = None): - if handle is None: - self.arch = arch - self._pending_reg_lists = {} - self._cb = core.BNCustomCallingConvention() - self._cb.context = 0 - self._cb.getCallerSavedRegisters = self._cb.getCallerSavedRegisters.__class__(self._get_caller_saved_regs) - self._cb.getIntegerArgumentRegisters = self._cb.getIntegerArgumentRegisters.__class__(self._get_int_arg_regs) - self._cb.getFloatArgumentRegisters = self._cb.getFloatArgumentRegisters.__class__(self._get_float_arg_regs) - self._cb.freeRegisterList = self._cb.freeRegisterList.__class__(self._free_register_list) - self._cb.areArgumentRegistersSharedIndex = self._cb.areArgumentRegistersSharedIndex.__class__(self._arg_regs_share_index) - self._cb.isStackReservedForArgumentRegisters = self._cb.isStackReservedForArgumentRegisters.__class__(self._stack_reserved_for_arg_regs) - self._cb.getIntegerReturnValueRegister = self._cb.getIntegerReturnValueRegister.__class__(self._get_int_return_reg) - self._cb.getHighIntegerReturnValueRegister = self._cb.getHighIntegerReturnValueRegister.__class__(self._get_high_int_return_reg) - self._cb.getFloatReturnValueRegister = self._cb.getFloatReturnValueRegister.__class__(self._get_float_return_reg) - self.handle = core.BNCreateCallingConvention(arch.handle, self.__class__.name, self._cb) - self.__class__._registered_calling_conventions.append(self) - else: - self.handle = handle - self.arch = Architecture(core.BNGetCallingConventionArchitecture(self.handle)) - self.__dict__["name"] = core.BNGetCallingConventionName(self.handle) - self.__dict__["arg_regs_share_index"] = core.BNAreArgumentRegistersSharedIndex(self.handle) - self.__dict__["stack_reserved_for_arg_regs"] = core.BNIsStackReservedForArgumentRegisters(self.handle) - - count = ctypes.c_ulonglong() - regs = core.BNGetCallerSavedRegisters(self.handle, count) - result = [] - arch = self.arch - for i in xrange(0, count.value): - result.append(arch.get_reg_name(regs[i])) - core.BNFreeRegisterList(regs, count.value) - self.__dict__["caller_saved_regs"] = result - - count = ctypes.c_ulonglong() - regs = core.BNGetIntegerArgumentRegisters(self.handle, count) - result = [] - arch = self.arch - for i in xrange(0, count.value): - result.append(arch.get_reg_name(regs[i])) - core.BNFreeRegisterList(regs, count.value) - self.__dict__["int_arg_regs"] = result - - count = ctypes.c_ulonglong() - regs = core.BNGetFloatArgumentRegisters(self.handle, count) - result = [] - arch = self.arch - for i in xrange(0, count.value): - result.append(arch.get_reg_name(regs[i])) - core.BNFreeRegisterList(regs, count.value) - self.__dict__["float_arg_regs"] = result - - reg = core.BNGetIntegerReturnValueRegister(self.handle) - if reg == 0xffffffff: - self.__dict__["int_return_reg"] = None - else: - self.__dict__["int_return_reg"] = self.arch.get_reg_name(reg) - - reg = core.BNGetHighIntegerReturnValueRegister(self.handle) - if reg == 0xffffffff: - self.__dict__["high_int_return_reg"] = None - else: - self.__dict__["high_int_return_reg"] = self.arch.get_reg_name(reg) - - reg = core.BNGetFloatReturnValueRegister(self.handle) - if reg == 0xffffffff: - self.__dict__["float_return_reg"] = None - else: - self.__dict__["float_return_reg"] = self.arch.get_reg_name(reg) - - def __del__(self): - core.BNFreeCallingConvention(self.handle) - - def _get_caller_saved_regs(self, ctxt, count): - try: - regs = self.__class__.caller_saved_regs - count[0] = len(regs) - reg_buf = (ctypes.c_uint * len(regs))() - for i in xrange(0, len(regs)): - reg_buf[i] = self.arch.regs[regs[i]].index - result = ctypes.cast(reg_buf, ctypes.c_void_p) - self._pending_reg_lists[result.value] = (result, reg_buf) - return result.value - except: - log_error(traceback.format_exc()) - count[0] = 0 - return None - - def _get_int_arg_regs(self, ctxt, count): - try: - regs = self.__class__.int_arg_regs - count[0] = len(regs) - reg_buf = (ctypes.c_uint * len(regs))() - for i in xrange(0, len(regs)): - reg_buf[i] = self.arch.regs[regs[i]].index - result = ctypes.cast(reg_buf, ctypes.c_void_p) - self._pending_reg_lists[result.value] = (result, reg_buf) - return result.value - except: - log_error(traceback.format_exc()) - count[0] = 0 - return None - - def _get_float_arg_regs(self, ctxt, count): - try: - regs = self.__class__.float_arg_regs - count[0] = len(regs) - reg_buf = (ctypes.c_uint * len(regs))() - for i in xrange(0, len(regs)): - reg_buf[i] = self.arch.regs[regs[i]].index - result = ctypes.cast(reg_buf, ctypes.c_void_p) - self._pending_reg_lists[result.value] = (result, reg_buf) - return result.value - except: - log_error(traceback.format_exc()) - count[0] = 0 - return None - - def _free_register_list(self, ctxt, regs): - try: - buf = ctypes.cast(regs, ctypes.c_void_p) - if buf.value not in self._pending_reg_lists: - raise ValueError, "freeing register list that wasn't allocated" - del self._pending_reg_lists[buf.value] - except: - log_error(traceback.format_exc()) - - def _arg_regs_share_index(self, ctxt): - try: - return self.__class__.arg_regs_share_index - except: - log_error(traceback.format_exc()) - return False - - def _stack_reserved_for_arg_regs(self, ctxt): - try: - return self.__class__.stack_reserved_for_arg_regs - except: - log_error(traceback.format_exc()) - return False - - def _get_int_return_reg(self, ctxt): - try: - return self.arch.regs[self.__class__.int_return_reg].index - except: - log_error(traceback.format_exc()) - return False - - def _get_high_int_return_reg(self, ctxt): - try: - if self.__class__.high_int_return_reg is None: - return 0xffffffff - return self.arch.regs[self.__class__.high_int_return_reg].index - except: - log_error(traceback.format_exc()) - return False - - def _get_float_return_reg(self, ctxt): - try: - if self.__class__.float_return_reg is None: - return 0xffffffff - return self.arch.regs[self.__class__.float_int_return_reg].index - except: - log_error(traceback.format_exc()) - return False - - def __repr__(self): - return "<calling convention: %s %s>" % (self.arch.name, self.name) - - def __str__(self): - return self.name - -class _PlatformMetaClass(type): - @property - def list(self): - _init_plugins() - count = ctypes.c_ulonglong() - platforms = core.BNGetPlatformList(count) - result = [] - for i in xrange(0, count.value): - result.append(Platform(None, core.BNNewPlatformReference(platforms[i]))) - core.BNFreePlatformList(platforms, count.value) - return result - - @property - def os_list(self): - _init_plugins() - count = ctypes.c_ulonglong() - platforms = core.BNGetPlatformOSList(count) - result = [] - for i in xrange(0, count.value): - result.append(str(platforms[i])) - core.BNFreePlatformOSList(platforms, count.value) - return result - - def __iter__(self): - _init_plugins() - count = ctypes.c_ulonglong() - platforms = core.BNGetPlatformList(count) - try: - for i in xrange(0, count.value): - yield Platform(None, core.BNNewPlatformReference(platforms[i])) - finally: - core.BNFreePlatformList(platforms, count.value) - - def __setattr__(self, name, value): - try: - type.__setattr__(self,name,value) - except AttributeError: - raise AttributeError, "attribute '%s' is read only" % name - - def __getitem__(cls, value): - _init_plugins() - platform = core.BNGetPlatformByName(str(value)) - if platform is None: - raise KeyError, "'%s' is not a valid platform" % str(value) - return Platform(None, platform) - - def get_list(cls, os = None, arch = None): - _init_plugins() - count = ctypes.c_ulonglong() - if os is None: - platforms = core.BNGetPlatformList(count) - elif arch is None: - platforms = core.BNGetPlatformListByOS(os) - else: - platforms = core.BNGetPlatformListByArchitecture(os, arch.handle) - result = [] - for i in xrange(0, count.value): - result.append(Platform(None, core.BNNewPlatformReference(platforms[i]))) - core.BNFreePlatformList(platforms, count.value) - return result - -class Platform(object): - """ - ``class Platform`` contains all information releated to the execution environment of the binary, mainly the - calling conventions used. - """ - __metaclass__ = _PlatformMetaClass - name = None - - def __init__(self, arch, handle = None): - if handle is None: - self.arch = arch - self.handle = core.BNCreatePlatform(arch.handle, self.__class__.name) - else: - self.handle = handle - self.__dict__["name"] = core.BNGetPlatformName(self.handle) - self.arch = Architecture(core.BNGetPlatformArchitecture(self.handle)) - - def __del__(self): - core.BNFreePlatform(self.handle) - - @property - def default_calling_convention(self): - """ - Default calling convention. - - :getter: returns a CallingConvention object for the default calling convention. - :setter: sets the default calling convention - :type: CallingConvention - """ - result = core.BNGetPlatformDefaultCallingConvention(self.handle) - if result is None: - return None - return CallingConvention(None, result) - - @default_calling_convention.setter - def default_calling_convention(self, value): - core.BNRegisterPlatformDefaultCallingConvention(self.handle, value.handle) - - @property - def cdecl_calling_convention(self): - """ - Cdecl calling convention. - - :getter: returns a CallingConvention object for the cdecl calling convention. - :setter sets the cdecl calling convention - :type: CallingConvention - """ - result = core.BNGetPlatformCdeclCallingConvention(self.handle) - if result is None: - return None - return CallingConvention(None, result) - - @cdecl_calling_convention.setter - def cdecl_calling_convention(self, value): - core.BNRegisterPlatformCdeclCallingConvention(self.handle, value.handle) - - @property - def stdcall_calling_convention(self): - """ - Stdcall calling convention. - - :getter: returns a CallingConvention object for the stdcall calling convention. - :setter sets the stdcall calling convention - :type: CallingConvention - """ - result = core.BNGetPlatformStdcallCallingConvention(self.handle) - if result is None: - return None - return CallingConvention(None, result) - - @stdcall_calling_convention.setter - def stdcall_calling_convention(self, value): - core.BNRegisterPlatformStdcallCallingConvention(self.handle, value.handle) - - @property - def fastcall_calling_convention(self): - """ - Fastcall calling convention. - - :getter: returns a CallingConvention object for the fastcall calling convention. - :setter sets the fastcall calling convention - :type: CallingConvention - """ - result = core.BNGetPlatformFastcallCallingConvention(self.handle) - if result is None: - return None - return CallingConvention(None, result) - - @fastcall_calling_convention.setter - def fastcall_calling_convention(self, value): - core.BNRegisterPlatformFastcallCallingConvention(self.handle, value.handle) - - @property - def system_call_convention(self): - """ - System call convention. - - :getter: returns a CallingConvention object for the system call convention. - :setter sets the system call convention - :type: CallingConvention - """ - result = core.BNGetPlatformSystemCallConvention(self.handle) - if result is None: - return None - return CallingConvention(None, result) - - @system_call_convention.setter - def system_call_convention(self, value): - core.BNSetPlatformSystemCallConvention(self.handle, value.handle) - - @property - def calling_conventions(self): - """ - List of platform CallingConvention objects (read-only) - - :getter: returns the list of supported CallingConvention objects - :type: list(CallingConvention) - """ - count = ctypes.c_ulonglong() - cc = core.BNGetPlatformCallingConventions(self.handle, count) - result = [] - for i in xrange(0, count.value): - result.append(CallingConvention(None, core.BNNewCallingConventionReference(cc[i]))) - core.BNFreeCallingConventionList(cc, count.value) - return result - - def __setattr__(self, name, value): - try: - object.__setattr__(self,name,value) - except AttributeError: - raise AttributeError, "attribute '%s' is read only" % name - - def __repr__(self): - return "<platform: %s>" % self.name - - def __str__(self): - return self.name - - def register(self, os): - """ - ``register`` registers the platform for given OS name. - - :param str os: OS name to register - :rtype: None - """ - core.BNRegisterPlatform(os, self.handle) - - def register_calling_convention(self, cc): - """ - ``register_calling_convention`` register a new calling convention. - - :param CallingConvention cc: a CallingConvention object to register - :rtype: None - """ - core.BNRegisterPlatformCallingConvention(self.handle, cc.handle) - - def get_related_platform(self, arch): - result = core.BNGetRelatedPlatform(self.handle, arch.handle) - if not result: - return None - return Platform(None, handle = result) - - def add_related_platform(self, arch, platform): - core.BNAddRelatedPlatform(self.handle, arch.handle, platform.handle) - - def get_associated_platform_by_address(self, addr): - new_addr = ctypes.c_ulonglong() - new_addr.value = addr - result = core.BNGetAssociatedPlatformByAddress(self.handle, new_addr) - return Platform(None, handle = result), new_addr.value - -class ScriptingOutputListener(object): - def _register(self, handle): - self._cb = core.BNScriptingOutputListener() - self._cb.context = 0 - self._cb.output = self._cb.output.__class__(self._output) - self._cb.error = self._cb.error.__class__(self._error) - self._cb.inputReadyStateChanged = self._cb.inputReadyStateChanged.__class__(self._input_ready_state_changed) - core.BNRegisterScriptingInstanceOutputListener(handle, self._cb) - - def _unregister(self, handle): - core.BNUnregisterScriptingInstanceOutputListener(handle, self._cb) - - def _output(self, ctxt, text): - try: - self.notify_output(text) - except: - log_error(traceback.format_exc()) - - def _error(self, ctxt, text): - try: - self.notify_error(text) - except: - log_error(traceback.format_exc()) - - def _input_ready_state_changed(self, ctxt, state): - try: - self.notify_input_ready_state_changed(state) - except: - log_error(traceback.format_exc()) - - def notify_output(self, text): - pass - - def notify_error(self, text): - pass - - def notify_input_ready_state_changed(self, state): - pass - -class ScriptingInstance(object): - def __init__(self, provider, handle = None): - if handle is None: - self._cb = core.BNScriptingInstanceCallbacks() - self._cb.context = 0 - self._cb.destroyInstance = self._cb.destroyInstance.__class__(self._destroy_instance) - self._cb.executeScriptInput = self._cb.executeScriptInput.__class__(self._execute_script_input) - self._cb.setCurrentBinaryView = self._cb.setCurrentBinaryView.__class__(self._set_current_binary_view) - self._cb.setCurrentFunction = self._cb.setCurrentFunction.__class__(self._set_current_function) - self._cb.setCurrentBasicBlock = self._cb.setCurrentBasicBlock.__class__(self._set_current_basic_block) - self._cb.setCurrentAddress = self._cb.setCurrentAddress.__class__(self._set_current_address) - self._cb.setCurrentSelection = self._cb.setCurrentSelection.__class__(self._set_current_selection) - self.handle = core.BNInitScriptingInstance(provider.handle, self._cb) - else: - self.handle = core.handle_of_type(handle, core.BNScriptingInstance) - self.listeners = [] - - def __del__(self): - core.BNFreeScriptingInstance(self.handle) - - def _destroy_instance(self, ctxt): - try: - self.perform_destroy_instance() - except: - log_error(traceback.format_exc()) - - def _execute_script_input(self, ctxt, text): - try: - return self.perform_execute_script_input(text) - except: - log_error(traceback.format_exc()) - return core.InvalidScriptInput - - def _set_current_binary_view(self, ctxt, view): - try: - if view: - view = BinaryView(handle = core.BNNewViewReference(view)) - else: - view = None - self.perform_set_current_binary_view(view) - except: - log_error(traceback.format_exc()) - - def _set_current_function(self, ctxt, func): - try: - if func: - func = Function(BinaryView(handle = core.BNGetFunctionData(func)), core.BNNewFunctionReference(func)) - else: - func = None - self.perform_set_current_function(func) - except: - log_error(traceback.format_exc()) - - def _set_current_basic_block(self, ctxt, block): - try: - if block: - func = core.BNGetBasicBlockFunction(block) - if func is None: - block = None - else: - block = BasicBlock(BinaryView(handle = core.BNGetFunctionData(func)), core.BNNewBasicBlockReference(block)) - core.BNFreeFunction(func) - else: - block = None - self.perform_set_current_basic_block(block) - except: - log_error(traceback.format_exc()) - - def _set_current_address(self, ctxt, addr): - try: - self.perform_set_current_address(addr) - except: - log_error(traceback.format_exc()) - - def _set_current_selection(self, ctxt, begin, end): - try: - self.perform_set_current_selection(begin, end) - except: - log_error(traceback.format_exc()) - - @abc.abstractmethod - def perform_destroy_instance(self): - raise NotImplementedError - - @abc.abstractmethod - def perform_execute_script_input(self, text): - return core.InvalidScriptInput - - @abc.abstractmethod - def perform_set_current_binary_view(self, view): - raise NotImplementedError - - @abc.abstractmethod - def perform_set_current_function(self, func): - raise NotImplementedError - - @abc.abstractmethod - def perform_set_current_basic_block(self, block): - raise NotImplementedError - - @abc.abstractmethod - def perform_set_current_address(self, addr): - raise NotImplementedError - - @abc.abstractmethod - def perform_set_current_selection(self, begin, end): - raise NotImplementedError - - @property - def input_ready_state(self): - return core.BNGetScriptingInstanceInputReadyState(self.handle) - - @input_ready_state.setter - def input_ready_state(self, value): - core.BNNotifyInputReadyStateForScriptingInstance(self.handle, value) - - def output(self, text): - core.BNNotifyOutputForScriptingInstance(self.handle, text) - - def error(self, text): - core.BNNotifyErrorForScriptingInstance(self.handle, text) - - def execute_script_input(self, text): - return core.BNExecuteScriptInput(self.handle, text) - - def set_current_binary_view(self, view): - if view is not None: - view = view.handle - core.BNSetScriptingInstanceCurrentBinaryView(self.handle, view) - - def set_current_function(self, func): - if func is not None: - func = func.handle - core.BNSetScriptingInstanceCurrentFunction(self.handle, func) - - def set_current_basic_block(self, block): - if block is not None: - block = block.handle - core.BNSetScriptingInstanceCurrentBasicBlock(self.handle, block) - - def set_current_address(self, addr): - core.BNSetScriptingInstanceCurrentAddress(self.handle, addr) - - def set_current_selection(self, begin, end): - core.BNSetScriptingInstanceCurrentSelection(self.handle, begin, end) - - def register_output_listener(self, listener): - listener._register(self.handle) - self.listeners.append(listener) - - def unregister_output_listener(self, listener): - if listener in self.listeners: - listener._unregister(self.handle) - self.listeners.remove(listener) - -class _ScriptingProviderMetaclass(type): - @property - def list(self): - """List all ScriptingProvider types (read-only)""" - _init_plugins() - count = ctypes.c_ulonglong() - types = core.BNGetScriptingProviderList(count) - result = [] - for i in xrange(0, count.value): - result.append(ScriptingProvider(types[i])) - core.BNFreeScriptingProviderList(types) - return result - - def __iter__(self): - _init_plugins() - count = ctypes.c_ulonglong() - types = core.BNGetScriptingProviderList(count) - try: - for i in xrange(0, count.value): - yield ScriptingProvider(types[i]) - finally: - core.BNFreeScriptingProviderList(types) - - def __getitem__(self, value): - _init_plugins() - provider = core.BNGetScriptingProviderByName(str(value)) - if provider is None: - raise KeyError, "'%s' is not a valid scripting provider" % str(value) - return ScriptingProvider(provider) - - def __setattr__(self, name, value): - try: - type.__setattr__(self,name,value) - except AttributeError: - raise AttributeError, "attribute '%s' is read only" % name - -class ScriptingProvider(object): - __metaclass__ = _ScriptingProviderMetaclass - - name = None - instance_class = None - _registered_providers = [] - - def __init__(self, handle = None): - if handle is not None: - self.handle = core.handle_of_type(handle, core.BNScriptingProvider) - self.__dict__["name"] = core.BNGetScriptingProviderName(handle) - - def register(self): - self._cb = core.BNScriptingProviderCallbacks() - self._cb.context = 0 - self._cb.createInstance = self._cb.createInstance.__class__(self._create_instance) - self.handle = core.BNRegisterScriptingProvider(self.__class__.name, self._cb) - self.__class__._registered_providers.append(self) - - def _create_instance(self, ctxt): - try: - result = self.__class__.instance_class(self) - if result is None: - return None - return ctypes.cast(core.BNNewScriptingInstanceReference(result.handle), ctypes.c_void_p).value - except: - log_error(traceback.format_exc()) - return None - - def create_instance(self): - result = core.BNCreateScriptingProviderInstance(self.handle) - if result is None: - return None - return ScriptingInstance(self, handle = result) - -class _PythonScriptingInstanceOutput(object): - def __init__(self, orig, is_error): - self.orig = orig - self.is_error = is_error - self.buffer = "" - - def write(self, data): - global _output_to_log - - interpreter = None - if "value" in dir(PythonScriptingInstance._interpreter): - interpreter = PythonScriptingInstance._interpreter.value - - if interpreter is None: - if _output_to_log: - self.buffer += data - while True: - i = self.buffer.find('\n') - if i == -1: - break - line = self.buffer[:i] - self.buffer = self.buffer[i + 1:] - - if self.is_error: - log_error(line) - else: - log_info(line) - else: - self.orig.write(data) - else: - PythonScriptingInstance._interpreter.value = None - try: - if self.is_error: - interpreter.instance.error(data) - else: - interpreter.instance.output(data) - finally: - PythonScriptingInstance._interpreter.value = interpreter - -class _PythonScriptingInstanceInput(object): - def __init__(self, orig): - self.orig = orig - - def read(self, size): - interpreter = None - if "value" in dir(PythonScriptingInstance._interpreter): - interpreter = PythonScriptingInstance._interpreter.value - - if interpreter is None: - return self.orig.read(size) - else: - PythonScriptingInstance._interpreter.value = None - try: - result = interpreter.read(size) - finally: - PythonScriptingInstance._interpreter.value = interpreter - return result - - def readline(self): - interpreter = None - if "value" in dir(PythonScriptingInstance._interpreter): - interpreter = PythonScriptingInstance._interpreter.value - - if interpreter is None: - return self.orig.readline() - else: - result = "" - while True: - data = interpreter.read(1) - result += data - if (len(data) == 0) or (data == "\n"): - break - return result - -class PythonScriptingInstance(ScriptingInstance): - _interpreter = threading.local() - - class InterpreterThread(threading.Thread): - def __init__(self, instance): - super(PythonScriptingInstance.InterpreterThread, self).__init__() - self.instance = instance - self.locals = {"__name__": "__console__", "__doc__": None, "binaryninja": sys.modules[__name__]} - self.interpreter = code.InteractiveInterpreter(self.locals) - self.event = threading.Event() - self.daemon = True - - # Latest selections from UI - self.current_view = None - self.current_func = None - self.current_block = None - self.current_addr = 0 - self.current_selection_begin = 0 - self.current_selection_end = 0 - - # Selections that were current as of last issued command - self.active_view = None - self.active_func = None - self.active_block = None - self.active_addr = 0 - self.active_selection_begin = 0 - self.active_selection_end = 0 - - self.locals["get_selected_data"] = self.get_selected_data - self.locals["write_at_cursor"] = self.write_at_cursor - - self.exit = False - self.code = None - self.input = "" - - self.interpreter.runsource("from binaryninja import *\n") - - def execute(self, code): - self.code = code - self.event.set() - - def add_input(self, data): - self.input += data - self.event.set() - - def end(self): - self.exit = True - self.event.set() - - def read(self, size): - while not self.exit: - if len(self.input) > size: - result = self.input[:size] - self.input = self.input[size:] - return result - elif len(self.input) > 0: - result = self.input - self.input = "" - return result - self.instance.input_ready_state = core.ReadyForScriptProgramInput - self.event.wait() - self.event.clear() - return "" - - def run(self): - while not self.exit: - self.event.wait() - self.event.clear() - if self.exit: - break - if self.code is not None: - self.instance.input_ready_state = core.NotReadyForInput - code = self.code - self.code = None - - PythonScriptingInstance._interpreter.value = self - try: - self.active_view = self.current_view - self.active_func = self.current_func - self.active_block = self.current_block - self.active_addr = self.current_addr - self.active_selection_begin = self.current_selection_begin - self.active_selection_end = self.current_selection_end - - self.locals["current_view"] = self.active_view - self.locals["bv"] = self.active_view - self.locals["current_function"] = self.active_func - self.locals["current_basic_block"] = self.active_block - self.locals["current_address"] = self.active_addr - self.locals["here"] = self.active_addr - self.locals["current_selection"] = (self.active_selection_begin, self.active_selection_end) - - self.interpreter.runsource(code) - - if self.locals["here"] != self.active_addr: - if not self.active_view.file.navigate(self.active_view.file.view, self.locals["here"]): - sys.stderr.write("Address 0x%x is not valid for the current view\n" % self.locals["here"]) - elif self.locals["current_address"] != self.active_addr: - if not self.active_view.file.navigate(self.active_view.file.view, self.locals["current_address"]): - sys.stderr.write("Address 0x%x is not valid for the current view\n" % self.locals["current_address"]) - except: - traceback.print_exc() - finally: - PythonScriptingInstance._interpreter.value = None - self.instance.input_ready_state = core.ReadyForScriptExecution - - def get_selected_data(self): - if self.active_view is None: - return None - length = self.active_selection_end - self.active_selection_begin - return self.active_view.read(self.active_selection_begin, length) - - def write_at_cursor(self, data): - if self.active_view is None: - return 0 - selected_length = self.active_selection_end - self.active_selection_begin - data = str(data) - if (len(data) == selected_length) or (selected_length == 0): - return self.active_view.write(self.active_selection_begin, data) - else: - self.active_view.remove(self.active_selection_begin, selected_length) - return self.active_view.insert(self.active_selection_begin, data) - - def __init__(self, provider): - super(PythonScriptingInstance, self).__init__(provider) - self.interpreter = PythonScriptingInstance.InterpreterThread(self) - self.interpreter.start() - self.queued_input = "" - self.input_ready_state = core.ReadyForScriptExecution - - @abc.abstractmethod - def perform_destroy_instance(self): - self.interpreter.end() - - @abc.abstractmethod - def perform_execute_script_input(self, text): - if self.input_ready_state == core.NotReadyForInput: - return core.InvalidScriptInput - - if self.input_ready_state == core.ReadyForScriptProgramInput: - if len(text) == 0: - return core.SuccessfulScriptExecution - self.input_ready_state = core.NotReadyForInput - self.interpreter.add_input(text) - return core.SuccessfulScriptExecution - - try: - result = code.compile_command(text) - except: - result = False - - if result is None: - # Command is not complete, ask for more input - return core.IncompleteScriptInput - - self.input_ready_state = core.NotReadyForInput - self.interpreter.execute(text) - return core.SuccessfulScriptExecution - - @abc.abstractmethod - def perform_set_current_binary_view(self, view): - self.interpreter.current_view = view - - @abc.abstractmethod - def perform_set_current_function(self, func): - self.interpreter.current_func = func - - @abc.abstractmethod - def perform_set_current_basic_block(self, block): - self.interpreter.current_block = block - - @abc.abstractmethod - def perform_set_current_address(self, addr): - self.interpreter.current_addr = addr - - @abc.abstractmethod - def perform_set_current_selection(self, begin, end): - self.interpreter.current_selection_begin = begin - self.interpreter.current_selection_end = end - -class PythonScriptingProvider(ScriptingProvider): - name = "Python" - instance_class = PythonScriptingInstance - -class MainThreadAction(object): - def __init__(self, handle): - self.handle = handle - - def __del__(self): - core.BNFreeMainThreadAction(self.handle) - - def execute(self): - core.BNExecuteMainThreadAction(self.handle) - - @property - def done(self): - return core.BNIsMainThreadActionDone(self.handle) - - def wait(self): - core.BNWaitForMainThreadAction(self.handle) - -class MainThreadActionHandler(object): - _main_thread = None - - def __init__(self): - self._cb = core.BNMainThreadCallbacks() - self._cb.context = 0 - self._cb.addAction = self._cb.addAction.__class__(self._add_action) - - def register(self): - self.__class__._main_thread = self - core.BNRegisterMainThread(self._cb) - - def _add_action(self, ctxt, action): - try: - self.add_action(MainThreadAction(action)) - except: - log_error(traceback.format_exc()) - - def add_action(self, action): - pass - -class _BackgroundTaskMetaclass(type): - @property - def list(self): - """List all running background tasks (read-only)""" - count = ctypes.c_ulonglong() - tasks = core.BNGetRunningBackgroundTasks(count) - result = [] - for i in xrange(0, count.value): - result.append(BackgroundTask(core.BNNewBackgroundTaskReference(tasks[i]))) - core.BNFreeBackgroundTaskList(tasks) - return result - - def __iter__(self): - _init_plugins() - count = ctypes.c_ulonglong() - tasks = core.BNGetRunningBackgroundTasks(count) - try: - for i in xrange(0, count.value): - yield BackgroundTask(core.BNNewBackgroundTaskReference(tasks[i])) - finally: - core.BNFreeBackgroundTaskList(tasks) - -class BackgroundTask(object): - __metaclass__ = _BackgroundTaskMetaclass - - def __init__(self, initial_progress_text = "", can_cancel = False, handle = None): - if handle is None: - self.handle = core.BNBeginBackgroundTask(initial_progress_text, can_cancel) - else: - self.handle = handle - - def __del__(self): - core.BNFreeBackgroundTask(self.handle) - - @property - def progress(self): - """Text description of the progress of the background task (displayed in status bar of the UI)""" - return core.BNGetBackgroundTaskProgressText(self.handle) - - @progress.setter - def progress(self, value): - core.BNSetBackgroundTaskProgressText(self.handle, str(value)) - - @property - def can_cancel(self): - """Whether the task can be cancelled (read-only)""" - return core.BNCanCancelBackgroundTask(self.handle) - - @property - def finished(self): - """Whether the task has finished""" - return core.BNIsBackgroundTaskFinished(self.handle) - - @finished.setter - def finished(self, value): - if value: - self.finish() - - def finish(self): - core.BNFinishBackgroundTask(self.handle) - - @property - def cancelled(self): - """Whether the task has been cancelled""" - return core.BNIsBackgroundTaskCancelled(self.handle) - - @cancelled.setter - def cancelled(self, value): - if value: - self.cancel() - - def cancel(self): - core.BNCancelBackgroundTask(self.handle) - -class BackgroundTaskThread(BackgroundTask): - def __init__(self, initial_progress_text = "", can_cancel = False): - class _Thread(threading.Thread): - def __init__(self, task): - threading.Thread.__init__(self) - self.task = task - - def run(self): - self.task.run() - self.task.finish() - self.task = None - - BackgroundTask.__init__(self, initial_progress_text, can_cancel) - self.thread = _Thread(self) - - def run(self): - pass - - def start(self): - self.thread.start() - - def join(self): - self.thread.join() - -class LabelField(object): - def __init__(self, text): - self.text = text - - def _fill_core_struct(self, value): - value.type = core.LabelFormField - value.prompt = self.text - - def _fill_core_result(self, value): - pass - - def _get_result(self, value): - pass - -class SeparatorField(object): - def _fill_core_struct(self, value): - value.type = core.SeparatorFormField - - def _fill_core_result(self, value): - pass - - def _get_result(self, value): - pass - -class TextLineField(object): - def __init__(self, prompt): - self.prompt = prompt - self.result = None - - def _fill_core_struct(self, value): - value.type = core.TextLineFormField - value.prompt = self.prompt - - def _fill_core_result(self, value): - value.stringResult = core.BNAllocString(str(self.result)) - - def _get_result(self, value): - self.result = value.stringResult - -class MultilineTextField(object): - def __init__(self, prompt): - self.prompt = prompt - self.result = None - - def _fill_core_struct(self, value): - value.type = core.MultilineTextFormField - value.prompt = self.prompt - - def _fill_core_result(self, value): - value.stringResult = core.BNAllocString(str(self.result)) - - def _get_result(self, value): - self.result = value.stringResult - -class IntegerField(object): - def __init__(self, prompt): - self.prompt = prompt - self.result = None - - def _fill_core_struct(self, value): - value.type = core.IntegerFormField - value.prompt = self.prompt - - def _fill_core_result(self, value): - value.intResult = self.result - - def _get_result(self, value): - self.result = value.intResult - -class AddressField(object): - def __init__(self, prompt, view = None, current_address = 0): - self.prompt = prompt - self.view = view - self.current_address = current_address - self.result = None - - def _fill_core_struct(self, value): - value.type = core.AddressFormField - value.prompt = self.prompt - value.view = None - if self.view is not None: - value.view = self.view.handle - value.currentAddress = self.current_address - - def _fill_core_result(self, value): - value.addressResult = self.result - - def _get_result(self, value): - self.result = value.addressResult - -class ChoiceField(object): - def __init__(self, prompt, choices): - self.prompt = prompt - self.choices = choices - self.result = None - - def _fill_core_struct(self, value): - value.type = core.ChoiceFormField - value.prompt = self.prompt - choice_buf = (ctypes.c_char_p * len(self.choices))() - for i in xrange(0, len(self.choices)): - choice_buf[i] = str(self.choices[i]) - value.choices = choice_buf - value.count = len(self.choices) - - def _fill_core_result(self, value): - value.indexResult = self.result - - def _get_result(self, value): - self.result = value.indexResult - -class OpenFileNameField(object): - def __init__(self, prompt, ext = ""): - self.prompt = prompt - self.ext = ext - self.result = None - - def _fill_core_struct(self, value): - value.type = core.OpenFileNameFormField - value.prompt = self.prompt - value.ext = self.ext - - def _fill_core_result(self, value): - value.stringResult = core.BNAllocString(str(self.result)) - - def _get_result(self, value): - self.result = value.stringResult - -class SaveFileNameField(object): - def __init__(self, prompt, ext = "", default_name = ""): - self.prompt = prompt - self.ext = ext - self.default_name = default_name - self.result = None - - def _fill_core_struct(self, value): - value.type = core.SaveFileNameFormField - value.prompt = self.prompt - value.ext = self.ext - value.defaultName = self.default_name - - def _fill_core_result(self, value): - value.stringResult = core.BNAllocString(str(self.result)) - - def _get_result(self, value): - self.result = value.stringResult - -class DirectoryNameField(object): - def __init__(self, prompt, default_name = ""): - self.prompt = prompt - self.default_name = default_name - self.result = None - - def _fill_core_struct(self, value): - value.type = core.DirectoryNameField - value.prompt = self.prompt - value.defaultName = self.default_name - - def _fill_core_result(self, value): - value.stringResult = core.BNAllocString(str(self.result)) - - def _get_result(self, value): - self.result = value.stringResult - -class InteractionHandler(object): - _interaction_handler = None - - def __init__(self): - self._cb = core.BNInteractionHandlerCallbacks() - self._cb.context = 0 - self._cb.showPlainTextReport = self._cb.showPlainTextReport.__class__(self._show_plain_text_report) - self._cb.showMarkdownReport = self._cb.showMarkdownReport.__class__(self._show_markdown_report) - self._cb.showHTMLReport = self._cb.showHTMLReport.__class__(self._show_html_report) - self._cb.getTextLineInput = self._cb.getTextLineInput.__class__(self._get_text_line_input) - self._cb.getIntegerInput = self._cb.getIntegerInput.__class__(self._get_int_input) - self._cb.getAddressInput = self._cb.getAddressInput.__class__(self._get_address_input) - self._cb.getChoiceInput = self._cb.getChoiceInput.__class__(self._get_choice_input) - self._cb.getOpenFileNameInput = self._cb.getOpenFileNameInput.__class__(self._get_open_filename_input) - self._cb.getSaveFileNameInput = self._cb.getSaveFileNameInput.__class__(self._get_save_filename_input) - self._cb.getDirectoryNameInput = self._cb.getDirectoryNameInput.__class__(self._get_directory_name_input) - self._cb.getFormInput = self._cb.getFormInput.__class__(self._get_form_input) - self._cb.showMessageBox = self._cb.showMessageBox.__class__(self._show_message_box) - - def register(self): - self.__class__._interaction_handler = self - core.BNRegisterInteractionHandler(self._cb) - - def _show_plain_text_report(self, ctxt, view, title, contents): - try: - if view: - view = BinaryView(handle = core.BNNewViewReference(view)) - else: - view = None - self.show_plain_text_report(view, title, contents) - except: - log_error(traceback.format_exc()) - - def _show_markdown_report(self, ctxt, view, title, contents, plaintext): - try: - if view: - view = BinaryView(handle = core.BNNewViewReference(view)) - else: - view = None - self.show_markdown_report(view, title, contents, plaintext) - except: - log_error(traceback.format_exc()) - - def _show_html_report(self, ctxt, view, title, contents, plaintext): - try: - if view: - view = BinaryView(handle = core.BNNewViewReference(view)) - else: - view = None - self.show_html_report(view, title, contents, plaintext) - except: - log_error(traceback.format_exc()) - - def _get_text_line_input(self, ctxt, result, prompt, title): - try: - value = self.get_text_line_input(prompt, title) - if value is None: - return False - result[0] = core.BNAllocString(str(value)) - return True - except: - log_error(traceback.format_exc()) - - def _get_int_input(self, ctxt, result, prompt, title): - try: - value = self.get_int_input(prompt, title) - if value is None: - return False - result[0] = value - return True - except: - log_error(traceback.format_exc()) - - def _get_address_input(self, ctxt, result, prompt, title, view, current_address): - try: - if view: - view = BinaryView(handle = core.BNNewViewReference(view)) - else: - view = None - value = self.get_address_input(prompt, title, view, current_address) - if value is None: - return False - result[0] = value - return True - except: - log_error(traceback.format_exc()) - - def _get_choice_input(self, ctxt, result, prompt, title, choice_buf, count): - try: - choices = [] - for i in xrange(0, count): - choices.append(choice_buf[i]) - value = self.get_choice_input(prompt, title, choices) - if value is None: - return False - result[0] = value - return True - except: - log_error(traceback.format_exc()) - - def _get_open_filename_input(self, ctxt, result, prompt, ext): - try: - value = self.get_open_filename_input(prompt, ext) - if value is None: - return False - result[0] = core.BNAllocString(str(value)) - return True - except: - log_error(traceback.format_exc()) - - def _get_save_filename_input(self, ctxt, result, prompt, ext, default_name): - try: - value = self.get_save_filename_input(prompt, ext, default_name) - if value is None: - return False - result[0] = core.BNAllocString(str(value)) - return True - except: - log_error(traceback.format_exc()) - - def _get_directory_name_input(self, ctxt, result, prompt, default_name): - try: - value = self.get_directory_name_input(prompt, default_name) - if value is None: - return False - result[0] = core.BNAllocString(str(value)) - return True - except: - log_error(traceback.format_exc()) - - def _get_form_input(self, ctxt, fields, count, title): - try: - field_objs = [] - for i in xrange(0, count): - if fields[i].type == core.LabelFormField: - field_objs.append(LabelField(fields[i].prompt)) - elif fields[i].type == core.SeparatorFormField: - field_objs.append(SeparatorField()) - elif fields[i].type == core.TextLineFormField: - field_objs.append(TextLineField(fields[i].prompt)) - elif fields[i].type == core.MultilineTextFormField: - field_objs.append(MultilineTextField(fields[i].prompt)) - elif fields[i].type == core.IntegerFormField: - field_objs.append(IntegerField(fields[i].prompt)) - elif fields[i].type == core.AddressFormField: - view = None - if fields[i].view: - view = BinaryView(handle = core.BNNewViewReference(fields[i].view)) - field_objs.append(AddressField(fields[i].prompt, view, fields[i].currentAddress)) - elif fields[i].type == core.ChoiceFormField: - choices = [] - for i in xrange(0, fields[i].count): - choices.append(fields[i].choices[i]) - field_objs.append(ChoiceField(fields[i].prompt, choices)) - elif fields[i].type == core.OpenFileNameFormField: - field_objs.append(OpenFileNameField(fields[i].prompt, fields[i].ext)) - elif fields[i].type == core.SaveFileNameFormField: - field_objs.append(SaveFileNameField(fields[i].prompt, fields[i].ext, fields[i].defaultName)) - elif fields[i].type == core.DirectoryNameField: - field_objs.append(DirectoryNameField(fields[i].prompt, fields[i].defaultName)) - else: - field_objs.append(LabelField(fields[i].prompt)) - if not self.get_form_input(field_objs, title): - return False - for i in xrange(0, count): - field_objs[i]._fill_core_result(fields[i]) - return True - except: - log_error(traceback.format_exc()) - - def _show_message_box(self, ctxt, title, text, buttons, icon): - try: - return self.show_message_box(title, text, buttons, icon) - except: - log_error(traceback.format_exc()) - - def show_plain_text_report(self, view, title, contents): - pass - - def show_markdown_report(self, view, title, contents, plaintext): - self.show_html_report(view, title, markdown_to_html(contents), plaintext) - - def show_html_report(self, view, title, contents, plaintext): - if len(plaintext) != 0: - self.show_plain_text_report(view, title, plaintext) - - def get_text_line_input(self, prompt, title): - return None - - def get_int_input(self, prompt, title): - while True: - text = self.get_text_line_input(prompt, title) - if len(text) == 0: - return False - try: - return int(text) - except: - continue - - def get_address_input(self, prompt, title, view, current_address): - return get_int_input(prompt, title) - - def get_choice_input(self, prompt, title, choices): - return None - - def get_open_filename_input(self, prompt, ext): - return get_text_line_input(prompt, "Open File") - - def get_save_filename_input(self, prompt, ext, default_name): - return get_text_line_input(title, "Save File") +# Binary Ninja components +import _binaryninjacore as core +from .enums import * +from .databuffer import * +from .filemetadata import * +from .fileaccessor import * +from .binaryview import * +from .transform import * +from .architecture import * +from .basicblock import * +from .function import * +from .log import * +from .lowlevelil import * +from .types import * +from .functionrecognizer import * +from .update import * +from .plugin import * +from .callingconvention import * +from .platform import * +from .demangle import * +from .mainthread import * +from .interaction import * +from .lineardisassembly import * +from .undoaction import * +from .highlight import * +from .scriptingprovider import * - def get_directory_name_input(self, prompt, default_name): - return get_text_line_input(title, "Select Directory") - def get_form_input(self, fields, title): - return False +def shutdown(): + core.BNShutdown() - def show_message_box(self, title, text, buttons, icon): - return core.CancelButton -class _DestructionCallbackHandler: +class _DestructionCallbackHandler(object): def __init__(self): self._cb = core.BNObjectDestructionCallbacks() self._cb.context = 0 @@ -10946,436 +69,11 @@ class _DestructionCallbackHandler: def destruct_function(self, ctxt, func): Function._unregister(func) -_destruct_callbacks = _DestructionCallbackHandler() - -def LLIL_TEMP(n): - return n | 0x80000000 - -def LLIL_REG_IS_TEMP(n): - return (n & 0x80000000) != 0 - -def LLIL_GET_TEMP_REG_INDEX(n): - return n & 0x7fffffff - -def shutdown(): - core.BNShutdown() - -def log(level, text): - """ - ``log`` writes messages to the log console for the given log level. - - ============ ======== ======================================================================= - LogLevelName LogLevel Description - ============ ======== ======================================================================= - DebugLog 0 Logs debuging information messages to the console. - InfoLog 1 Logs general information messages to the console. - WarningLog 2 Logs message to console with **Warning** icon. - ErrorLog 3 Logs message to console with **Error** icon, focusing the error console. - AlertLog 4 Logs message to pop up window. - ============ ======== ======================================================================= - - :param LogLevel level: Log level to use - :param str text: message to print - :rtype: None - """ - core.BNLog(level, "%s", str(text)) - -def log_debug(text): - """ - ``log_debug`` Logs debuging information messages to the console. - - :param str text: message to print - :rtype: None - :Example: - - >>> log_to_stdout(core.DebugLog) - >>> log_debug("Hotdogs!") - Hotdogs! - """ - core.BNLogDebug("%s", str(text)) - -def log_info(text): - """ - ``log_info`` Logs general information messages to the console. - - :param str text: message to print - :rtype: None - :Example: - - >>> log_info("Saucisson!") - Saucisson! - >>> - """ - core.BNLogInfo("%s", str(text)) - -def log_warn(text): - """ - ``log_warn`` Logs message to console, if run through the GUI it logs with **Warning** icon. - - :param str text: message to print - :rtype: None - :Example: - - >>> log_to_stdout(core.DebugLog) - >>> log_info("Chilidogs!") - Chilidogs! - >>> - """ - core.BNLogWarn("%s", str(text)) - -def log_error(text): - """ - ``log_error`` Logs message to console, if run through the GUI it logs with **Error** icon, focusing the error console. - - :param str text: message to print - :rtype: None - :Example: - - >>> log_to_stdout(core.DebugLog) - >>> log_error("Spanferkel!") - Spanferkel! - >>> - """ - core.BNLogError("%s", str(text)) - -def log_alert(text): - """ - ``log_alert`` Logs message console and to a pop up window if run through the GUI. - - :param str text: message to print - :rtype: None - :Example: - - >>> log_to_stdout(core.DebugLog) - >>> log_alert("Kielbasa!") - Kielbasa! - >>> - """ - core.BNLogAlert("%s", str(text)) - -def log_to_stdout(min_level): - """ - ``log_to_stdout`` redirects minimum log level to standard out. - - :param int min_level: minimum level to log to - :rtype: None - :Example: - - >>> log_debug("Hotdogs!") - >>> log_to_stdout(core.DebugLog) - >>> log_debug("Hotdogs!") - Hotdogs! - >>> - """ - core.BNLogToStdout(min_level) - -def log_to_stderr(min_level): - """ - ``log_to_stderr`` redirects minimum log level to standard error. - :param int min_level: minimum level to log to - :rtype: None - """ - core.BNLogToStderr(min_level) - -def log_to_file(min_level, path, append = False): - """ - ``log_to_file`` redirects minimum log level to a file named ``path``, optionally appending rather than overwritting. - - :param int min_level: minimum level to log to - :param str path: path to log to - :param bool append: optional flag for specifying appending. True = append, False = overwrite. - :rtype: None - """ - core.BNLogToFile(min_level, str(path), append) - -def close_logs(): - """ - ``close_logs`` close all log files. - - :rtype: None - """ - core.BNCloseLogs() - -def escape_string(text): - return DataBuffer(text).escape() - -def unescape_string(text): - return DataBuffer(text).unescape() - -def preprocess_source(source, filename = None, include_dirs = []): - """ - ``preprocess_source`` run the C preprocessor on the given source or source filename. - - :param str source: source to preprocess - :param str filename: optional filename to preprocess - :param list(str) include_dirs: list of string directorires to use as include directories. - :return: returns a tuple of (preprocessed_source, error_string) - :rtype: tuple(str,str) - :Example: - - >>> source = "#define TEN 10\\nint x[TEN];\\n" - >>> preprocess_source(source) - ('#line 1 "input"\\n\\n#line 2 "input"\\n int x [ 10 ] ;\\n', '') - >>> - """ - if filename is None: - filename = "input" - dir_buf = (ctypes.c_char_p * len(include_dirs))() - for i in xrange(0, len(include_dirs)): - dir_buf[i] = str(include_dirs[i]) - output = ctypes.c_char_p() - errors = ctypes.c_char_p() - result = core.BNPreprocessSource(source, filename, output, errors, dir_buf, len(include_dirs)) - output_str = output.value - error_str = errors.value - core.BNFreeString(ctypes.cast(output, ctypes.POINTER(ctypes.c_byte))) - core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) - if result: - return (output_str, error_str) - return (None, error_str) - -def are_auto_updates_enabled(): - """ - ``are_auto_updates_enabled`` queries if auto updates are enabled. - - :return: boolean True if auto updates are enabled. False if they are disabled. - :rtype: bool - """ - return core.BNAreAutoUpdatesEnabled() - -def set_auto_updates_enabled(enabled): - """ - ``set_auto_updates_enabled`` sets auto update enabled status. - - :param bool enabled: True to enable update, Flase to disable updates. - :rtype: None - """ - core.BNSetAutoUpdatesEnabled(enabled) - -def get_time_since_last_update_check(): - """ - ``get_time_since_last_update_check`` returns the time stamp for the last time updates were checked. - - :return: time stacmp for last update check - :rtype: int - """ - return core.BNGetTimeSinceLastUpdateCheck() - -def updates_checked(): - core.BNUpdatesChecked() - -def get_qualified_name(names): - """ - ``get_qualified_name`` gets a qualified name for the provied name list. - - :param list(str) names: name list to qualify - :return: a qualified name - :rtype: str - :Example: - - >>> type, name = demangle_ms(Architecture["x86_64"], "?testf@Foobar@@SA?AW4foo@1@W421@@Z") - >>> get_qualified_name(name) - 'Foobar::testf' - >>> - """ - return "::".join(names) - -def demangle_ms(arch, mangled_name): - """ - ``demangle_ms`` demangles a mangled Microsoft Visual Studio C++ name to a Type object. - - :param Architecture arch: Architecture for the symbol. Required for pointer and integer sizes. - :param str mangled_name: a mangled Microsoft Visual Studio C++ name - :return: returns a Type object for the mangled name - :rtype: Type - :Example: - - >>> demangle_ms(Architecture["x86_64"], "?testf@Foobar@@SA?AW4foo@1@W421@@Z") - (<type: public: static enum Foobar::foo __cdecl (enum Foobar::foo)>, ['Foobar', 'testf']) - >>> - """ - handle = ctypes.POINTER(core.BNType)() - outName = ctypes.POINTER(ctypes.c_char_p)() - outSize = ctypes.c_ulonglong() - names = [] - if core.BNDemangleMS(arch.handle, mangled_name, ctypes.byref(handle), ctypes.byref(outName), ctypes.byref(outSize)): - for i in xrange(outSize.value): - names.append(outName[i]) - #core.BNFreeDemangledName(outName.value, outSize.value) - return (Type(handle), names) - return (None, mangledName) - - -def demangle_gnu3(arch, mangled_name): - handle = ctypes.POINTER(core.BNType)() - outName = ctypes.POINTER(ctypes.c_char_p)() - outSize = ctypes.c_ulonglong() - names = [] - if core.BNDemangleGNU3(arch.handle, mangled_name, ctypes.byref(handle), ctypes.byref(outName), ctypes.byref(outSize)): - for i in xrange(outSize.value): - names.append(outName[i]) - #core.BNFreeDemangledName(outName.value, outSize.value) - if not handle: - return (None, names) - return (Type(handle), names) - return (None, mangled_name) - - -_output_to_log = False -def redirect_output_to_log(): - global _output_to_log - _output_to_log = True - -class _ThreadActionContext(object): - _actions = [] - - def __init__(self, func): - self.func = func - self.interpreter = None - if "value" in dir(PythonScriptingInstance._interpreter): - self.interpreter = PythonScriptingInstance._interpreter.value - self.__class__._actions.append(self) - self.callback = ctypes.CFUNCTYPE(None, ctypes.c_void_p)(lambda ctxt: self.execute()) - - def execute(self): - old_interpreter = None - if "value" in dir(PythonScriptingInstance._interpreter): - old_interpreter = PythonScriptingInstance._interpreter.value - PythonScriptingInstance._interpreter.value = self.interpreter - try: - self.func() - finally: - PythonScriptingInstance._interpreter.value = old_interpreter - self.__class__._actions.remove(self) - -def execute_on_main_thread(func): - action = _ThreadActionContext(func) - obj = core.BNExecuteOnMainThread(0, action.callback) - if obj: - return MainThreadAction(obj) - return None - -def execute_on_main_thread_and_wait(func): - action = _ThreadActionContext(func) - core.BNExecuteOnMainThreadAndWait(0, action.callback) - -def worker_enqueue(func): - action = _ThreadActionContext(func) - core.BNWorkerEnqueue(0, action.callback) - -def worker_priority_enqueue(func): - action = _ThreadActionContext(func) - core.BNWorkerPriorityEnqueue(0, action.callback) - -def worker_interactive_enqueue(func): - action = _ThreadActionContext(func) - core.BNWorkerInteractiveEnqueue(0, action.callback) - -def get_worker_thread_count(): - return core.BNGetWorkerThreadCount() - -def set_worker_thread_count(count): - core.BNSetWorkerThreadCount(count) - -def markdown_to_html(contents): - return core.BNMarkdownToHTML(contents) - -def show_plain_text_report(title, contents): - core.BNShowPlainTextReport(None, title, contents) - -def show_markdown_report(title, contents, plaintext = ""): - core.BNShowMarkdownReport(None, title, contents, plaintext) - -def show_html_report(title, contents, plaintext = ""): - core.BNShowHTMLReport(None, title, contents, plaintext) - -def get_text_line_input(prompt, title): - value = ctypes.c_char_p() - if not core.BNGetTextLineInput(value, prompt, title): - return None - result = value.value - core.BNFreeString(ctypes.cast(value, ctypes.POINTER(ctypes.c_byte))) - return result - -def get_int_input(prompt, title): - value = ctypes.c_longlong() - if not core.BNGetIntegerInput(value, prompt, title): - return None - return value.value - -def get_address_input(prompt, title): - value = ctypes.c_ulonglong() - if not core.BNGetAddressInput(value, prompt, title, None, 0): - return None - return value.value - -def get_choice_input(prompt, title, choices): - choice_buf = (ctypes.c_char_p * len(choices))() - for i in xrange(0, len(choices)): - choice_buf[i] = str(choices[i]) - value = ctypes.c_ulonglong() - if not core.BNGetChoiceInput(value, prompt, title, choice_buf, len(choices)): - return None - return value.value - -def get_open_filename_input(prompt, ext = ""): - value = ctypes.c_char_p() - if not core.BNGetOpenFileNameInput(value, prompt, ext): - return None - result = value.value - core.BNFreeString(ctypes.cast(value, ctypes.POINTER(ctypes.c_byte))) - return result - -def get_save_filename_input(prompt, ext = "", default_name = ""): - value = ctypes.c_char_p() - if not core.BNGetSaveFileNameInput(value, prompt, ext, default_name): - return None - result = value.value - core.BNFreeString(ctypes.cast(value, ctypes.POINTER(ctypes.c_byte))) - return result - -def get_directory_name_input(prompt, default_name = ""): - value = ctypes.c_char_p() - if not core.BNGetDirectoryNameInput(value, prompt, default_name): - return None - result = value.value - core.BNFreeString(ctypes.cast(value, ctypes.POINTER(ctypes.c_byte))) - return result - -def get_form_input(fields, title): - value = (core.BNFormInputField * len(fields))() - for i in xrange(0, len(fields)): - if isinstance(fields[i], str): - LabelField(fields[i])._fill_core_struct(value[i]) - elif fields[i] is None: - SeparatorField()._fill_core_struct(value[i]) - else: - fields[i]._fill_core_struct(value[i]) - if not core.BNGetFormInput(value, len(fields), title): - return False - for i in xrange(0, len(fields)): - if not (isinstance(fields[i], str) or (fields[i] is None)): - fields[i]._get_result(value[i]) - core.BNFreeFormInputResults(value, len(fields)) - return True - -def show_message_box(title, text, buttons = core.OKButtonSet, icon = core.InformationIcon): - return core.BNShowMessageBox(title, text, buttons, icon) +_destruct_callbacks = _DestructionCallbackHandler() bundled_plugin_path = core.BNGetBundledPluginDirectory() user_plugin_path = core.BNGetUserPluginDirectory() core_version = core.BNGetVersionString() core_build_id = core.BNGetBuildId() - -# Ensure all enumeration constants from the core are exposed by this module -for name in core.all_enum_values: - globals()[name] = core.all_enum_values[name] - -PythonScriptingProvider().register() - -# Wrap stdin/stdout/stderr for Python scripting provider implementation -sys.stdin = _PythonScriptingInstanceInput(sys.stdin) -sys.stdout = _PythonScriptingInstanceOutput(sys.stdout, False) -sys.stderr = _PythonScriptingInstanceOutput(sys.stderr, True) diff --git a/python/architecture.py b/python/architecture.py new file mode 100644 index 00000000..d67f00a6 --- /dev/null +++ b/python/architecture.py @@ -0,0 +1,1693 @@ +# 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 traceback +import ctypes +import abc + +# Binary Ninja components +import _binaryninjacore as core +from enums import (Endianness, ImplicitRegisterExtend, BranchType, + InstructionTextTokenType, LowLevelILFlagCondition, FlagRole) +import startup +import function +import lowlevelil +import callingconvention +import platform +import log +import databuffer +import types + + +class _ArchitectureMetaClass(type): + @property + def list(self): + startup._init_plugins() + count = ctypes.c_ulonglong() + archs = core.BNGetArchitectureList(count) + result = [] + for i in xrange(0, count.value): + result.append(Architecture(archs[i])) + core.BNFreeArchitectureList(archs) + return result + + def __iter__(self): + startup._init_plugins() + count = ctypes.c_ulonglong() + archs = core.BNGetArchitectureList(count) + try: + for i in xrange(0, count.value): + yield Architecture(archs[i]) + finally: + core.BNFreeArchitectureList(archs) + + def __getitem__(cls, name): + startup._init_plugins() + arch = core.BNGetArchitectureByName(name) + if arch is None: + raise KeyError("'%s' is not a valid architecture" % str(name)) + return Architecture(arch) + + def register(cls): + startup._init_plugins() + if cls.name is None: + raise ValueError("architecture 'name' is not defined") + arch = cls() + cls._registered_cb = arch._cb + arch.handle = core.BNRegisterArchitecture(cls.name, arch._cb) + + def __setattr__(self, name, value): + try: + type.__setattr__(self, name, value) + except AttributeError: + raise AttributeError("attribute '%s' is read only" % name) + + +class Architecture(object): + """ + ``class Architecture`` is the parent class for all CPU architectures. Subclasses of Architecture implement assembly, + disassembly, IL lifting, and patching. + + ``class Architecture`` has a ``__metaclass__`` with the additional methods ``register``, and supports + iteration:: + + >>> #List the architectures + >>> list(Architecture) + [<arch: aarch64>, <arch: armv7>, <arch: armv7eb>, <arch: mipsel32>, <arch: mips32>, <arch: powerpc>, + <arch: x86>, <arch: x86_64>] + >>> #Register a new Architecture + >>> class MyArch(Architecture): + ... name = "MyArch" + ... + >>> MyArch.register() + >>> list(Architecture) + [<arch: aarch64>, <arch: armv7>, <arch: armv7eb>, <arch: mipsel32>, <arch: mips32>, <arch: powerpc>, + <arch: x86>, <arch: x86_64>, <arch: MyArch>] + >>> + + For the purposes of this documentation the variable ``arch`` will be used in the following context :: + + >>> from binaryninja import * + >>> arch = Architecture['x86'] + """ + name = None + endianness = Endianness.LittleEndian + address_size = 8 + default_int_size = 4 + max_instr_length = 16 + opcode_display_length = 8 + regs = {} + stack_pointer = None + link_reg = None + flags = [] + flag_write_types = [] + flag_roles = {} + flags_required_for_flag_condition = {} + flags_written_by_flag_write_type = {} + __metaclass__ = _ArchitectureMetaClass + next_address = 0 + + def __init__(self, handle=None): + if handle is not None: + self.handle = core.handle_of_type(handle, core.BNArchitecture) + self.__dict__["name"] = core.BNGetArchitectureName(self.handle) + self.__dict__["endianness"] = Endianness(core.BNGetArchitectureEndianness(self.handle)).name + self.__dict__["address_size"] = core.BNGetArchitectureAddressSize(self.handle) + self.__dict__["default_int_size"] = core.BNGetArchitectureDefaultIntegerSize(self.handle) + self.__dict__["max_instr_length"] = core.BNGetArchitectureMaxInstructionLength(self.handle) + self.__dict__["opcode_display_length"] = core.BNGetArchitectureOpcodeDisplayLength(self.handle) + self.__dict__["stack_pointer"] = core.BNGetArchitectureRegisterName(self.handle, + core.BNGetArchitectureStackPointerRegister(self.handle)) + self.__dict__["link_reg"] = core.BNGetArchitectureRegisterName(self.handle, + core.BNGetArchitectureLinkRegister(self.handle)) + + count = ctypes.c_ulonglong() + regs = core.BNGetAllArchitectureRegisters(self.handle, count) + self.__dict__["regs"] = {} + for i in xrange(0, count.value): + name = core.BNGetArchitectureRegisterName(self.handle, regs[i]) + info = core.BNGetArchitectureRegisterInfo(self.handle, regs[i]) + full_width_reg = core.BNGetArchitectureRegisterName(self.handle, info.fullWidthRegister) + self.regs[name] = function.RegisterInfo(full_width_reg, info.size, info.offset, + ImplicitRegisterExtend(info.extend).name, regs[i]) + core.BNFreeRegisterList(regs) + + count = ctypes.c_ulonglong() + flags = core.BNGetAllArchitectureFlags(self.handle, count) + self._flags = {} + self._flags_by_index = {} + self.__dict__["flags"] = [] + for i in xrange(0, count.value): + name = core.BNGetArchitectureFlagName(self.handle, flags[i]) + self._flags[name] = flags[i] + self._flags_by_index[flags[i]] = name + self.flags.append(name) + core.BNFreeRegisterList(flags) + + count = ctypes.c_ulonglong() + types = core.BNGetAllArchitectureFlagWriteTypes(self.handle, count) + self._flag_write_types = {} + self._flag_write_types_by_index = {} + self.__dict__["flag_write_types"] = [] + for i in xrange(0, count.value): + name = core.BNGetArchitectureFlagWriteTypeName(self.handle, types[i]) + self._flag_write_types[name] = types[i] + self._flag_write_types_by_index[types[i]] = name + self.flag_write_types.append(name) + core.BNFreeRegisterList(types) + + self._flag_roles = {} + self.__dict__["flag_roles"] = {} + for flag in self.__dict__["flags"]: + role = FlagRole(core.BNGetArchitectureFlagRole(self.handle, self._flags[flag])) + self.__dict__["flag_roles"][flag] = role + self._flag_roles[self._flags[flag]] = role + + self._flags_required_for_flag_condition = {} + self.__dict__["flags_required_for_flag_condition"] = {} + for cond in LowLevelILFlagCondition: + count = ctypes.c_ulonglong() + flags = core.BNGetArchitectureFlagsRequiredForFlagCondition(self.handle, cond, count) + flag_indexes = [] + flag_names = [] + for i in xrange(0, count.value): + flag_indexes.append(flags[i]) + flag_names.append(self._flags_by_index[flags[i]]) + core.BNFreeRegisterList(flags) + self._flags_required_for_flag_condition[cond] = flag_indexes + self.__dict__["flags_required_for_flag_condition"][cond] = flag_names + + self._flags_written_by_flag_write_type = {} + self.__dict__["flags_written_by_flag_write_type"] = {} + for write_type in self.flag_write_types: + count = ctypes.c_ulonglong() + flags = core.BNGetArchitectureFlagsWrittenByFlagWriteType(self.handle, + self._flag_write_types[write_type], count) + flag_indexes = [] + flag_names = [] + for i in xrange(0, count.value): + flag_indexes.append(flags[i]) + flag_names.append(self._flags_by_index[flags[i]]) + core.BNFreeRegisterList(flags) + self._flags_written_by_flag_write_type[self._flag_write_types[write_type]] = flag_indexes + self.__dict__["flags_written_by_flag_write_type"][write_type] = flag_names + else: + startup._init_plugins() + + if self.__class__.opcode_display_length > self.__class__.max_instr_length: + self.__class__.opcode_display_length = self.__class__.max_instr_length + + self._cb = core.BNCustomArchitecture() + self._cb.context = 0 + self._cb.init = self._cb.init.__class__(self._init) + self._cb.getEndianness = self._cb.getEndianness.__class__(self._get_endianness) + self._cb.getAddressSize = self._cb.getAddressSize.__class__(self._get_address_size) + self._cb.getDefaultIntegerSize = self._cb.getDefaultIntegerSize.__class__(self._get_default_integer_size) + self._cb.getMaxInstructionLength = self._cb.getMaxInstructionLength.__class__(self._get_max_instruction_length) + self._cb.getOpcodeDisplayLength = self._cb.getOpcodeDisplayLength.__class__(self._get_opcode_display_length) + self._cb.getAssociatedArchitectureByAddress = \ + self._cb.getAssociatedArchitectureByAddress.__class__(self._get_associated_arch_by_address) + self._cb.getInstructionInfo = self._cb.getInstructionInfo.__class__(self._get_instruction_info) + self._cb.getInstructionText = self._cb.getInstructionText.__class__(self._get_instruction_text) + self._cb.freeInstructionText = self._cb.freeInstructionText.__class__(self._free_instruction_text) + self._cb.getInstructionLowLevelIL = self._cb.getInstructionLowLevelIL.__class__( + self._get_instruction_low_level_il) + self._cb.getRegisterName = self._cb.getRegisterName.__class__(self._get_register_name) + self._cb.getFlagName = self._cb.getFlagName.__class__(self._get_flag_name) + self._cb.getFlagWriteTypeName = self._cb.getFlagWriteTypeName.__class__(self._get_flag_write_type_name) + self._cb.getFullWidthRegisters = self._cb.getFullWidthRegisters.__class__(self._get_full_width_registers) + self._cb.getAllRegisters = self._cb.getAllRegisters.__class__(self._get_all_registers) + self._cb.getAllFlags = self._cb.getAllRegisters.__class__(self._get_all_flags) + self._cb.getAllFlagWriteTypes = self._cb.getAllRegisters.__class__(self._get_all_flag_write_types) + self._cb.getFlagRole = self._cb.getFlagRole.__class__(self._get_flag_role) + self._cb.getFlagsRequiredForFlagCondition = self._cb.getFlagsRequiredForFlagCondition.__class__( + self._get_flags_required_for_flag_condition) + self._cb.getFlagsWrittenByFlagWriteType = self._cb.getFlagsWrittenByFlagWriteType.__class__( + self._get_flags_written_by_flag_write_type) + self._cb.getFlagWriteLowLevelIL = self._cb.getFlagWriteLowLevelIL.__class__( + self._get_flag_write_low_level_il) + self._cb.getFlagConditionLowLevelIL = self._cb.getFlagConditionLowLevelIL.__class__( + self._get_flag_condition_low_level_il) + self._cb.freeRegisterList = self._cb.freeRegisterList.__class__(self._free_register_list) + self._cb.getRegisterInfo = self._cb.getRegisterInfo.__class__(self._get_register_info) + self._cb.getStackPointerRegister = self._cb.getStackPointerRegister.__class__( + self._get_stack_pointer_register) + self._cb.getLinkRegister = self._cb.getLinkRegister.__class__(self._get_link_register) + self._cb.assemble = self._cb.assemble.__class__(self._assemble) + self._cb.isNeverBranchPatchAvailable = self._cb.isNeverBranchPatchAvailable.__class__( + self._is_never_branch_patch_available) + self._cb.isAlwaysBranchPatchAvailable = self._cb.isAlwaysBranchPatchAvailable.__class__( + self._is_always_branch_patch_available) + self._cb.isInvertBranchPatchAvailable = self._cb.isInvertBranchPatchAvailable.__class__( + self._is_invert_branch_patch_available) + self._cb.isSkipAndReturnZeroPatchAvailable = self._cb.isSkipAndReturnZeroPatchAvailable.__class__( + self._is_skip_and_return_zero_patch_available) + self._cb.isSkipAndReturnValuePatchAvailable = self._cb.isSkipAndReturnValuePatchAvailable.__class__( + self._is_skip_and_return_value_patch_available) + self._cb.convertToNop = self._cb.convertToNop.__class__(self._convert_to_nop) + self._cb.alwaysBranch = self._cb.alwaysBranch.__class__(self._always_branch) + self._cb.invertBranch = self._cb.invertBranch.__class__(self._invert_branch) + self._cb.skipAndReturnValue = self._cb.skipAndReturnValue.__class__(self._skip_and_return_value) + + self._all_regs = {} + self._full_width_regs = {} + self._regs_by_index = {} + self.__dict__["regs"] = self.__class__.regs + reg_index = 0 + for reg in self.regs: + info = self.regs[reg] + if reg not in self._all_regs: + self._all_regs[reg] = reg_index + self._regs_by_index[reg_index] = reg + self.regs[reg].index = reg_index + reg_index += 1 + if info.full_width_reg not in self._all_regs: + self._all_regs[info.full_width_reg] = reg_index + self._regs_by_index[reg_index] = info.full_width_reg + self.regs[info.full_width_reg].index = reg_index + reg_index += 1 + if info.full_width_reg not in self._full_width_regs: + self._full_width_regs[info.full_width_reg] = self._all_regs[info.full_width_reg] + + self._flags = {} + self._flags_by_index = {} + self.__dict__["flags"] = self.__class__.flags + flag_index = 0 + for flag in self.__class__.flags: + if flag not in self._flags: + self._flags[flag] = flag_index + self._flags_by_index[flag_index] = flag + flag_index += 1 + + self._flag_write_types = {} + self._flag_write_types_by_index = {} + self.__dict__["flag_write_types"] = self.__class__.flag_write_types + write_type_index = 0 + for write_type in self.__class__.flag_write_types: + if write_type not in self._flag_write_types: + self._flag_write_types[write_type] = write_type_index + self._flag_write_types_by_index[write_type_index] = write_type + write_type_index += 1 + + self._flag_roles = {} + self.__dict__["flag_roles"] = self.__class__.flag_roles + for flag in self.__class__.flag_roles: + role = self.__class__.flag_roles[flag] + if isinstance(role, str): + role = FlagRole[role] + self._flag_roles[self._flags[flag]] = role + + self._flags_required_for_flag_condition = {} + self.__dict__["flags_required_for_flag_condition"] = self.__class__.flags_required_for_flag_condition + for cond in self.__class__.flags_required_for_flag_condition: + flags = [] + for flag in self.__class__.flags_required_for_flag_condition[cond]: + flags.append(self._flags[flag]) + self._flags_required_for_flag_condition[cond] = flags + + self._flags_written_by_flag_write_type = {} + self.__dict__["flags_written_by_flag_write_type"] = self.__class__.flags_written_by_flag_write_type + for write_type in self.__class__.flags_written_by_flag_write_type: + flags = [] + for flag in self.__class__.flags_written_by_flag_write_type[write_type]: + flags.append(self._flags[flag]) + self._flags_written_by_flag_write_type[self._flag_write_types[write_type]] = flags + + self._pending_reg_lists = {} + self._pending_token_lists = {} + + @property + def full_width_regs(self): + """List of full width register strings (read-only)""" + count = ctypes.c_ulonglong() + regs = core.BNGetFullWidthArchitectureRegisters(self.handle, count) + result = [] + for i in xrange(0, count.value): + result.append(core.BNGetArchitectureRegisterName(self.handle, regs[i])) + core.BNFreeRegisterList(regs) + return result + + @property + def calling_conventions(self): + """Dict of CallingConvention objects (read-only)""" + count = ctypes.c_ulonglong() + cc = core.BNGetArchitectureCallingConventions(self.handle, count) + result = {} + for i in xrange(0, count.value): + obj = callingconvention.CallingConvention(None, core.BNNewCallingConventionReference(cc[i])) + result[obj.name] = obj + core.BNFreeCallingConventionList(cc, count) + return result + + @property + def standalone_platform(self): + """Architecture standalone platform (read-only)""" + pl = core.BNGetArchitectureStandalonePlatform(self.handle) + return platform.Platform(self, pl) + + def __setattr__(self, name, value): + if ((name == "name") or (name == "endianness") or (name == "address_size") or + (name == "default_int_size") or (name == "regs") or (name == "get_max_instruction_length")): + raise AttributeError("attribute '%s' is read only" % name) + else: + try: + object.__setattr__(self, name, value) + except AttributeError: + raise AttributeError("attribute '%s' is read only" % name) + + def __repr__(self): + return "<arch: %s>" % self.name + + def _init(self, ctxt, handle): + self.handle = handle + + def _get_endianness(self, ctxt): + try: + return self.__class__.endianness + except: + log.log_error(traceback.format_exc()) + return Endianness.LittleEndian + + def _get_address_size(self, ctxt): + try: + return self.__class__.address_size + except: + log.log_error(traceback.format_exc()) + return 8 + + def _get_default_integer_size(self, ctxt): + try: + return self.__class__.default_int_size + except: + log.log_error(traceback.format_exc()) + return 4 + + def _get_max_instruction_length(self, ctxt): + try: + return self.__class__.max_instr_length + except: + log.log_error(traceback.format_exc()) + return 16 + + def _get_opcode_display_length(self, ctxt): + try: + return self.__class__.opcode_display_length + except: + log.log_error(traceback.format_exc()) + return 8 + + def _get_associated_arch_by_address(self, ctxt, addr): + try: + result, new_addr = self.perform_get_associated_arch_by_address(addr[0]) + addr[0] = new_addr + return ctypes.cast(result.handle, ctypes.c_void_p).value + except: + log.log_error(traceback.format_exc()) + return ctypes.cast(self.handle, ctypes.c_void_p).value + + def _get_instruction_info(self, ctxt, data, addr, max_len, result): + try: + buf = ctypes.create_string_buffer(max_len) + ctypes.memmove(buf, data, max_len) + info = self.perform_get_instruction_info(buf.raw, addr) + if info is None: + return False + result[0].length = info.length + result[0].branchDelay = info.branch_delay + result[0].branchCount = len(info.branches) + for i in xrange(0, len(info.branches)): + if isinstance(info.branches[i].type, str): + result[0].branchType[i] = BranchType[info.branches[i].type] + else: + result[0].branchType[i] = info.branches[i].type + result[0].branchTarget[i] = info.branches[i].target + if info.branches[i].arch is None: + result[0].branchArch[i] = None + else: + result[0].branchArch[i] = info.branches[i].arch.handle + return True + except (KeyError, OSError): + log.log_error(traceback.format_exc()) + return False + + def _get_instruction_text(self, ctxt, data, addr, length, result, count): + try: + buf = ctypes.create_string_buffer(length[0]) + ctypes.memmove(buf, data, length[0]) + info = self.perform_get_instruction_text(buf.raw, addr) + if info is None: + return False + tokens = info[0] + length[0] = info[1] + count[0] = len(tokens) + token_buf = (core.BNInstructionTextToken * len(tokens))() + for i in xrange(0, len(tokens)): + if isinstance(tokens[i].type, str): + token_buf[i].type = InstructionTextTokenType[tokens[i].type] + else: + token_buf[i].type = tokens[i].type + token_buf[i].text = tokens[i].text + token_buf[i].value = tokens[i].value + token_buf[i].size = tokens[i].size + token_buf[i].operand = tokens[i].operand + result[0] = token_buf + ptr = ctypes.cast(token_buf, ctypes.c_void_p) + self._pending_token_lists[ptr.value] = (ptr.value, token_buf) + return True + except (KeyError, OSError): + log.log_error(traceback.format_exc()) + return False + + def _free_instruction_text(self, tokens, count): + try: + buf = ctypes.cast(tokens, ctypes.c_void_p) + if buf.value not in self._pending_token_lists: + raise ValueError("freeing token list that wasn't allocated") + del self._pending_token_lists[buf.value] + except KeyError: + log.log_error(traceback.format_exc()) + + def _get_instruction_low_level_il(self, ctxt, data, addr, length, il): + try: + buf = ctypes.create_string_buffer(length[0]) + ctypes.memmove(buf, data, length[0]) + result = self.perform_get_instruction_low_level_il(buf.raw, addr, + lowlevelil.LowLevelILFunction(self, core.BNNewLowLevelILFunctionReference(il))) + if result is None: + return False + length[0] = result + return True + except OSError: + log.log_error(traceback.format_exc()) + return False + + def _get_register_name(self, ctxt, reg): + try: + if reg in self._regs_by_index: + return core.BNAllocString(self._regs_by_index[reg]) + return core.BNAllocString("") + except (KeyError, OSError): + log.log_error(traceback.format_exc()) + return core.BNAllocString("") + + def _get_flag_name(self, ctxt, flag): + try: + if flag in self._flags_by_index: + return core.BNAllocString(self._flags_by_index[flag]) + return core.BNAllocString("") + except (KeyError, OSError): + log.log_error(traceback.format_exc()) + return core.BNAllocString("") + + def _get_flag_write_type_name(self, ctxt, write_type): + try: + if write_type in self._flag_write_types_by_index: + return core.BNAllocString(self._flag_write_types_by_index[write_type]) + return core.BNAllocString("") + except (KeyError, OSError): + log.log_error(traceback.format_exc()) + return core.BNAllocString("") + + def _get_full_width_registers(self, ctxt, count): + try: + regs = self._full_width_regs.values() + count[0] = len(regs) + reg_buf = (ctypes.c_uint * len(regs))() + for i in xrange(0, len(regs)): + reg_buf[i] = regs[i] + result = ctypes.cast(reg_buf, ctypes.c_void_p) + self._pending_reg_lists[result.value] = (result, reg_buf) + return result.value + except KeyError: + log.log_error(traceback.format_exc()) + count[0] = 0 + return None + + def _get_all_registers(self, ctxt, count): + try: + regs = self._regs_by_index.keys() + count[0] = len(regs) + reg_buf = (ctypes.c_uint * len(regs))() + for i in xrange(0, len(regs)): + reg_buf[i] = regs[i] + result = ctypes.cast(reg_buf, ctypes.c_void_p) + self._pending_reg_lists[result.value] = (result, reg_buf) + return result.value + except KeyError: + log.log_error(traceback.format_exc()) + count[0] = 0 + return None + + def _get_all_flags(self, ctxt, count): + try: + flags = self._flags_by_index.keys() + count[0] = len(flags) + flag_buf = (ctypes.c_uint * len(flags))() + for i in xrange(0, len(flags)): + flag_buf[i] = flags[i] + result = ctypes.cast(flag_buf, ctypes.c_void_p) + self._pending_reg_lists[result.value] = (result, flag_buf) + return result.value + except KeyError: + log.log_error(traceback.format_exc()) + count[0] = 0 + return None + + def _get_all_flag_write_types(self, ctxt, count): + try: + types = self._flag_write_types_by_index.keys() + count[0] = len(types) + type_buf = (ctypes.c_uint * len(types))() + for i in xrange(0, len(types)): + type_buf[i] = types[i] + result = ctypes.cast(type_buf, ctypes.c_void_p) + self._pending_reg_lists[result.value] = (result, type_buf) + return result.value + except KeyError: + log.log_error(traceback.format_exc()) + count[0] = 0 + return None + + def _get_flag_role(self, ctxt, flag): + try: + if flag in self._flag_roles: + return self._flag_roles[flag] + return FlagRole.SpecialFlagRole + except KeyError: + log.log_error(traceback.format_exc()) + return None + + def _get_flags_required_for_flag_condition(self, ctxt, cond, count): + try: + if cond in self._flags_required_for_flag_condition: + flags = self._flags_required_for_flag_condition[cond] + else: + flags = [] + count[0] = len(flags) + flag_buf = (ctypes.c_uint * len(flags))() + for i in xrange(0, len(flags)): + flag_buf[i] = flags[i] + result = ctypes.cast(flag_buf, ctypes.c_void_p) + self._pending_reg_lists[result.value] = (result, flag_buf) + return result.value + except KeyError: + log.log_error(traceback.format_exc()) + count[0] = 0 + return None + + def _get_flags_written_by_flag_write_type(self, ctxt, write_type, count): + try: + if write_type in self._flags_written_by_flag_write_type: + flags = self._flags_written_by_flag_write_type[write_type] + else: + flags = [] + count[0] = len(flags) + flag_buf = (ctypes.c_uint * len(flags))() + for i in xrange(0, len(flags)): + flag_buf[i] = flags[i] + result = ctypes.cast(flag_buf, ctypes.c_void_p) + self._pending_reg_lists[result.value] = (result, flag_buf) + return result.value + except (KeyError, OSError): + log.log_error(traceback.format_exc()) + count[0] = 0 + return None + + def _get_flag_write_low_level_il(self, ctxt, op, size, write_type, flag, operands, operand_count, il): + try: + write_type_name = None + if write_type != 0: + write_type_name = self._flag_write_types_by_index[write_type] + flag_name = self._flags_by_index[flag] + operand_list = [] + for i in xrange(operand_count): + if operands[i].constant: + operand_list.append(("const", operands[i].value)) + elif lowlevelil.LLIL_REG_IS_TEMP(operands[i].reg): + operand_list.append(("reg", operands[i].reg)) + else: + operand_list.append(("reg", self._regs_by_index[operands[i].reg])) + return self.perform_get_flag_write_low_level_il(op, size, write_type_name, flag_name, operand_list, + lowlevelil.LowLevelILFunction(self, core.BNNewLowLevelILFunctionReference(il))).index + except (KeyError, OSError): + log.log_error(traceback.format_exc()) + return False + + def _get_flag_condition_low_level_il(self, ctxt, cond, il): + try: + return self.perform_get_flag_condition_low_level_il(cond, + lowlevelil.LowLevelILFunction(self, core.BNNewLowLevelILFunctionReference(il))).index + except OSError: + log.log_error(traceback.format_exc()) + return 0 + + def _free_register_list(self, ctxt, regs): + try: + buf = ctypes.cast(regs, ctypes.c_void_p) + if buf.value not in self._pending_reg_lists: + raise ValueError("freeing register list that wasn't allocated") + del self._pending_reg_lists[buf.value] + except (ValueError, KeyError): + log.log_error(traceback.format_exc()) + + def _get_register_info(self, ctxt, reg, result): + try: + if reg not in self._regs_by_index: + result[0].fullWidthRegister = 0 + result[0].offset = 0 + result[0].size = 0 + result[0].extend = ImplicitRegisterExtend.NoExtend + return + info = self.__class__.regs[self._regs_by_index[reg]] + result[0].fullWidthRegister = self._all_regs[info.full_width_reg] + result[0].offset = info.offset + result[0].size = info.size + if isinstance(info.extend, str): + result[0].extend = ImplicitRegisterExtend[info.extend] + else: + result[0].extend = info.extend + except KeyError: + log.log_error(traceback.format_exc()) + result[0].fullWidthRegister = 0 + result[0].offset = 0 + result[0].size = 0 + result[0].extend = ImplicitRegisterExtend.NoExtend + + def _get_stack_pointer_register(self, ctxt): + try: + return self._all_regs[self.__class__.stack_pointer] + except KeyError: + log.log_error(traceback.format_exc()) + return 0 + + def _get_link_register(self, ctxt): + try: + if self.__class__.link_reg is None: + return 0xffffffff + return self._all_regs[self.__class__.link_reg] + except KeyError: + log.log_error(traceback.format_exc()) + return 0 + + def _assemble(self, ctxt, code, addr, result, errors): + try: + data, error_str = self.perform_assemble(code, addr) + errors[0] = core.BNAllocString(str(error_str)) + if data is None: + return False + data = str(data) + buf = ctypes.create_string_buffer(len(data)) + ctypes.memmove(buf, data, len(data)) + core.BNSetDataBufferContents(result, buf, len(data)) + return True + except: + log.log_error(traceback.format_exc()) + errors[0] = core.BNAllocString("Unhandled exception during assembly.\n") + return False + + def _is_never_branch_patch_available(self, ctxt, data, addr, length): + try: + buf = ctypes.create_string_buffer(length) + ctypes.memmove(buf, data, length) + return self.perform_is_never_branch_patch_available(buf.raw, addr) + except: + log.log_error(traceback.format_exc()) + return False + + def _is_always_branch_patch_available(self, ctxt, data, addr, length): + try: + buf = ctypes.create_string_buffer(length) + ctypes.memmove(buf, data, length) + return self.perform_is_always_branch_patch_available(buf.raw, addr) + except: + log.log_error(traceback.format_exc()) + return False + + def _is_invert_branch_patch_available(self, ctxt, data, addr, length): + try: + buf = ctypes.create_string_buffer(length) + ctypes.memmove(buf, data, length) + return self.perform_is_invert_branch_patch_available(buf.raw, addr) + except: + log.log_error(traceback.format_exc()) + return False + + def _is_skip_and_return_zero_patch_available(self, ctxt, data, addr, length): + try: + buf = ctypes.create_string_buffer(length) + ctypes.memmove(buf, data, length) + return self.perform_is_skip_and_return_zero_patch_available(buf.raw, addr) + except: + log.log_error(traceback.format_exc()) + return False + + def _is_skip_and_return_value_patch_available(self, ctxt, data, addr, length): + try: + buf = ctypes.create_string_buffer(length) + ctypes.memmove(buf, data, length) + return self.perform_is_skip_and_return_value_patch_available(buf.raw, addr) + except: + log.log_error(traceback.format_exc()) + return False + + def _convert_to_nop(self, ctxt, data, addr, length): + try: + buf = ctypes.create_string_buffer(length) + ctypes.memmove(buf, data, length) + result = self.perform_convert_to_nop(buf.raw, addr) + if result is None: + return False + result = str(result) + if len(result) > length: + result = result[0:length] + ctypes.memmove(data, result, len(result)) + return True + except: + log.log_error(traceback.format_exc()) + return False + + def _always_branch(self, ctxt, data, addr, length): + try: + buf = ctypes.create_string_buffer(length) + ctypes.memmove(buf, data, length) + result = self.perform_always_branch(buf.raw, addr) + if result is None: + return False + result = str(result) + if len(result) > length: + result = result[0:length] + ctypes.memmove(data, result, len(result)) + return True + except: + log.log_error(traceback.format_exc()) + return False + + def _invert_branch(self, ctxt, data, addr, length): + try: + buf = ctypes.create_string_buffer(length) + ctypes.memmove(buf, data, length) + result = self.perform_invert_branch(buf.raw, addr) + if result is None: + return False + result = str(result) + if len(result) > length: + result = result[0:length] + ctypes.memmove(data, result, len(result)) + return True + except: + log.log_error(traceback.format_exc()) + return False + + def _skip_and_return_value(self, ctxt, data, addr, length, value): + try: + buf = ctypes.create_string_buffer(length) + ctypes.memmove(buf, data, length) + result = self.perform_skip_and_return_value(buf.raw, addr, value) + if result is None: + return False + result = str(result) + if len(result) > length: + result = result[0:length] + ctypes.memmove(data, result, len(result)) + return True + except: + log.log_error(traceback.format_exc()) + return False + + def perform_get_associated_arch_by_address(self, addr): + return self, addr + + @abc.abstractmethod + def perform_get_instruction_info(self, data, addr): + """ + ``perform_get_instruction_info`` implements a method which interpretes the bytes passed in ``data`` as an + :py:Class:`InstructionInfo` object. The InstructionInfo object should have the length of the current instruction. + If the instruction is a branch instruction the method should add a branch of the proper type: + + ===================== =================================================== + BranchType Description + ===================== =================================================== + UnconditionalBranch Branch will always be taken + FalseBranch False branch condition + TrueBranch True branch condition + CallDestination Branch is a call instruction (Branch with Link) + FunctionReturn Branch returns from a function + SystemCall System call instruction + IndirectBranch Branch destination is a memory address or register + UnresolvedBranch Call instruction that isn't + ===================== =================================================== + + :param str data: bytes to decode + :param int addr: virtual address of the byte to be decoded + :return: a :py:class:`InstructionInfo` object containing the length and branche types for the given instruction + :rtype: InstructionInfo + """ + raise NotImplementedError + + @abc.abstractmethod + def perform_get_instruction_text(self, data, addr): + """ + ``perform_get_instruction_text`` implements a method which interpretes the bytes passed in ``data`` as a + list of :py:class:`InstructionTextToken` objects. + + :param str data: bytes to decode + :param int addr: virtual address of the byte to be decoded + :return: a tuple of list(InstructionTextToken) and length of instruction decoded + :rtype: tuple(list(InstructionTextToken), int) + """ + raise NotImplementedError + + @abc.abstractmethod + def perform_get_instruction_low_level_il(self, data, addr, il): + """ + ``perform_get_instruction_low_level_il`` implements a method to interpret the bytes passed in ``data`` to + low-level IL instructions. The il instructions must be appended to the :py:class:`LowLevelILFunction`. + + .. note:: Architecture subclasses should implement this method. + + :param str data: bytes to be interpreted as low-level IL instructions + :param int addr: virtual address of start of ``data`` + :param LowLevelILFunction il: LowLevelILFunction object to append LowLevelILExpr objects to + :rtype: None + """ + raise NotImplementedError + + @abc.abstractmethod + def perform_get_flag_write_low_level_il(self, op, size, write_type, flag, operands, il): + """ + .. note:: Architecture subclasses should implement this method. + .. warning:: This method should never be called directly. + + :param LowLevelILOperation op: + :param int size: + :param int write_type: + :param int flag: + :param list(int_or_str): + :param LowLevelILFunction il: + :rtype: LowLevelILExpr + """ + return il.unimplemented() + + @abc.abstractmethod + def perform_get_flag_condition_low_level_il(self, cond, il): + """ + .. note:: Architecture subclasses should implement this method. + .. warning:: This method should never be called directly. + + :param LowLevelILFlagCondition cond: + :param LowLevelILFunction il: + :rtype: LowLevelILExpr + """ + return il.unimplemented() + + @abc.abstractmethod + def perform_assemble(self, code, addr): + """ + ``perform_assemble`` implements a method to convert the string of assembly instructions ``code`` loaded at + virtual address ``addr`` to the byte representation of those instructions. This can be done by simply shelling + out to an assembler like yasm or llvm-mc, since this method isn't performance sensitive. + + .. note:: Architecture subclasses should implement this method. + .. note :: It is important that the assembler used accepts a syntax identical to the one emitted by the \ + disassembler. This will prevent confusing the user. + .. warning:: This method should never be called directly. + + :param str code: string representation of the instructions to be assembled + :param int addr: virtual address that the instructions will be loaded at + :return: the bytes for the assembled instructions or error string + :rtype: (a tuple of instructions and empty string) or (or None and error string) + """ + return None, "Architecture does not implement an assembler.\n" + + @abc.abstractmethod + def perform_is_never_branch_patch_available(self, data, addr): + """ + ``perform_is_never_branch_patch_available`` implements a check to determine if the instruction represented by + the bytes contained in ``data`` at address addr is a branch instruction that can be made to never branch. + + .. note:: Architecture subclasses should implement this method. + .. warning:: This method should never be called directly. + + :param str data: bytes to be checked + :param int addr: the virtual address of the instruction to be patched + :return: True if the instruction can be patched, False otherwise + :rtype: bool + """ + return False + + @abc.abstractmethod + def perform_is_always_branch_patch_available(self, data, addr): + """ + ``perform_is_always_branch_patch_available`` implements a check to determine if the instruction represented by + the bytes contained in ``data`` at address addr is a conditional branch that can be made unconditional. + + .. note:: Architecture subclasses should implement this method. + .. warning:: This method should never be called directly. + + :param str data: bytes to be checked + :param int addr: the virtual address of the instruction to be patched + :return: True if the instruction can be patched, False otherwise + :rtype: bool + """ + return False + + @abc.abstractmethod + def perform_is_invert_branch_patch_available(self, data, addr): + """ + ``perform_is_invert_branch_patch_available`` implements a check to determine if the instruction represented by + the bytes contained in ``data`` at address addr is a conditional branch which can be inverted. + + .. note:: Architecture subclasses should implement this method. + .. warning:: This method should never be called directly. + + :param int addr: the virtual address of the instruction to be patched + :return: True if the instruction can be patched, False otherwise + :rtype: bool + """ + return False + + @abc.abstractmethod + def perform_is_skip_and_return_zero_patch_available(self, data, addr): + """ + ``perform_is_skip_and_return_zero_patch_available`` implements a check to determine if the instruction represented by + the bytes contained in ``data`` at address addr is a *call-like* instruction which can made into instructions + that are equivilent to "return 0". For example if ``data`` was the x86 instruction ``call eax`` which could be + converted into ``xor eax,eax`` thus this function would return True. + + .. note:: Architecture subclasses should implement this method. + .. warning:: This method should never be called directly. + + :param str data: bytes to be checked + :param int addr: the virtual address of the instruction to be patched + :return: True if the instruction can be patched, False otherwise + :rtype: bool + """ + return False + + @abc.abstractmethod + def perform_is_skip_and_return_value_patch_available(self, data, addr): + """ + ``perform_is_skip_and_return_value_patch_available`` implements a check to determine if the instruction represented by + the bytes contained in ``data`` at address addr is a *call-like* instruction which can made into instructions + that are equivilent to "return 0". For example if ``data`` was the x86 instruction ``call 0xdeadbeef`` which could be + converted into ``mov eax, 42`` thus this function would return True. + + .. note:: Architecture subclasses should implement this method. + .. warning:: This method should never be called directly. + + :param str data: bytes to be checked + :param int addr: the virtual address of the instruction to be patched + :return: True if the instruction can be patched, False otherwise + :rtype: bool + """ + return False + + @abc.abstractmethod + def perform_convert_to_nop(self, data, addr): + """ + ``perform_convert_to_nop`` implements a method which returns a nop sequence of len(data) bytes long. + + .. note:: Architecture subclasses should implement this method. + .. warning:: This method should never be called directly. + + :param str data: bytes at virtual address ``addr`` + :param int addr: the virtual address of the instruction to be patched + :return: nop sequence of same length as ``data`` or None + :rtype: str or None + """ + return None + + @abc.abstractmethod + def perform_always_branch(self, data, addr): + """ + ``perform_always_branch`` implements a method which converts the branch represented by the bytes in ``data`` to + at ``addr`` to an unconditional branch. + + .. note:: Architecture subclasses should implement this method. + .. warning:: This method should never be called directly. + + :param str data: bytes to be checked + :param int addr: the virtual address of the instruction to be patched + :return: The bytes of the replacement unconditional branch instruction + :rtype: str + """ + return None + + @abc.abstractmethod + def perform_invert_branch(self, data, addr): + """ + ``perform_invert_branch`` implements a method which inverts the branch represented by the bytes in ``data`` to + at ``addr``. + + .. note:: Architecture subclasses should implement this method. + .. warning:: This method should never be called directly. + + :param str data: bytes to be checked + :param int addr: the virtual address of the instruction to be patched + :return: The bytes of the replacement unconditional branch instruction + :rtype: str + """ + return None + + @abc.abstractmethod + def perform_skip_and_return_value(self, data, addr, value): + """ + ``perform_skip_and_return_value`` implements a method which converts a *call-like* instruction represented by + the bytes in ``data`` at ``addr`` to one or more instructions that are equivilent to a function returning a + value. + + .. note:: Architecture subclasses should implement this method. + .. warning:: This method should never be called directly. + + :param str data: bytes to be checked + :param int addr: the virtual address of the instruction to be patched + :param int value: value to be returned + :return: The bytes of the replacement unconditional branch instruction + :rtype: str + """ + return None + + def get_associated_arch_by_address(self, addr): + new_addr = ctypes.c_ulonglong() + new_addr.value = addr + result = core.BNGetAssociatedArchitectureByAddress(self.handle, new_addr) + return Architecture(handle = result), new_addr.value + + def get_instruction_info(self, data, addr): + """ + ``get_instruction_info`` returns an InstructionInfo object for the instruction at the given virtual address + ``addr`` with data ``data``. + + .. note :: The instruction info object should always set the InstructionInfo.length to the instruction length, \ + and the branches of the proper types shoulde be added if the instruction is a branch. + + :param str data: max_instruction_length bytes from the binary at virtual address ``addr`` + :param int addr: virtual address of bytes in ``data`` + :return: the InstructionInfo for the current instruction + :rtype: InstructionInfo + """ + info = core.BNInstructionInfo() + data = str(data) + buf = (ctypes.c_ubyte * len(data))() + ctypes.memmove(buf, data, len(data)) + if not core.BNGetInstructionInfo(self.handle, buf, addr, len(data), info): + return None + result = function.InstructionInfo() + result.length = info.length + result.branch_delay = info.branchDelay + for i in xrange(0, info.branchCount): + branch_type = BranchType(info.branchType[i]).name + target = info.branchTarget[i] + if info.branchArch[i]: + arch = Architecture(info.branchArch[i]) + else: + arch = None + result.add_branch(branch_type, target, arch) + return result + + def get_instruction_text(self, data, addr): + """ + ``get_instruction_text`` returns a list of InstructionTextToken objects for the instruction at the given virtual + address ``addr`` with data ``data``. + + :param str data: max_instruction_length bytes from the binary at virtual address ``addr`` + :param int addr: virtual address of bytes in ``data`` + :return: an InstructionTextToken list for the current instruction + :rtype: list(InstructionTextToken) + """ + data = str(data) + count = ctypes.c_ulonglong() + length = ctypes.c_ulonglong() + length.value = len(data) + buf = (ctypes.c_ubyte * len(data))() + ctypes.memmove(buf, data, len(data)) + tokens = ctypes.POINTER(core.BNInstructionTextToken)() + if not core.BNGetInstructionText(self.handle, buf, addr, length, tokens, count): + return None, 0 + result = [] + for i in xrange(0, count.value): + token_type = InstructionTextTokenType(tokens[i].type) + text = tokens[i].text + value = tokens[i].value + size = tokens[i].size + operand = tokens[i].operand + result.append(function.InstructionTextToken(token_type, text, value, size, operand)) + core.BNFreeInstructionText(tokens, count.value) + return result, length.value + + def get_instruction_low_level_il_instruction(self, bv, addr): + il = lowlevelil.LowLevelILFunction(self) + data = bv.read(addr, self.max_instr_length) + self.get_instruction_low_level_il(data, addr, il) + return il[0] + + def get_instruction_low_level_il(self, data, addr, il): + """ + ``get_instruction_low_level_il`` appends LowLevelILExpr objects for the instruction at the given virtual + address ``addr`` with data ``data``. + + :param str data: max_instruction_length bytes from the binary at virtual address ``addr`` + :param int addr: virtual address of bytes in ``data`` + :param LowLevelILFunction il: The function the current instruction belongs to + :return: the length of the current instruction + :rtype: int + """ + data = str(data) + length = ctypes.c_ulonglong() + length.value = len(data) + buf = (ctypes.c_ubyte * len(data))() + ctypes.memmove(buf, data, len(data)) + core.BNGetInstructionLowLevelIL(self.handle, buf, addr, length, il.handle) + return length.value + + def get_reg_name(self, reg): + """ + ``get_reg_name`` gets a register name from a register number. + + :param int reg: register number + :return: the corresponding register string + :rtype: str + """ + return core.BNGetArchitectureRegisterName(self.handle, reg) + + def get_flag_name(self, flag): + """ + ``get_flag_name`` gets a flag name from a flag number. + + :param int reg: register number + :return: the corresponding register string + :rtype: str + """ + return core.BNGetArchitectureFlagName(self.handle, flag) + + def get_flag_write_type_name(self, write_type): + """ + ``get_flag_write_type_name`` gets the flag write type name for the given flag. + + :param int write_type: flag + :return: flag write type name + :rtype: str + """ + return core.BNGetArchitectureFlagWriteTypeName(self.handle, write_type) + + def get_flag_by_name(self, flag): + """ + ``get_flag_by_name`` get flag name for flag index. + + :param int flag: flag index + :return: flag name for flag index + :rtype: str + """ + return self._flags[flag] + + def get_flag_write_type_by_name(self, write_type): + """ + ``get_flag_write_type_by_name`` gets the flag write type name for the flage write type. + + :param int write_type: flag write type + :return: flag write type + :rtype: str + """ + return self._flag_write_types[write_type] + + def get_flag_write_low_level_il(self, op, size, write_type, operands, il): + """ + :param LowLevelILOperation op: + :param int size: + :param str write_type: + :param list(str or int) operands: a list of either items that are either string register names or constant \ + integer values + :param LowLevelILFunction il: + :rtype: LowLevelILExpr + """ + operand_list = (core.BNRegisterOrConstant * len(operands))() + for i in xrange(len(operands)): + if isinstance(operands[i], str): + operand_list[i].constant = False + operand_list[i].reg = self._flags[operands[i]] + else: + operand_list[i].constant = True + operand_list[i].value = operands[i] + return lowlevelil.LowLevelILExpr(core.BNGetArchitectureFlagWriteLowLevelIL(self.handle, op, size, + self._flag_write_types[write_type], operand_list, len(operand_list), il.handle)) + + def get_default_flag_write_low_level_il(self, op, size, write_type, operands, il): + """ + :param LowLevelILOperation op: + :param int size: + :param str write_type: + :param list(str or int) operands: a list of either items that are either string register names or constant \ + integer values + :param LowLevelILFunction il: + :rtype: LowLevelILExpr index + """ + operand_list = (core.BNRegisterOrConstant * len(operands))() + for i in xrange(len(operands)): + if isinstance(operands[i], str): + operand_list[i].constant = False + operand_list[i].reg = self._flags[operands[i]] + else: + operand_list[i].constant = True + operand_list[i].value = operands[i] + return lowlevelil.LowLevelILExpr(core.BNGetDefaultArchitectureFlagWriteLowLevelIL(self.handle, op, size, + self._flag_write_types[write_type], operand_list, len(operand_list), il.handle)) + + def get_flag_condition_low_level_il(self, cond, il): + """ + :param LowLevelILFlagCondition cond: + :param LowLevelILFunction il: + :rtype: LowLevelILExpr + """ + return lowlevelil.LowLevelILExpr(core.BNGetArchitectureFlagConditionLowLevelIL(self.handle, cond, il.handle)) + + def get_modified_regs_on_write(self, reg): + """ + ``get_modified_regs_on_write`` returns a list of register names that are modified when ``reg`` is written. + + :param str reg: string register name + :return: list of register names + :rtype: list(str) + """ + reg = core.BNGetArchitectureRegisterByName(self.handle, str(reg)) + count = ctypes.c_ulonglong() + regs = core.BNGetModifiedArchitectureRegistersOnWrite(self.handle, reg, count) + result = [] + for i in xrange(0, count.value): + result.append(core.BNGetArchitectureRegisterName(self.handle, regs[i])) + core.BNFreeRegisterList(regs) + return result + + def assemble(self, code, addr=0): + """ + ``assemble`` converts the string of assembly instructions ``code`` loaded at virtual address ``addr`` to the + byte representation of those instructions. + + :param str code: string representation of the instructions to be assembled + :param int addr: virtual address that the instructions will be loaded at + :return: the bytes for the assembled instructions or error string + :rtype: (a tuple of instructions and empty string) or (or None and error string) + :Example: + + >>> arch.assemble("je 10") + ('\\x0f\\x84\\x04\\x00\\x00\\x00', '') + >>> + """ + result = databuffer.DataBuffer() + errors = ctypes.c_char_p() + if not core.BNAssemble(self.handle, code, addr, result.handle, errors): + return None, errors.value + return str(result), errors.value + + def is_never_branch_patch_available(self, data, addr): + """ + ``is_never_branch_patch_available`` determines if the instruction ``data`` at ``addr`` can be made to **never branch**. + + :param str data: bytes for the instruction to be checked + :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: + + >>> arch.is_never_branch_patch_available(arch.assemble("je 10")[0], 0) + True + >>> arch.is_never_branch_patch_available(arch.assemble("nop")[0], 0) + False + >>> + """ + data = str(data) + buf = (ctypes.c_ubyte * len(data))() + ctypes.memmove(buf, data, len(data)) + return core.BNIsArchitectureNeverBranchPatchAvailable(self.handle, buf, addr, len(data)) + + def is_always_branch_patch_available(self, data, addr): + """ + ``is_always_branch_patch_available`` determines if the instruction ``data`` at ``addr`` can be made to + **always branch**. + + :param str data: bytes for the instruction to be checked + :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: + + >>> arch.is_always_branch_patch_available(arch.assemble("je 10")[0], 0) + True + >>> arch.is_always_branch_patch_available(arch.assemble("nop")[0], 0) + False + >>> + """ + data = str(data) + buf = (ctypes.c_ubyte * len(data))() + ctypes.memmove(buf, data, len(data)) + return core.BNIsArchitectureAlwaysBranchPatchAvailable(self.handle, buf, addr, len(data)) + + def is_invert_branch_patch_available(self, data, addr): + """ + ``is_always_branch_patch_available`` determines if the instruction ``data`` at ``addr`` can be inverted. + + :param str data: bytes for the instruction to be checked + :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: + + >>> arch.is_invert_branch_patch_available(arch.assemble("je 10")[0], 0) + True + >>> arch.is_invert_branch_patch_available(arch.assemble("nop")[0], 0) + False + >>> + """ + data = str(data) + buf = (ctypes.c_ubyte * len(data))() + ctypes.memmove(buf, data, len(data)) + return core.BNIsArchitectureInvertBranchPatchAvailable(self.handle, buf, addr, len(data)) + + def is_skip_and_return_zero_patch_available(self, data, addr): + """ + ``is_skip_and_return_zero_patch_available`` determines if the instruction ``data`` at ``addr`` is a *call-like* + instruction that can be made into an instruction *returns zero*. + + :param str data: bytes for the instruction to be checked + :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: + + >>> arch.is_skip_and_return_zero_patch_available(arch.assemble("call 0")[0], 0) + True + >>> arch.is_skip_and_return_zero_patch_available(arch.assemble("call eax")[0], 0) + True + >>> arch.is_skip_and_return_zero_patch_available(arch.assemble("jmp eax")[0], 0) + False + >>> + """ + data = str(data) + buf = (ctypes.c_ubyte * len(data))() + ctypes.memmove(buf, data, len(data)) + return core.BNIsArchitectureSkipAndReturnZeroPatchAvailable(self.handle, buf, addr, len(data)) + + def is_skip_and_return_value_patch_available(self, data, addr): + """ + ``is_skip_and_return_zero_patch_available`` determines if the instruction ``data`` at ``addr`` is a *call-like* + instruction that can be made into an instruction *returns a value*. + + :param str data: bytes for the instruction to be checked + :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: + + >>> arch.is_skip_and_return_zero_patch_available(arch.assemble("call 0")[0], 0) + True + >>> arch.is_skip_and_return_zero_patch_available(arch.assemble("jmp eax")[0], 0) + False + >>> + """ + data = str(data) + buf = (ctypes.c_ubyte * len(data))() + ctypes.memmove(buf, data, len(data)) + return core.BNIsArchitectureSkipAndReturnValuePatchAvailable(self.handle, buf, addr, len(data)) + + def convert_to_nop(self, data, addr): + """ + ``convert_to_nop`` reads the instruction(s) in ``data`` at virtual address ``addr`` and returns a string of nop + instructions of the same length as data. + + :param str data: bytes for the instruction to be converted + :param int addr: the virtual address of the instruction to be patched + :return: string containing len(data) worth of no-operation instructions + :rtype: str + :Example: + + >>> arch.convert_to_nop("\\x00\\x00", 0) + '\\x90\\x90' + >>> + """ + data = str(data) + buf = (ctypes.c_ubyte * len(data))() + ctypes.memmove(buf, data, len(data)) + if not core.BNArchitectureConvertToNop(self.handle, buf, addr, len(data)): + return None + result = ctypes.create_string_buffer(len(data)) + ctypes.memmove(result, buf, len(data)) + return result.raw + + def always_branch(self, data, addr): + """ + ``always_branch`` reads the instruction(s) in ``data`` at virtual address ``addr`` and returns a string of bytes + of the same length which always branches. + + :param str data: bytes for the instruction to be converted + :param int addr: the virtual address of the instruction to be patched + :return: string containing len(data) which always branches to the same location as the provided instruction + :rtype: str + :Example: + + >>> bytes = arch.always_branch(arch.assemble("je 10")[0], 0) + >>> arch.get_instruction_text(bytes, 0) + (['nop '], 1L) + >>> arch.get_instruction_text(bytes[1:], 0) + (['jmp ', '0x9'], 5L) + >>> + """ + data = str(data) + buf = (ctypes.c_ubyte * len(data))() + ctypes.memmove(buf, data, len(data)) + if not core.BNArchitectureAlwaysBranch(self.handle, buf, addr, len(data)): + return None + result = ctypes.create_string_buffer(len(data)) + ctypes.memmove(result, buf, len(data)) + return result.raw + + def invert_branch(self, data, addr): + """ + ``invert_branch`` reads the instruction(s) in ``data`` at virtual address ``addr`` and returns a string of bytes + of the same length which inverts the branch of provided instruction. + + :param str data: bytes for the instruction to be converted + :param int addr: the virtual address of the instruction to be patched + :return: string containing len(data) which always branches to the same location as the provided instruction + :rtype: str + :Example: + + >>> arch.get_instruction_text(arch.invert_branch(arch.assemble("je 10")[0], 0), 0) + (['jne ', '0xa'], 6L) + >>> arch.get_instruction_text(arch.invert_branch(arch.assemble("jo 10")[0], 0), 0) + (['jno ', '0xa'], 6L) + >>> arch.get_instruction_text(arch.invert_branch(arch.assemble("jge 10")[0], 0), 0) + (['jl ', '0xa'], 6L) + >>> + """ + data = str(data) + buf = (ctypes.c_ubyte * len(data))() + ctypes.memmove(buf, data, len(data)) + if not core.BNArchitectureInvertBranch(self.handle, buf, addr, len(data)): + return None + result = ctypes.create_string_buffer(len(data)) + ctypes.memmove(result, buf, len(data)) + return result.raw + + def skip_and_return_value(self, data, addr, value): + """ + ``skip_and_return_value`` reads the instruction(s) in ``data`` at virtual address ``addr`` and returns a string of + bytes of the same length which doesn't call and instead *return a value*. + + :param str data: bytes for the instruction to be converted + :param int addr: the virtual address of the instruction to be patched + :return: string containing len(data) which always branches to the same location as the provided instruction + :rtype: str + :Example: + + >>> arch.get_instruction_text(arch.skip_and_return_value(arch.assemble("call 10")[0], 0, 0), 0) + (['mov ', 'eax', ', ', '0x0'], 5L) + >>> + """ + data = str(data) + buf = (ctypes.c_ubyte * len(data))() + ctypes.memmove(buf, data, len(data)) + if not core.BNArchitectureSkipAndReturnValue(self.handle, buf, addr, len(data), value): + return None + result = ctypes.create_string_buffer(len(data)) + ctypes.memmove(result, buf, len(data)) + return result.raw + + def is_view_type_constant_defined(self, type_name, const_name): + """ + + :param str type_name: the BinaryView type name of the constant to query + :param str const_name: the constant name to query + :rtype: None + :Example: + + >>> arch.set_view_type_constant("ELF", "R_COPY", ELF_RELOC_COPY) + >>> arch.is_view_type_constant_defined("ELF", "R_COPY") + True + >>> arch.is_view_type_constant_defined("ELF", "NOT_THERE") + False + >>> + """ + return core.BNIsBinaryViewTypeArchitectureConstantDefined(self.handle, type_name, const_name) + + def get_view_type_constant(self, type_name, const_name, default_value=0): + """ + ``get_view_type_constant`` retrieves the view type constant for the given type_name and const_name. + + :param str type_name: the BinaryView type name of the constant to be retrieved + :param str const_name: the constant name to retrieved + :param int value: optional default value if the type_name is not present. default value is zero. + :return: The BinaryView type constant or the default_value if not found + :rtype: int + :Example: + + >>> ELF_RELOC_COPY = 5 + >>> arch.set_view_type_constant("ELF", "R_COPY", ELF_RELOC_COPY) + >>> arch.get_view_type_constant("ELF", "R_COPY") + 5L + >>> arch.get_view_type_constant("ELF", "NOT_HERE", 100) + 100L + """ + return core.BNGetBinaryViewTypeArchitectureConstant(self.handle, type_name, const_name, default_value) + + def set_view_type_constant(self, type_name, const_name, value): + """ + ``set_view_type_constant`` creates a new binaryview type constant. + + :param str type_name: the BinaryView type name of the constant to be registered + :param str const_name: the constant name to register + :param int value: the value of the constant + :rtype: None + :Example: + + >>> ELF_RELOC_COPY = 5 + >>> arch.set_view_type_constant("ELF", "R_COPY", ELF_RELOC_COPY) + >>> + """ + core.BNSetBinaryViewTypeArchitectureConstant(self.handle, type_name, const_name, value) + + def parse_types_from_source(self, source, filename=None, include_dirs=[]): + """ + ``parse_types_from_source`` parses the source string and any needed headers searching for them in + the optional list of directories provided in ``include_dirs``. + + :param str source: source string to be parsed + :param str filename: optional source filename + :param list(str) include_dirs: optional list of string filename include directories + :return: py:class:`TypeParserResult` (a SyntaxError is thrown on parse error) + :rtype: TypeParserResult + :Example: + + >>> arch.parse_types_from_source('int foo;\\nint bar(int x);\\nstruct bas{int x,y;};\\n') + ({types: {'bas': <type: struct bas>}, variables: {'foo': <type: int32_t>}, functions:{'bar': + <type: int32_t(int32_t x)>}}, '') + >>> + """ + + if filename is None: + filename = "input" + dir_buf = (ctypes.c_char_p * len(include_dirs))() + for i in xrange(0, len(include_dirs)): + dir_buf[i] = str(include_dirs[i]) + parse = core.BNTypeParserResult() + errors = ctypes.c_char_p() + result = core.BNParseTypesFromSource(self.handle, source, filename, parse, errors, dir_buf, len(include_dirs)) + error_str = errors.value + core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) + if not result: + raise SyntaxError, error_str + type_dict = {} + variables = {} + functions = {} + for i in xrange(0, parse.typeCount): + name = types.QualifiedName._from_core_struct(parse.types[i].name) + type_dict[name] = types.Type(core.BNNewTypeReference(parse.types[i].type)) + for i in xrange(0, parse.variableCount): + name = types.QualifiedName._from_core_struct(parse.variables[i].name) + variables[name] = types.Type(core.BNNewTypeReference(parse.variables[i].type)) + for i in xrange(0, parse.functionCount): + name = types.QualifiedName._from_core_struct(parse.functions[i].name) + functions[name] = types.Type(core.BNNewTypeReference(parse.functions[i].type)) + core.BNFreeTypeParserResult(parse) + return types.TypeParserResult(type_dict, variables, functions) + + def parse_types_from_source_file(self, filename, include_dirs=[]): + """ + ``parse_types_from_source_file`` parses the source file ``filename`` and any needed headers searching for them in + the optional list of directories provided in ``include_dirs``. + + :param str filename: filename of file to be parsed + :param list(str) include_dirs: optional list of string filename include directories + :return: py:class:`TypeParserResult` (a SyntaxError is thrown on parse error) + :rtype: TypeParserResult + :Example: + + >>> file = "/Users/binja/tmp.c" + >>> open(file).read() + 'int foo;\\nint bar(int x);\\nstruct bas{int x,y;};\\n' + >>> arch.parse_types_from_source_file(file) + ({types: {'bas': <type: struct bas>}, variables: {'foo': <type: int32_t>}, functions: + {'bar': <type: int32_t(int32_t x)>}}, '') + >>> + """ + dir_buf = (ctypes.c_char_p * len(include_dirs))() + for i in xrange(0, len(include_dirs)): + dir_buf[i] = str(include_dirs[i]) + parse = core.BNTypeParserResult() + errors = ctypes.c_char_p() + result = core.BNParseTypesFromSourceFile(self.handle, filename, parse, errors, dir_buf, len(include_dirs)) + error_str = errors.value + core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) + if not result: + raise SyntaxError, error_str + type_dict = {} + variables = {} + functions = {} + for i in xrange(0, parse.typeCount): + name = types.QualifiedName._from_core_struct(parse.types[i].name) + type_dict[name] = types.Type(core.BNNewTypeReference(parse.types[i].type)) + for i in xrange(0, parse.variableCount): + name = types.QualifiedName._from_core_struct(parse.variables[i].name) + variables[name] = types.Type(core.BNNewTypeReference(parse.variables[i].type)) + for i in xrange(0, parse.functionCount): + name = types.QualifiedName._from_core_struct(parse.functions[i].name) + functions[name] = types.Type(core.BNNewTypeReference(parse.functions[i].type)) + core.BNFreeTypeParserResult(parse) + return types.TypeParserResult(type_dict, variables, functions) + + def register_calling_convention(self, cc): + """ + ``register_calling_convention`` registers a new calling convention for the Architecture. + + :param CallingConvention cc: CallingConvention object to be registered + :rtype: None + """ + core.BNRegisterCallingConvention(self.handle, cc.handle) + + +class ReferenceSource(object): + def __init__(self, func, arch, addr): + self.function = func + self.arch = arch + self.address = addr + + def __repr__(self): + if self.arch: + return "<ref: %s@%#x>" % (self.arch.name, self.address) + else: + return "<ref: %#x>" % self.address diff --git a/python/associateddatastore.py b/python/associateddatastore.py new file mode 100644 index 00000000..6b5e688e --- /dev/null +++ b/python/associateddatastore.py @@ -0,0 +1,45 @@ +# 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 copy + + +class _AssociatedDataStore(dict): + _defaults = {} + + @classmethod + def set_default(cls, name, value): + cls._defaults[name] = value + + def __getattr__(self, name): + if name in self.__dict__: + return self.__dict__[name] + if name not in self: + if name in self.__class__._defaults: + result = copy.copy(self.__class__._defaults[name]) + self[name] = result + return result + return self.__getitem__(name) + + def __setattr__(self, name, value): + self.__setitem__(name, value) + + def __delattr__(self, name): + self.__delitem__(name) diff --git a/python/basicblock.py b/python/basicblock.py new file mode 100644 index 00000000..ae9889fc --- /dev/null +++ b/python/basicblock.py @@ -0,0 +1,232 @@ +# 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 ctypes + +# Binary Ninja components +import _binaryninjacore as core +from enums import BranchType, HighlightColorStyle, HighlightStandardColor, InstructionTextTokenType +import architecture +import highlight +import function + + +class BasicBlockEdge(object): + def __init__(self, branch_type, target, arch): + self.type = branch_type + if self.type != BranchType.UnresolvedBranch: + self.target = target + self.arch = arch + + def __repr__(self): + if self.type == BranchType.UnresolvedBranch: + return "<%s>" % BranchType(self.type).name + elif self.arch: + return "<%s: %s@%#x>" % (self.type, self.arch.name, self.target) + else: + return "<%s: %#x>" % (self.type, self.target) + + +class BasicBlock(object): + def __init__(self, view, handle): + self.view = view + self.handle = core.handle_of_type(handle, core.BNBasicBlock) + + def __del__(self): + core.BNFreeBasicBlock(self.handle) + + @property + def function(self): + """Basic block function (read-only)""" + func = core.BNGetBasicBlockFunction(self.handle) + if func is None: + return None + return function.Function(self.view, func) + + @property + def arch(self): + """Basic block architecture (read-only)""" + arch = core.BNGetBasicBlockArchitecture(self.handle) + if arch is None: + return None + return architecture.Architecture(arch) + + @property + def start(self): + """Basic block start (read-only)""" + return core.BNGetBasicBlockStart(self.handle) + + @property + def end(self): + """Basic block end (read-only)""" + return core.BNGetBasicBlockEnd(self.handle) + + @property + def length(self): + """Basic block length (read-only)""" + return core.BNGetBasicBlockLength(self.handle) + + @property + def outgoing_edges(self): + """List of basic block outgoing edges (read-only)""" + count = ctypes.c_ulonglong(0) + edges = core.BNGetBasicBlockOutgoingEdges(self.handle, count) + result = [] + for i in xrange(0, count.value): + branch_type = edges[i].type + target = edges[i].target + if edges[i].arch: + arch = architecture.Architecture(edges[i].arch) + else: + arch = None + result.append(BasicBlockEdge(branch_type, target, arch)) + core.BNFreeBasicBlockOutgoingEdgeList(edges) + return result + + @property + def has_undetermined_outgoing_edges(self): + """Whether basic block has undetermined outgoing edges (read-only)""" + return core.BNBasicBlockHasUndeterminedOutgoingEdges(self.handle) + + @property + def annotations(self): + """List of automatic annotations for the start of this block (read-only)""" + return self.function.get_block_annotations(self.arch, self.start) + + @property + def disassembly_text(self): + """ + ``disassembly_text`` property which returns a list of function.DisassemblyTextLine objects for the current basic block. + :Example: + + >>> current_basic_block.disassembly_text + [<0x100000f30: _main:>, ...] + """ + return self.get_disassembly_text() + + @property + def highlight(self): + """Gets or sets the highlight color for basic block + + :Example: + + >>> current_basic_block.highlight = HighlightStandardColor.BlueHighlightColor + >>> current_basic_block.highlight + <color: blue> + """ + color = core.BNGetBasicBlockHighlight(self.handle) + if color.style == HighlightColorStyle.StandardHighlightColor: + return highlight.HighlightColor(color=color.color, alpha=color.alpha) + elif color.style == HighlightColorStyle.MixedHighlightColor: + return highlight.HighlightColor(color=color.color, mix_color=color.mixColor, mix=color.mix, alpha=color.alpha) + elif color.style == HighlightColorStyle.CustomHighlightColor: + return highlight.HighlightColor(red=color.r, green=color.g, blue=color.b, alpha=color.alpha) + return highlight.HighlightColor(color=HighlightStandardColor.NoHighlightColor) + + @highlight.setter + def highlight(self, value): + self.set_user_highlight(value) + + def __setattr__(self, name, value): + try: + object.__setattr__(self, name, value) + except AttributeError: + raise AttributeError("attribute '%s' is read only" % name) + + def __len__(self): + return int(core.BNGetBasicBlockLength(self.handle)) + + def __repr__(self): + arch = self.arch + if arch: + return "<block: %s@%#x-%#x>" % (arch.name, self.start, self.end) + else: + return "<block: %#x-%#x>" % (self.start, self.end) + + def __iter__(self): + start = self.start + end = self.end + + idx = start + while idx < end: + data = self.view.read(idx, 16) + inst_info = self.view.arch.get_instruction_info(data, idx) + inst_text = self.view.arch.get_instruction_text(data, idx) + + yield inst_text + idx += inst_info.length + + def mark_recent_use(self): + core.BNMarkBasicBlockAsRecentlyUsed(self.handle) + + def get_disassembly_text(self, settings=None): + """ + ``get_disassembly_text`` returns a list of function.DisassemblyTextLine objects for the current basic block. + :Example: + + >>>current_basic_block.get_disassembly_text() + [<0x100000f30: _main:>, <0x100000f30: push rbp>, ... ] + """ + settings_obj = None + if settings: + settings_obj = settings.handle + + count = ctypes.c_ulonglong() + lines = core.BNGetBasicBlockDisassemblyText(self.handle, settings_obj, count) + result = [] + for i in xrange(0, count.value): + addr = lines[i].addr + tokens = [] + for j in xrange(0, lines[i].count): + token_type = InstructionTextTokenType(lines[i].tokens[j].type) + text = lines[i].tokens[j].text + value = lines[i].tokens[j].value + size = lines[i].tokens[j].size + operand = lines[i].tokens[j].operand + tokens.append(function.InstructionTextToken(token_type, text, value, size, operand)) + result.append(function.DisassemblyTextLine(addr, tokens)) + core.BNFreeDisassemblyTextLines(lines, count.value) + return result + + def set_auto_highlight(self, color): + """ + ``set_auto_highlight`` highlights the current BasicBlock with the supplied color. + + .warning:: Use only in analysis plugins. Do not use in regular plugins, as colors won't be saved to the database. + + :param HighlightStandardColor or highlight.HighlightColor color: Color value to use for highlighting + """ + if not isinstance(color, HighlightStandardColor) and not isinstance(color, highlight.HighlightColor): + raise ValueError("Specified color is not one of HighlightStandardColor, highlight.HighlightColor") + core.BNSetAutoBasicBlockHighlight(self.handle, color._get_core_struct()) + + def set_user_highlight(self, color): + """ + ``set_user_highlight`` highlights the current BasicBlock with the supplied color + + :param HighlightStandardColor or highlight.HighlightColor color: Color value to use for highlighting + :Example: + + >>> current_basic_block.set_user_highlight(highlight.HighlightColor(red=0xff, blue=0xff, green=0)) + >>> current_basic_block.set_user_highlight(HighlightStandardColor.BlueHighlightColor) + """ + if not isinstance(color, HighlightStandardColor) and not isinstance(color, highlight.HighlightColor): + raise ValueError("Specified color is not one of HighlightStandardColor, highlight.HighlightColor") + core.BNSetUserBasicBlockHighlight(self.handle, color._get_core_struct()) diff --git a/python/binaryview.py b/python/binaryview.py new file mode 100644 index 00000000..34be139d --- /dev/null +++ b/python/binaryview.py @@ -0,0 +1,3640 @@ +# 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 +from enums import AnalysisState, SymbolType, InstructionTextTokenType, Endianness, ModificationStatus, StringType, SegmentFlag +import function +import startup +import architecture +import platform +import associateddatastore +import fileaccessor +import filemetadata +import log +import databuffer +import basicblock +import types +import lineardisassembly + + +class BinaryDataNotification(object): + def __init__(self): + pass + + 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 + + def type_defined(self, view, name, type): + pass + + def type_undefined(self, view, name, type): + 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 == AnalysisState.DisassembleState: + return "Disassembling (%d/%d)" % (self.count, self.total) + if self.state == AnalysisState.AnalyzeState: + return "Analyzing (%d/%d)" % (self.count, self.total) + return "Idle" + + def __repr__(self): + return "<progress: %s>" % str(self) + + +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) + self._cb.typeDefined = self._cb.typeDefined.__class__(self._type_defined) + self._cb.typeUndefined = self._cb.typeUndefined.__class__(self._type_undefined) + + def _register(self): + core.BNRegisterDataNotification(self.view.handle, self._cb) + + def _unregister(self): + core.BNUnregisterDataNotification(self.view.handle, self._cb) + + def _data_written(self, ctxt, view, offset, length): + try: + self.notify.data_written(self.view, offset, length) + except OSError: + log.log_error(traceback.format_exc()) + + def _data_inserted(self, ctxt, view, offset, length): + try: + self.notify.data_inserted(self.view, offset, length) + except: + log.log_error(traceback.format_exc()) + + def _data_removed(self, ctxt, view, offset, length): + try: + self.notify.data_removed(self.view, offset, length) + except: + log.log_error(traceback.format_exc()) + + def _function_added(self, ctxt, view, func): + try: + self.notify.function_added(self.view, function.Function(self.view, core.BNNewFunctionReference(func))) + except: + log.log_error(traceback.format_exc()) + + def _function_removed(self, ctxt, view, func): + try: + self.notify.function_removed(self.view, function.Function(self.view, core.BNNewFunctionReference(func))) + except: + log.log_error(traceback.format_exc()) + + def _function_updated(self, ctxt, view, func): + try: + self.notify.function_updated(self.view, function.Function(self.view, core.BNNewFunctionReference(func))) + except: + log.log_error(traceback.format_exc()) + + def _data_var_added(self, ctxt, view, var): + try: + address = var.address + var_type = types.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 = types.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 = types.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, StringType(string_type), offset, length) + except: + log.log_error(traceback.format_exc()) + + def _string_removed(self, ctxt, view, string_type, offset, length): + try: + self.notify.string_removed(self.view, StringType(string_type), offset, length) + except: + log.log_error(traceback.format_exc()) + + def _type_defined(self, ctxt, name, type_obj): + try: + qualified_name = types.QualifiedName._from_core_struct(name[0]) + self.notify.type_defined(self.view, qualified_name, Type(core.BNNewTypeReference(type_obj))) + except: + log_error(traceback.format_exc()) + + def _type_undefined(self, ctxt, name, type_obj): + try: + qualified_name = types.QualifiedName._from_core_struct(name[0]) + self.notify.type_undefined(self.view, qualified_name, Type(core.BNNewTypeReference(type_obj))) + except: + 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) + + +class BinaryViewType(object): + __metaclass__ = _BinaryViewTypeMetaclass + + def __init__(self, handle): + self.handle = core.handle_of_type(handle, core.BNBinaryViewType) + + @property + def name(self): + """BinaryView name (read-only)""" + return core.BNGetBinaryViewTypeName(self.handle) + + @property + def long_name(self): + """BinaryView long name (read-only)""" + return core.BNGetBinaryViewTypeLongName(self.handle) + + def __repr__(self): + return "<view type: '%s'>" % self.name + + 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) + + @classmethod + def get_view_of_file(cls, filename, update_analysis=True): + """ + ``get_view_of_file`` returns the first available, non-Raw `BinaryView` available. + + :param str filename: Path to filename or bndb + :param bool update_analysis: defaults to True. Pass False to not run update_analysis_and_wait. + :return: returns a BinaryView object for the given filename. + :rtype: BinaryView or None + """ + sqlite = "SQLite format 3" + if filename.endswith(".bndb"): + f = open(filename, 'r') + if f is None or f.read(len(sqlite)) != sqlite: + return None + f.close() + view = filemetadata.FileMetadata().open_existing_database(filename) + else: + view = BinaryView.open(filename) + + if view is None: + return None + for available in view.available_view_types: + if available.name != "Raw": + bv = cls[available.name].open(filename) + if update_analysis: + bv.update_analysis_and_wait() + return bv + return None + + 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) + + +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 & SegmentFlag.SegmentReadable) != 0 else "-", + "w" if (self.flags & SegmentFlag.SegmentWritable) != 0 else "-", + "x" if (self.flags & SegmentFlag.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.handle)) + else: + startup._init_plugins() + if not self.__class__._registered: + raise TypeError("view type not registered") + self._cb = core.BNCustomBinaryView() + self._cb.context = 0 + self._cb.init = self._cb.init.__class__(self._init) + self._cb.read = self._cb.read.__class__(self._read) + self._cb.write = self._cb.write.__class__(self._write) + self._cb.insert = self._cb.insert.__class__(self._insert) + self._cb.remove = self._cb.remove.__class__(self._remove) + self._cb.getModification = self._cb.getModification.__class__(self._get_modification) + self._cb.isValidOffset = self._cb.isValidOffset.__class__(self._is_valid_offset) + self._cb.isOffsetReadable = self._cb.isOffsetReadable.__class__(self._is_offset_readable) + self._cb.isOffsetWritable = self._cb.isOffsetWritable.__class__(self._is_offset_writable) + self._cb.isOffsetExecutable = self._cb.isOffsetExecutable.__class__(self._is_offset_executable) + self._cb.getNextValidOffset = self._cb.getNextValidOffset.__class__(self._get_next_valid_offset) + self._cb.getStart = self._cb.getStart.__class__(self._get_start) + self._cb.getLength = self._cb.getLength.__class__(self._get_length) + self._cb.getEntryPoint = self._cb.getEntryPoint.__class__(self._get_entry_point) + self._cb.isExecutable = self._cb.isExecutable.__class__(self._is_executable) + self._cb.getDefaultEndianness = self._cb.getDefaultEndianness.__class__(self._get_default_endianness) + self._cb.getAddressSize = self._cb.getAddressSize.__class__(self._get_address_size) + self._cb.save = self._cb.save.__class__(self._save) + self.file = file_metadata + if parent_view is not None: + parent_view = parent_view.handle + self.handle = core.BNCreateCustomBinaryView(self.__class__.name, file_metadata.handle, parent_view, self._cb) + self.notifications = {} + self.next_address = None # Do NOT try to access view before init() is called, use placeholder + + @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): + """ + ```set_default_session_data``` saves a variable to the BinaryView. + :param name: name of the variable to be saved + :param value: value of the variable to be saved + + :Example: + >>> BinaryView.set_default_session_data("variable_name", "value") + >>> bv.session_data.variable_name + 'value' + """ + _BinaryViewAssociatedDataStore.set_default(name, value) + + def __del__(self): + for i in self.notifications.values(): + i._unregister() + core.BNFreeBinaryView(self.handle) + + def __iter__(self): + count = ctypes.c_ulonglong(0) + funcs = core.BNGetAnalysisFunctionList(self.handle, count) + try: + for i in xrange(0, count.value): + yield function.Function(self, core.BNNewFunctionReference(funcs[i])) + finally: + core.BNFreeFunctionList(funcs, count.value) + + @property + def parent_view(self): + """View that contains the raw data used by this view (read-only)""" + result = core.BNGetParentView(self.handle) + if result is None: + return None + return BinaryView(handle=result) + + @property + def modified(self): + """boolean modification state of the BinaryView (read/write)""" + return self.file.modified + + @modified.setter + def modified(self, value): + self.file.modified = value + + @property + def analysis_changed(self): + """boolean analysis state changed of the currently running analysis (read-only)""" + return self.file.analysis_changed + + @property + def has_database(self): + """boolean has a database been written to disk (read-only)""" + return self.file.has_database + + @property + def view(self): + return self.file.view + + @view.setter + def view(self, value): + self.file.view = value + + @property + def offset(self): + return self.file.offset + + @offset.setter + def offset(self, value): + self.file.offset = value + + @property + def start(self): + """Start offset of the binary (read-only)""" + return core.BNGetStartOffset(self.handle) + + @property + def end(self): + """End offset of the binary (read-only)""" + return core.BNGetEndOffset(self.handle) + + @property + def entry_point(self): + """Entry point of the binary (read-only)""" + return core.BNGetEntryPoint(self.handle) + + @property + def arch(self): + """The architecture associated with the current BinaryView (read/write)""" + arch = core.BNGetDefaultArchitecture(self.handle) + if arch is None: + return None + return architecture.Architecture(handle=arch) + + @arch.setter + def arch(self, value): + if value is None: + core.BNSetDefaultArchitecture(self.handle, None) + else: + core.BNSetDefaultArchitecture(self.handle, value.handle) + + @property + def platform(self): + """The platform associated with the current BinaryView (read/write)""" + plat = core.BNGetDefaultPlatform(self.handle) + if plat is None: + return None + return platform.Platform(self.arch, handle=plat) + + @platform.setter + def platform(self, value): + if value is None: + core.BNSetDefaultPlatform(self.handle, None) + else: + core.BNSetDefaultPlatform(self.handle, value.handle) + + @property + def endianness(self): + """Endianness of the binary (read-only)""" + return Endianness(core.BNGetDefaultEndianness(self.handle)) + + @property + def address_size(self): + """Address size of the binary (read-only)""" + return core.BNGetViewAddressSize(self.handle) + + @property + def executable(self): + """Whether the binary is an executable (read-only)""" + return core.BNIsExecutableView(self.handle) + + @property + def functions(self): + """List of functions (read-only)""" + count = ctypes.c_ulonglong(0) + funcs = core.BNGetAnalysisFunctionList(self.handle, count) + result = [] + for i in xrange(0, count.value): + result.append(function.Function(self, core.BNNewFunctionReference(funcs[i]))) + core.BNFreeFunctionList(funcs, count.value) + return result + + @property + def has_functions(self): + """Boolean whether the binary has functions (read-only)""" + return core.BNHasFunctions(self.handle) + + @property + def entry_function(self): + """Entry function (read-only)""" + func = core.BNGetAnalysisEntryPoint(self.handle) + if func is None: + return None + return function.Function(self, func) + + @property + def symbols(self): + """Dict of symbols (read-only)""" + count = ctypes.c_ulonglong(0) + syms = core.BNGetSymbols(self.handle, count) + result = {} + for i in xrange(0, count.value): + sym = types.Symbol(None, None, None, handle=core.BNNewSymbolReference(syms[i])) + result[sym.raw_name] = sym + core.BNFreeSymbolList(syms, count.value) + return result + + @property + def view_type(self): + """View type (read-only)""" + return core.BNGetViewType(self.handle) + + @property + def available_view_types(self): + """Available view types (read-only)""" + count = ctypes.c_ulonglong(0) + types = core.BNGetBinaryViewTypesForData(self.handle, count) + result = [] + for i in xrange(0, count.value): + result.append(BinaryViewType(types[i])) + core.BNFreeBinaryViewTypeList(types) + return result + + @property + def strings(self): + """List of strings (read-only)""" + return self.get_strings() + + @property + def saved(self): + """boolean state of whether or not the file has been saved (read/write)""" + return self.file.saved + + @saved.setter + def saved(self, value): + self.file.saved = value + + @property + def analysis_progress(self): + """Status of current analysis (read-only)""" + result = core.BNGetAnalysisProgress(self.handle) + return AnalysisProgress(result.state, result.count, result.total) + + @property + def linear_disassembly(self): + """Iterator for all lines in the linear disassembly of the view""" + return self.get_linear_disassembly(None) + + @property + def data_vars(self): + """List of data variables (read-only)""" + count = ctypes.c_ulonglong(0) + var_list = core.BNGetDataVariables(self.handle, count) + result = {} + for i in xrange(0, count.value): + addr = var_list[i].address + var_type = types.Type(core.BNNewTypeReference(var_list[i].type)) + auto_discovered = var_list[i].autoDiscovered + result[addr] = DataVariable(addr, var_type, auto_discovered) + core.BNFreeDataVariables(var_list, count.value) + return result + + @property + def types(self): + """List of defined types (read-only)""" + count = ctypes.c_ulonglong(0) + type_list = core.BNGetAnalysisTypeList(self.handle, count) + result = {} + for i in xrange(0, count.value): + name = types.QualifiedName._from_core_struct(type_list[i].name) + result[name] = types.Type(core.BNNewTypeReference(type_list[i].type)) + core.BNFreeTypeList(type_list, count.value) + return result + + @property + def segments(self): + """List of segments (read-only)""" + count = ctypes.c_ulonglong(0) + segment_list = core.BNGetSegments(self.handle, count) + result = [] + for i in xrange(0, count.value): + result.append(Segment(segment_list[i].start, segment_list[i].length, + segment_list[i].dataOffset, segment_list[i].dataLength, segment_list[i].flags)) + core.BNFreeSegmentList(segment_list) + return result + + @property + def sections(self): + """List of sections (read-only)""" + count = ctypes.c_ulonglong(0) + section_list = core.BNGetSections(self.handle, count) + result = {} + for i in xrange(0, count.value): + result[section_list[i].name] = Section(section_list[i].name, section_list[i].type, section_list[i].start, + section_list[i].length, section_list[i].linkedSection, section_list[i].infoSection, + section_list[i].infoData, section_list[i].align, section_list[i].entrySize) + core.BNFreeSectionList(section_list, count.value) + return result + + @property + def allocated_ranges(self): + """List of valid address ranges for this view (read-only)""" + count = ctypes.c_ulonglong(0) + range_list = core.BNGetAllocatedRanges(self.handle, count) + result = [] + for i in xrange(0, count.value): + result.append(AddressRange(range_list[i].start, range_list[i].end)) + core.BNFreeAddressRanges(range_list) + return result + + @property + def session_data(self): + """Dictionary object where plugins can store arbitrary data associated with the view""" + handle = ctypes.cast(self.handle, ctypes.c_void_p) + if handle.value not in BinaryView._associated_data: + obj = _BinaryViewAssociatedDataStore() + BinaryView._associated_data[handle.value] = obj + return obj + else: + return BinaryView._associated_data[handle.value] + + def __len__(self): + return int(core.BNGetViewLength(self.handle)) + + def __getitem__(self, i): + if isinstance(i, tuple): + result = "" + for s in i: + result += self.__getitem__(s) + return result + elif isinstance(i, slice): + if i.step is not None: + raise IndexError("step not implemented") + i = i.indices(self.end) + start = i[0] + stop = i[1] + if stop <= start: + return "" + return str(self.read(start, stop - start)) + elif i < 0: + if i >= -len(self): + value = str(self.read(int(len(self) + i), 1)) + if len(value) == 0: + return IndexError("index not readable") + return value + raise IndexError("index out of range") + elif (i >= self.start) and (i < self.end): + value = str(self.read(int(i), 1)) + if len(value) == 0: + return IndexError("index not readable") + return value + else: + raise IndexError("index out of range") + + def __setitem__(self, i, value): + if isinstance(i, slice): + if i.step is not None: + raise IndexError("step not supported on assignment") + i = i.indices(self.end) + start = i[0] + stop = i[1] + if stop < start: + stop = start + if len(value) != (stop - start): + self.remove(start, stop - start) + self.insert(start, value) + else: + self.write(start, value) + elif i < 0: + if i >= -len(self): + if len(value) != 1: + raise ValueError("expected single byte for assignment") + if self.write(int(len(self) + i), value) != 1: + raise IndexError("index not writable") + else: + raise IndexError("index out of range") + elif (i >= self.start) and (i < self.end): + if len(value) != 1: + raise ValueError("expected single byte for assignment") + if self.write(int(i), value) != 1: + raise IndexError("index not writable") + else: + raise IndexError("index out of range") + + def __repr__(self): + start = self.start + length = len(self) + if start != 0: + size = "start %#x, len %#x" % (start, length) + else: + size = "len %#x" % length + filename = self.file.filename + if len(filename) > 0: + return "<BinaryView: '%s', %s>" % (filename, size) + return "<BinaryView: %s>" % (size) + + def _init(self, ctxt): + try: + return self.init() + except: + log.log_error(traceback.format_exc()) + return False + + def _read(self, ctxt, dest, offset, length): + try: + data = self.perform_read(offset, length) + if data is None: + return 0 + if len(data) > length: + data = data[0:length] + ctypes.memmove(dest, str(data), len(data)) + return len(data) + except: + log.log_error(traceback.format_exc()) + return 0 + + def _write(self, ctxt, offset, src, length): + try: + data = ctypes.create_string_buffer(length) + ctypes.memmove(data, src, length) + return self.perform_write(offset, data.raw) + except: + log.log_error(traceback.format_exc()) + return 0 + + def _insert(self, ctxt, offset, src, length): + try: + data = ctypes.create_string_buffer(length) + ctypes.memmove(data, src, length) + return self.perform_insert(offset, data.raw) + except: + log.log_error(traceback.format_exc()) + return 0 + + def _remove(self, ctxt, offset, length): + try: + return self.perform_remove(offset, length) + except: + log.log_error(traceback.format_exc()) + return 0 + + def _get_modification(self, ctxt, offset): + try: + return self.perform_get_modification(offset) + except: + log.log_error(traceback.format_exc()) + return ModificationStatus.Original + + def _is_valid_offset(self, ctxt, offset): + try: + return self.perform_is_valid_offset(offset) + except: + log.log_error(traceback.format_exc()) + return False + + def _is_offset_readable(self, ctxt, offset): + try: + return self.perform_is_offset_readable(offset) + except: + log.log_error(traceback.format_exc()) + return False + + def _is_offset_writable(self, ctxt, offset): + try: + return self.perform_is_offset_writable(offset) + except: + log.log_error(traceback.format_exc()) + return False + + def _is_offset_executable(self, ctxt, offset): + try: + return self.perform_is_offset_executable(offset) + except: + log.log_error(traceback.format_exc()) + return False + + def _get_next_valid_offset(self, ctxt, offset): + try: + return self.perform_get_next_valid_offset(offset) + except: + log.log_error(traceback.format_exc()) + return offset + + def _get_start(self, ctxt): + try: + return self.perform_get_start() + except: + log.log_error(traceback.format_exc()) + return 0 + + def _get_length(self, ctxt): + try: + return self.perform_get_length() + except: + log.log_error(traceback.format_exc()) + return 0 + + def _get_entry_point(self, ctxt): + try: + return self.perform_get_entry_point() + except: + log.log_error(traceback.format_exc()) + return 0 + + def _is_executable(self, ctxt): + try: + return self.perform_is_executable() + except: + log.log_error(traceback.format_exc()) + return False + + def _get_default_endianness(self, ctxt): + try: + return self.perform_get_default_endianness() + except: + log.log_error(traceback.format_exc()) + return Endianness.LittleEndian + + def _get_address_size(self, ctxt): + try: + return self.perform_get_address_size() + except: + log.log_error(traceback.format_exc()) + return 8 + + def _save(self, ctxt, file_accessor): + try: + return self.perform_save(fileaccessor.CoreFileAccessor(file_accessor)) + except: + log.log_error(traceback.format_exc()) + return False + + 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, 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, 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: ModificationStatus + """ + return ModificationStatus.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 ``Endianness.LittleEndian`` or ``Endianness.BigEndian`` + :rtype: Endianness + """ + return Endianness.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(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(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(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(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 ModificationStatus. + + :param int addr: virtual address to get modification from + :param int length: optional length of modification + :return: Either ModificationStatus of the byte at ``addr``, or string of modified bytes at ``addr`` + :rtype: ModificationStatus or str + """ + if length is None: + return ModificationStatus(core.BNGetModification(self.handle, addr)) + data = (ModificationStatus * length)() + length = core.BNGetModificationArray(self.handle, addr, data, length) + return data[0:length] + + 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, addr, plat=None): + """ + ``add_function`` add a new function of the given ``plat`` at the virtual address ``addr`` + + :param int addr: virtual address of the function to be added + :param Platform plat: Platform for the function to be added + :rtype: None + :Example: + + >>> bv.add_function(1) + >>> bv.functions + [<func: x86_64@0x1>] + + """ + if self.platform is None: + raise Exception("Default platform not set in BinaryView") + if plat is None: + plat = self.platform + core.BNAddFunctionForAnalysis(self.handle, plat.handle, addr) + + def add_entry_point(self, addr, plat=None): + """ + ``add_entry_point`` adds an virtual address to start analysis from for a given plat. + + :param int addr: virtual address to start analysis from + :param Platform plat: Platform for the entry point analysis + :rtype: None + :Example: + >>> bv.add_entry_point(0xdeadbeef) + >>> + """ + if self.platform is None: + raise Exception("Default platform not set in BinaryView") + if plat is None: + plat = self.platform + core.BNAddEntryPointForAnalysis(self.handle, plat.handle, addr) + + 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, addr, plat=None): + """ + ``create_user_function`` add a new *user* function of the given ``plat`` at the virtual address ``addr`` + + :param int addr: virtual address of the *user* function to be added + :param Platform plat: Platform for the function to be added + :rtype: None + :Example: + + >>> bv.create_user_function(1) + >>> bv.functions + [<func: x86_64@0x1>] + + """ + if plat is None: + plat = self.platform + 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(object): + def __init__(self): + self.cond = threading.Condition() + self.done = False + + def complete(self): + self.cond.acquire() + self.done = True + self.cond.notify() + self.cond.release() + + def wait(self): + self.cond.acquire() + while not self.done: + self.cond.wait() + self.cond.release() + + wait = WaitEvent() + # TODO: figure out if we actually need this 'event' variable, likely we do + event = AnalysisCompletionEvent(self, lambda: wait.complete()) + core.BNUpdateAnalysis(self.handle) + wait.wait() + + 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, addr, plat=None): + """ + ``get_function_at`` gets a binaryninja.Function object for the function at the virtual address ``addr``: + + :param int addr: virtual address of the desired function + :param Platform plat: plat of the desired function + :return: returns a Function object or None for the function at the virtual address provided + :rtype: Function + :Example: + + >>> bv.get_function_at(bv.entry_point) + <func: x86_64@0x100001174> + >>> + """ + if plat is None: + plat = self.platform + func = core.BNGetAnalysisFunction(self.handle, plat.handle, addr) + if func is None: + return None + return function.Function(self, func) + + 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): + """ + ``get_code_refs`` returns a list of ReferenceSource objects (xrefs or cross-references) that point to the provided virtual address. + + :param int addr: virtual address to query for references + :return: List of References for the given virtual address + :rtype: list(ReferenceSource) + :Example: + + >>> bv.get_code_refs(here) + [<ref: x86@0x4165ff>] + >>> + + """ + 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 types.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 types.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(types.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(types.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(SymbolType.ImportAddressSymbol, 0x10002028, 1) + [<ImportAddressSymbol: "KERNEL32!GetCurrentThreadId@IAT" @ 0x10002028>] + >>> + """ + if isinstance(sym_type, str): + sym_type = SymbolType[sym_type] + count = ctypes.c_ulonglong(0) + if start is None: + syms = core.BNGetSymbolsOfType(self.handle, sym_type, count) + else: + syms = core.BNGetSymbolsOfTypeInRange(self.handle, sym_type, start, length, count) + result = [] + for i in xrange(0, count.value): + result.append(types.Symbol(None, None, None, handle = core.BNNewSymbolReference(syms[i]))) + core.BNFreeSymbolList(syms, count.value) + return result + + 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 define_auto_symbol_and_var_or_function(self, sym, sym_type, plat=None): + """ + ``define_auto_symbol_and_var_or_function`` + + :param Symbol sym: the symbol to define + :param SymbolType sym_type: Type of symbol being defined + :param Platform plat: (optional) platform + :rtype: None + """ + if plat is None: + plat = self.plat + if plat is not None: + plat = plat.handle + if sym_type is not None: + sym_type = sym_type.handle + core.BNDefineAutoSymbolAndVariableOrFunction(self.handle, plat, sym.handle, sym_type) + + 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, addr, arch=None): + """ + ``is_never_branch_patch_available`` queries the architecture plugin to determine if the instruction at the + instruction at ``addr`` can be made to **never branch**. The actual logic of which is implemented in the + ``perform_is_never_branch_patch_available`` in the corresponding architecture. + + :param int addr: the virtual address of the instruction to be patched + :param Architecture arch: (optional) the architecture of the instructions if different from the default + :return: True if the instruction can be patched, False otherwise + :rtype: bool + :Example: + + >>> bv.get_disassembly(0x100012ed) + 'test eax, eax' + >>> bv.is_never_branch_patch_available(0x100012ed) + False + >>> bv.get_disassembly(0x100012ef) + 'jg 0x100012f5' + >>> bv.is_never_branch_patch_available(0x100012ef) + True + >>> + """ + if arch is None: + arch = self.arch + return core.BNIsNeverBranchPatchAvailable(self.handle, arch.handle, addr) + + def is_always_branch_patch_available(self, addr, arch=None): + """ + ``is_always_branch_patch_available`` queries the architecture plugin to determine if the + instruction at ``addr`` can be made to **always branch**. The actual logic of which is implemented in the + ``perform_is_always_branch_patch_available`` in the corresponding architecture. + + :param int addr: the virtual address of the instruction to be patched + :param Architecture arch: (optional) the architecture for the current view + :return: True if the instruction can be patched, False otherwise + :rtype: bool + :Example: + + >>> bv.get_disassembly(0x100012ed) + 'test eax, eax' + >>> bv.is_always_branch_patch_available(0x100012ed) + False + >>> bv.get_disassembly(0x100012ef) + 'jg 0x100012f5' + >>> bv.is_always_branch_patch_available(0x100012ef) + True + >>> + """ + if arch is None: + arch = self.arch + return core.BNIsAlwaysBranchPatchAvailable(self.handle, arch.handle, addr) + + def is_invert_branch_patch_available(self, addr, arch=None): + """ + ``is_invert_branch_patch_available`` queries the architecture plugin to determine if the instruction at ``addr`` + is a branch that can be inverted. The actual logic of which is implemented in the + ``perform_is_invert_branch_patch_available`` in the corresponding architecture. + + :param int addr: the virtual address of the instruction to be patched + :param Architecture arch: (optional) the architecture of the instructions if different from the default + :return: True if the instruction can be patched, False otherwise + :rtype: bool + :Example: + + >>> bv.get_disassembly(0x100012ed) + 'test eax, eax' + >>> bv.is_invert_branch_patch_available(0x100012ed) + False + >>> bv.get_disassembly(0x100012ef) + 'jg 0x100012f5' + >>> bv.is_invert_branch_patch_available(0x100012ef) + True + >>> + """ + if arch is None: + arch = self.arch + return core.BNIsInvertBranchPatchAvailable(self.handle, arch.handle, addr) + + def is_skip_and_return_zero_patch_available(self, addr, arch=None): + """ + ``is_skip_and_return_zero_patch_available`` queries the architecture plugin to determine if the + instruction at ``addr`` is similar to an x86 "call" instruction which can be made to return zero. The actual + logic of which is implemented in the ``perform_is_skip_and_return_zero_patch_available`` in the corresponding + architecture. + + :param int addr: the virtual address of the instruction to be patched + :param Architecture arch: (optional) the architecture of the instructions if different from the default + :return: True if the instruction can be patched, False otherwise + :rtype: bool + :Example: + + >>> bv.get_disassembly(0x100012f6) + 'mov dword [0x10003020], eax' + >>> bv.is_skip_and_return_zero_patch_available(0x100012f6) + False + >>> bv.get_disassembly(0x100012fb) + 'call 0x10001629' + >>> bv.is_skip_and_return_zero_patch_available(0x100012fb) + True + >>> + """ + if arch is None: + arch = self.arch + return core.BNIsSkipAndReturnZeroPatchAvailable(self.handle, arch.handle, addr) + + def is_skip_and_return_value_patch_available(self, addr, arch=None): + """ + ``is_skip_and_return_value_patch_available`` queries the architecture plugin to determine if the + instruction at ``addr`` is similar to an x86 "call" instruction which can be made to return a value. The actual + logic of which is implemented in the ``perform_is_skip_and_return_value_patch_available`` in the corresponding + architecture. + + :param int addr: the virtual address of the instruction to be patched + :param Architecture arch: (optional) the architecture of the instructions if different from the default + :return: True if the instruction can be patched, False otherwise + :rtype: bool + :Example: + + >>> bv.get_disassembly(0x100012f6) + 'mov dword [0x10003020], eax' + >>> bv.is_skip_and_return_value_patch_available(0x100012f6) + False + >>> bv.get_disassembly(0x100012fb) + 'call 0x10001629' + >>> bv.is_skip_and_return_value_patch_available(0x100012fb) + True + >>> + """ + if arch is None: + arch = self.arch + return core.BNIsSkipAndReturnValuePatchAvailable(self.handle, arch.handle, addr) + + def convert_to_nop(self, addr, arch=None): + """ + ``convert_to_nop`` converts the instruction at virtual address ``addr`` to a nop of the provided architecture. + + .. note:: This API performs a binary patch, analysis may need to be updated afterward. Additionally the binary\ + file must be saved in order to preserve the changes made. + + :param int addr: virtual address of the instruction to conver to nops + :param Architecture arch: (optional) the architecture of the instructions if different from the default + :return: True on success, False on falure. + :rtype: bool + :Example: + + >>> bv.get_disassembly(0x100012fb) + 'call 0x10001629' + >>> bv.convert_to_nop(0x100012fb) + True + >>> #The above 'call' instruction is 5 bytes, a nop in x86 is 1 byte, + >>> # thus 5 nops are used: + >>> bv.get_disassembly(0x100012fb) + 'nop' + >>> bv.get_next_disassembly() + 'nop' + >>> bv.get_next_disassembly() + 'nop' + >>> bv.get_next_disassembly() + 'nop' + >>> bv.get_next_disassembly() + 'nop' + >>> bv.get_next_disassembly() + 'mov byte [ebp-0x1c], al' + """ + if arch is None: + arch = self.arch + return core.BNConvertToNop(self.handle, arch.handle, addr) + + def always_branch(self, addr, arch=None): + """ + ``always_branch`` convert the instruction of architecture ``arch`` at the virtual address ``addr`` to an + unconditional branch. + + .. note:: This API performs a binary patch, analysis may need to be updated afterward. Additionally the binary\ + file must be saved in order to preserve the changes made. + + :param int addr: virtual address of the instruction to be modified + :param Architecture arch: (optional) the architecture of the instructions if different from the default + :return: True on success, False on falure. + :rtype: bool + :Example: + + >>> bv.get_disassembly(0x100012ef) + 'jg 0x100012f5' + >>> bv.always_branch(0x100012ef) + True + >>> bv.get_disassembly(0x100012ef) + 'jmp 0x100012f5' + >>> + """ + if arch is None: + arch = self.arch + return core.BNAlwaysBranch(self.handle, arch.handle, addr) + + def never_branch(self, addr, arch=None): + """ + ``never_branch`` convert the branch instruction of architecture ``arch`` at the virtual address ``addr`` to + a fall through. + + .. note:: This API performs a binary patch, analysis may need to be updated afterward. Additionally the binary\ + file must be saved in order to preserve the changes made. + + :param int addr: virtual address of the instruction to be modified + :param Architecture arch: (optional) the architecture of the instructions if different from the default + :return: True on success, False on falure. + :rtype: bool + :Example: + + >>> bv.get_disassembly(0x1000130e) + 'jne 0x10001317' + >>> bv.never_branch(0x1000130e) + True + >>> bv.get_disassembly(0x1000130e) + 'nop' + >>> + """ + if arch is None: + arch = self.arch + return core.BNConvertToNop(self.handle, arch.handle, addr) + + def invert_branch(self, addr, arch=None): + """ + ``invert_branch`` convert the branch instruction of architecture ``arch`` at the virtual address ``addr`` to the + inverse branch. + + .. note:: This API performs a binary patch, analysis may need to be updated afterward. Additionally the binary + file must be saved in order to preserve the changes made. + + :param int addr: virtual address of the instruction to be modified + :param Architecture arch: (optional) the architecture of the instructions if different from the default + :return: True on success, False on falure. + :rtype: bool + :Example: + + >>> bv.get_disassembly(0x1000130e) + 'je 0x10001317' + >>> bv.invert_branch(0x1000130e) + True + >>> + >>> bv.get_disassembly(0x1000130e) + 'jne 0x10001317' + >>> + """ + if arch is None: + arch = self.arch + return core.BNInvertBranch(self.handle, arch.handle, addr) + + def skip_and_return_value(self, addr, value, arch=None): + """ + ``skip_and_return_value`` convert the ``call`` instruction of architecture ``arch`` at the virtual address + ``addr`` to the equivilent of returning a value. + + :param int addr: virtual address of the instruction to be modified + :param int value: value to make the instruction *return* + :param Architecture arch: (optional) the architecture of the instructions if different from the default + :return: True on success, False on falure. + :rtype: bool + :Example: + + >>> bv.get_disassembly(0x1000132a) + 'call 0x1000134a' + >>> bv.skip_and_return_value(0x1000132a, 42) + True + >>> #The return value from x86 functions is stored in eax thus: + >>> bv.get_disassembly(0x1000132a) + 'mov eax, 0x2a' + >>> + """ + if arch is None: + arch = self.arch + return core.BNSkipAndReturnValue(self.handle, arch.handle, addr, value) + + def get_instruction_length(self, addr, arch=None): + """ + ``get_instruction_length`` returns the number of bytes in the instruction of Architecture ``arch`` at the virtual + address ``addr`` + + :param int addr: virtual address of the instruction query + :param Architecture arch: (optional) the architecture of the instructions if different from the default + :return: Number of bytes in instruction + :rtype: int + :Example: + + >>> bv.get_disassembly(0x100012f1) + 'xor eax, eax' + >>> bv.get_instruction_length(0x100012f1) + 2L + >>> + """ + if arch is None: + arch = self.arch + return core.BNGetInstructionLength(self.handle, arch.handle, addr) + + 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(StringType(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 = InstructionTextTokenType(lines[i].contents.tokens[j].type) + text = lines[i].contents.tokens[j].text + value = lines[i].contents.tokens[j].value + size = lines[i].contents.tokens[j].size + operand = lines[i].contents.tokens[j].operand + 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 type name + :rtype: tuple(Type, QualifiedName) + :Example: + + >>> bv.parse_type_string("int foo") + (<type: int32_t>, 'foo') + >>> + """ + result = core.BNQualifiedNameAndType() + errors = ctypes.c_char_p() + if not core.BNParseTypeString(self.handle, text, result, errors): + error_str = errors.value + core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) + raise SyntaxError(error_str) + type_obj = types.Type(core.BNNewTypeReference(result.type)) + name = types.QualifiedName._from_core_struct(result.name) + core.BNFreeQualifiedNameAndType(result) + return type_obj, name + + def get_type_by_name(self, name): + """ + ``get_type_by_name`` returns the defined type whose name corresponds with the provided ``name`` + + :param QualifiedName name: Type name to lookup + :return: A :py:Class:`Type` or None if the type does not exist + :rtype: Type or None + :Example: + + >>> type, name = bv.parse_type_string("int foo") + >>> bv.define_type(name, type) + >>> bv.get_type_by_name(name) + <type: int32_t> + >>> + """ + name = types.QualifiedName(name)._get_core_struct() + obj = core.BNGetAnalysisTypeByName(self.handle, name) + if not obj: + return None + return types.Type(obj) + + def is_type_auto_defined(self, name): + """ + ``is_type_auto_defined`` queries the user type list of name. If name is not in the *user* type list then the name + is considered an *auto* type. + + :param QualifiedName name: Name of type to query + :return: True if the type is not a *user* type. False if the type is a *user* type. + :Example: + >>> bv.is_type_auto_defined("foo") + True + >>> bv.define_user_type("foo", bv.parse_type_string("struct {int x,y;}")[0]) + >>> bv.is_type_auto_defined("foo") + False + >>> + """ + name = types.QualifiedName(name)._get_core_struct() + return core.BNIsAnalysisTypeAutoDefined(self.handle, name) + + 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 QualifiedName 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> + """ + name = types.QualifiedName(name)._get_core_struct() + 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 QualifiedName name: Name of the user type to be registered + :param Type type_obj: Type object to be registered + :rtype: None + :Example: + + >>> type, name = bv.parse_type_string("int foo") + >>> bv.define_user_type(name, type) + >>> bv.get_type_by_name(name) + <type: int32_t> + """ + name = types.QualifiedName(name)._get_core_struct() + core.BNDefineUserAnalysisType(self.handle, name, type_obj.handle) + + 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 QualifiedName 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) + >>> + """ + name = types.QualifiedName(name)._get_core_struct() + 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 QualifiedName 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) + >>> + """ + name = types.QualifiedName(name)._get_core_struct() + 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, Endianness.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, Endianness.BigEndian) + >>> bw = BinaryWriter(bv, Endianness.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): + """ + ``write16`` writes the lowest order two bytes from the integer ``value`` to the current offset, using internal endianness. + + :param int value: integer value to write. + :return: boolean True on success, False on failure. + :rtype: bool + """ + return core.BNWrite16(self.handle, value) + + def write32(self, value): + """ + ``write32`` writes the lowest order four bytes from the integer ``value`` to the current offset, using internal endianness. + + :param int value: integer value to write. + :return: boolean True on success, False on failure. + :rtype: bool + """ + return core.BNWrite32(self.handle, value) + + def write64(self, value): + """ + ``write64`` writes the lowest order eight bytes from the integer ``value`` to the current offset, using internal endianness. + + :param int value: integer value to write. + :return: boolean True on success, False on failure. + :rtype: bool + """ + return core.BNWrite64(self.handle, value) + + 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) diff --git a/python/callingconvention.py b/python/callingconvention.py new file mode 100644 index 00000000..5f4adeab --- /dev/null +++ b/python/callingconvention.py @@ -0,0 +1,212 @@ +# 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 traceback +import ctypes + +# Binary Ninja components +import _binaryninjacore as core +import architecture +import log + + +class CallingConvention(object): + name = None + caller_saved_regs = [] + int_arg_regs = [] + float_arg_regs = [] + arg_regs_share_index = False + stack_reserved_for_arg_regs = False + int_return_reg = None + high_int_return_reg = None + float_return_reg = None + + _registered_calling_conventions = [] + + def __init__(self, arch, handle = None): + if handle is None: + self.arch = arch + self._pending_reg_lists = {} + self._cb = core.BNCustomCallingConvention() + self._cb.context = 0 + self._cb.getCallerSavedRegisters = self._cb.getCallerSavedRegisters.__class__(self._get_caller_saved_regs) + self._cb.getIntegerArgumentRegisters = self._cb.getIntegerArgumentRegisters.__class__(self._get_int_arg_regs) + self._cb.getFloatArgumentRegisters = self._cb.getFloatArgumentRegisters.__class__(self._get_float_arg_regs) + self._cb.freeRegisterList = self._cb.freeRegisterList.__class__(self._free_register_list) + self._cb.areArgumentRegistersSharedIndex = self._cb.areArgumentRegistersSharedIndex.__class__(self._arg_regs_share_index) + self._cb.isStackReservedForArgumentRegisters = self._cb.isStackReservedForArgumentRegisters.__class__(self._stack_reserved_for_arg_regs) + self._cb.getIntegerReturnValueRegister = self._cb.getIntegerReturnValueRegister.__class__(self._get_int_return_reg) + self._cb.getHighIntegerReturnValueRegister = self._cb.getHighIntegerReturnValueRegister.__class__(self._get_high_int_return_reg) + self._cb.getFloatReturnValueRegister = self._cb.getFloatReturnValueRegister.__class__(self._get_float_return_reg) + self.handle = core.BNCreateCallingConvention(arch.handle, self.__class__.name, self._cb) + self.__class__._registered_calling_conventions.append(self) + else: + self.handle = handle + self.arch = architecture.Architecture(core.BNGetCallingConventionArchitecture(self.handle)) + self.__dict__["name"] = core.BNGetCallingConventionName(self.handle) + self.__dict__["arg_regs_share_index"] = core.BNAreArgumentRegistersSharedIndex(self.handle) + self.__dict__["stack_reserved_for_arg_regs"] = core.BNIsStackReservedForArgumentRegisters(self.handle) + + count = ctypes.c_ulonglong() + regs = core.BNGetCallerSavedRegisters(self.handle, count) + result = [] + arch = self.arch + for i in xrange(0, count.value): + result.append(arch.get_reg_name(regs[i])) + core.BNFreeRegisterList(regs, count.value) + self.__dict__["caller_saved_regs"] = result + + count = ctypes.c_ulonglong() + regs = core.BNGetIntegerArgumentRegisters(self.handle, count) + result = [] + arch = self.arch + for i in xrange(0, count.value): + result.append(arch.get_reg_name(regs[i])) + core.BNFreeRegisterList(regs, count.value) + self.__dict__["int_arg_regs"] = result + + count = ctypes.c_ulonglong() + regs = core.BNGetFloatArgumentRegisters(self.handle, count) + result = [] + arch = self.arch + for i in xrange(0, count.value): + result.append(arch.get_reg_name(regs[i])) + core.BNFreeRegisterList(regs, count.value) + self.__dict__["float_arg_regs"] = result + + reg = core.BNGetIntegerReturnValueRegister(self.handle) + if reg == 0xffffffff: + self.__dict__["int_return_reg"] = None + else: + self.__dict__["int_return_reg"] = self.arch.get_reg_name(reg) + + reg = core.BNGetHighIntegerReturnValueRegister(self.handle) + if reg == 0xffffffff: + self.__dict__["high_int_return_reg"] = None + else: + self.__dict__["high_int_return_reg"] = self.arch.get_reg_name(reg) + + reg = core.BNGetFloatReturnValueRegister(self.handle) + if reg == 0xffffffff: + self.__dict__["float_return_reg"] = None + else: + self.__dict__["float_return_reg"] = self.arch.get_reg_name(reg) + + def __del__(self): + core.BNFreeCallingConvention(self.handle) + + def _get_caller_saved_regs(self, ctxt, count): + try: + regs = self.__class__.caller_saved_regs + count[0] = len(regs) + reg_buf = (ctypes.c_uint * len(regs))() + for i in xrange(0, len(regs)): + reg_buf[i] = self.arch.regs[regs[i]].index + result = ctypes.cast(reg_buf, ctypes.c_void_p) + self._pending_reg_lists[result.value] = (result, reg_buf) + return result.value + except: + log.log_error(traceback.format_exc()) + count[0] = 0 + return None + + def _get_int_arg_regs(self, ctxt, count): + try: + regs = self.__class__.int_arg_regs + count[0] = len(regs) + reg_buf = (ctypes.c_uint * len(regs))() + for i in xrange(0, len(regs)): + reg_buf[i] = self.arch.regs[regs[i]].index + result = ctypes.cast(reg_buf, ctypes.c_void_p) + self._pending_reg_lists[result.value] = (result, reg_buf) + return result.value + except: + log.log_error(traceback.format_exc()) + count[0] = 0 + return None + + def _get_float_arg_regs(self, ctxt, count): + try: + regs = self.__class__.float_arg_regs + count[0] = len(regs) + reg_buf = (ctypes.c_uint * len(regs))() + for i in xrange(0, len(regs)): + reg_buf[i] = self.arch.regs[regs[i]].index + result = ctypes.cast(reg_buf, ctypes.c_void_p) + self._pending_reg_lists[result.value] = (result, reg_buf) + return result.value + except: + log.log_error(traceback.format_exc()) + count[0] = 0 + return None + + def _free_register_list(self, ctxt, regs): + try: + buf = ctypes.cast(regs, ctypes.c_void_p) + if buf.value not in self._pending_reg_lists: + raise ValueError("freeing register list that wasn't allocated") + del self._pending_reg_lists[buf.value] + except: + log.log_error(traceback.format_exc()) + + def _arg_regs_share_index(self, ctxt): + try: + return self.__class__.arg_regs_share_index + except: + log.log_error(traceback.format_exc()) + return False + + def _stack_reserved_for_arg_regs(self, ctxt): + try: + return self.__class__.stack_reserved_for_arg_regs + except: + log.log_error(traceback.format_exc()) + return False + + def _get_int_return_reg(self, ctxt): + try: + return self.arch.regs[self.__class__.int_return_reg].index + except: + log.log_error(traceback.format_exc()) + return False + + def _get_high_int_return_reg(self, ctxt): + try: + if self.__class__.high_int_return_reg is None: + return 0xffffffff + return self.arch.regs[self.__class__.high_int_return_reg].index + except: + log.log_error(traceback.format_exc()) + return False + + def _get_float_return_reg(self, ctxt): + try: + if self.__class__.float_return_reg is None: + return 0xffffffff + return self.arch.regs[self.__class__.float_int_return_reg].index + except: + log.log_error(traceback.format_exc()) + return False + + def __repr__(self): + return "<calling convention: %s %s>" % (self.arch.name, self.name) + + def __str__(self): + return self.name diff --git a/python/databuffer.py b/python/databuffer.py new file mode 100644 index 00000000..6b3423da --- /dev/null +++ b/python/databuffer.py @@ -0,0 +1,145 @@ +# 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 ctypes + +# Binary Ninja components +import _binaryninjacore as core + + +class DataBuffer(object): + def __init__(self, contents="", handle=None): + if handle is not None: + self.handle = core.handle_of_type(handle, core.BNDataBuffer) + elif isinstance(contents, int) or isinstance(contents, long): + self.handle = core.BNCreateDataBuffer(None, contents) + elif isinstance(contents, DataBuffer): + self.handle = core.BNDuplicateDataBuffer(contents.handle) + else: + self.handle = core.BNCreateDataBuffer(contents, len(contents)) + + def __del__(self): + core.BNFreeDataBuffer(self.handle) + + def __len__(self): + return int(core.BNGetDataBufferLength(self.handle)) + + def __getitem__(self, i): + if isinstance(i, tuple): + result = "" + source = str(self) + for s in i: + result += source[s] + return result + elif isinstance(i, slice): + if i.step is not None: + i = i.indices(len(self)) + start = i[0] + stop = i[1] + if stop <= start: + return "" + buf = ctypes.create_string_buffer(stop - start) + ctypes.memmove(buf, core.BNGetDataBufferContentsAt(self.handle, start), stop - start) + return buf.raw + else: + return str(self)[i] + elif i < 0: + if i >= -len(self): + return chr(core.BNGetDataBufferByte(self.handle, int(len(self) + i))) + raise IndexError("index out of range") + elif i < len(self): + return chr(core.BNGetDataBufferByte(self.handle, int(i))) + 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(len(self)) + start = i[0] + stop = i[1] + if stop < start: + stop = start + if len(value) != (stop - start): + data = str(self) + data = data[0:start] + value + data[stop:] + core.BNSetDataBufferContents(self.handle, data, len(data)) + else: + value = str(value) + buf = ctypes.create_string_buffer(value) + ctypes.memmove(core.BNGetDataBufferContentsAt(self.handle, start), buf, len(value)) + elif i < 0: + if i >= -len(self): + if len(value) != 1: + raise ValueError("expected single byte for assignment") + value = str(value) + buf = ctypes.create_string_buffer(value) + ctypes.memmove(core.BNGetDataBufferContentsAt(self.handle, int(len(self) + i)), buf, 1) + else: + raise IndexError("index out of range") + elif i < len(self): + if len(value) != 1: + raise ValueError("expected single byte for assignment") + value = str(value) + buf = ctypes.create_string_buffer(value) + ctypes.memmove(core.BNGetDataBufferContentsAt(self.handle, int(i)), buf, 1) + else: + raise IndexError("index out of range") + + def __str__(self): + buf = ctypes.create_string_buffer(len(self)) + ctypes.memmove(buf, core.BNGetDataBufferContents(self.handle), len(self)) + return buf.raw + + def __repr__(self): + return repr(str(self)) + + def escape(self): + return core.BNDataBufferToEscapedString(self.handle) + + def unescape(self): + return DataBuffer(handle=core.BNDecodeEscapedString(str(self))) + + def base64_encode(self): + return core.BNDataBufferToBase64(self.handle) + + def base64_decode(self): + return DataBuffer(handle = core.BNDecodeBase64(str(self))) + + def zlib_compress(self): + buf = core.BNZlibCompress(self.handle) + if buf is None: + return None + return DataBuffer(handle = buf) + + def zlib_decompress(self): + buf = core.BNZlibDecompress(self.handle) + if buf is None: + return None + return DataBuffer(handle = buf) + + +def escape_string(text): + return DataBuffer(text).escape() + + +def unescape_string(text): + return DataBuffer(text).unescape() diff --git a/python/demangle.py b/python/demangle.py new file mode 100644 index 00000000..ed38674a --- /dev/null +++ b/python/demangle.py @@ -0,0 +1,83 @@ +# 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 ctypes + +# Binary Ninja components +import _binaryninjacore as core +import types + + +def get_qualified_name(names): + """ + ``get_qualified_name`` gets a qualified name for the provied name list. + + :param list(str) names: name list to qualify + :return: a qualified name + :rtype: str + :Example: + + >>> type, name = demangle_ms(Architecture["x86_64"], "?testf@Foobar@@SA?AW4foo@1@W421@@Z") + >>> get_qualified_name(name) + 'Foobar::testf' + >>> + """ + return "::".join(names) + + +def demangle_ms(arch, mangled_name): + """ + ``demangle_ms`` demangles a mangled Microsoft Visual Studio C++ name to a Type object. + + :param Architecture arch: Architecture for the symbol. Required for pointer and integer sizes. + :param str mangled_name: a mangled Microsoft Visual Studio C++ name + :return: returns a Type object for the mangled name + :rtype: Type + :Example: + + >>> demangle_ms(Architecture["x86_64"], "?testf@Foobar@@SA?AW4foo@1@W421@@Z") + (<type: public: static enum Foobar::foo __cdecl (enum Foobar::foo)>, ['Foobar', 'testf']) + >>> + """ + handle = ctypes.POINTER(core.BNType)() + outName = ctypes.POINTER(ctypes.c_char_p)() + outSize = ctypes.c_ulonglong() + names = [] + if core.BNDemangleMS(arch.handle, mangled_name, ctypes.byref(handle), ctypes.byref(outName), ctypes.byref(outSize)): + for i in xrange(outSize.value): + names.append(outName[i]) + core.BNFreeDemangledName(ctypes.byref(outName), outSize.value) + return (types.Type(handle), names) + return (None, mangled_name) + + +def demangle_gnu3(arch, mangled_name): + handle = ctypes.POINTER(core.BNType)() + outName = ctypes.POINTER(ctypes.c_char_p)() + outSize = ctypes.c_ulonglong() + names = [] + if core.BNDemangleGNU3(arch.handle, mangled_name, ctypes.byref(handle), ctypes.byref(outName), ctypes.byref(outSize)): + for i in xrange(outSize.value): + names.append(outName[i]) + core.BNFreeDemangledName(ctypes.byref(outName), outSize.value) + if not handle: + return (None, names) + return (types.Type(handle), names) + return (None, mangled_name) diff --git a/python/enum/LICENSE b/python/enum/LICENSE new file mode 100644 index 00000000..9003b885 --- /dev/null +++ b/python/enum/LICENSE @@ -0,0 +1,32 @@ +Copyright (c) 2013, Ethan Furman. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + + Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials + provided with the distribution. + + Neither the name Ethan Furman nor the names of any + contributors may be used to endorse or promote products + derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/python/enum/README b/python/enum/README new file mode 100644 index 00000000..aa2333d8 --- /dev/null +++ b/python/enum/README @@ -0,0 +1,3 @@ +enum34 is the new Python stdlib enum module available in Python 3.4 +backported for previous versions of Python from 2.4 to 3.3. +tested on 2.6, 2.7, and 3.3+ diff --git a/python/enum/__init__.py b/python/enum/__init__.py new file mode 100644 index 00000000..d6ffb3a4 --- /dev/null +++ b/python/enum/__init__.py @@ -0,0 +1,837 @@ +"""Python Enumerations""" + +import sys as _sys + +__all__ = ['Enum', 'IntEnum', 'unique'] + +version = 1, 1, 6 + +pyver = float('%s.%s' % _sys.version_info[:2]) + +try: + any +except NameError: + def any(iterable): + for element in iterable: + if element: + return True + return False + +try: + from collections import OrderedDict +except ImportError: + OrderedDict = None + +try: + basestring +except NameError: + # In Python 2 basestring is the ancestor of both str and unicode + # in Python 3 it's just str, but was missing in 3.1 + basestring = str + +try: + unicode +except NameError: + # In Python 3 unicode no longer exists (it's just str) + unicode = str + +class _RouteClassAttributeToGetattr(object): + """Route attribute access on a class to __getattr__. + + This is a descriptor, used to define attributes that act differently when + accessed through an instance and through a class. Instance access remains + normal, but access to an attribute through a class will be routed to the + class's __getattr__ method; this is done by raising AttributeError. + + """ + def __init__(self, fget=None): + self.fget = fget + + def __get__(self, instance, ownerclass=None): + if instance is None: + raise AttributeError() + return self.fget(instance) + + def __set__(self, instance, value): + raise AttributeError("can't set attribute") + + def __delete__(self, instance): + raise AttributeError("can't delete attribute") + + +def _is_descriptor(obj): + """Returns True if obj is a descriptor, False otherwise.""" + return ( + hasattr(obj, '__get__') or + hasattr(obj, '__set__') or + hasattr(obj, '__delete__')) + + +def _is_dunder(name): + """Returns True if a __dunder__ name, False otherwise.""" + return (name[:2] == name[-2:] == '__' and + name[2:3] != '_' and + name[-3:-2] != '_' and + len(name) > 4) + + +def _is_sunder(name): + """Returns True if a _sunder_ name, False otherwise.""" + return (name[0] == name[-1] == '_' and + name[1:2] != '_' and + name[-2:-1] != '_' and + len(name) > 2) + + +def _make_class_unpicklable(cls): + """Make the given class un-picklable.""" + def _break_on_call_reduce(self, protocol=None): + raise TypeError('%r cannot be pickled' % self) + cls.__reduce_ex__ = _break_on_call_reduce + cls.__module__ = '<unknown>' + + +class _EnumDict(dict): + """Track enum member order and ensure member names are not reused. + + EnumMeta will use the names found in self._member_names as the + enumeration member names. + + """ + def __init__(self): + super(_EnumDict, self).__init__() + self._member_names = [] + + def __setitem__(self, key, value): + """Changes anything not dundered or not a descriptor. + + If a descriptor is added with the same name as an enum member, the name + is removed from _member_names (this may leave a hole in the numerical + sequence of values). + + If an enum member name is used twice, an error is raised; duplicate + values are not checked for. + + Single underscore (sunder) names are reserved. + + Note: in 3.x __order__ is simply discarded as a not necessary piece + leftover from 2.x + + """ + if pyver >= 3.0 and key in ('_order_', '__order__'): + return + elif key == '__order__': + key = '_order_' + if _is_sunder(key): + if key != '_order_': + raise ValueError('_names_ are reserved for future Enum use') + elif _is_dunder(key): + pass + elif key in self._member_names: + # descriptor overwriting an enum? + raise TypeError('Attempted to reuse key: %r' % key) + elif not _is_descriptor(value): + if key in self: + # enum overwriting a descriptor? + raise TypeError('Key already defined as: %r' % self[key]) + self._member_names.append(key) + super(_EnumDict, self).__setitem__(key, value) + + +# Dummy value for Enum as EnumMeta explicity checks for it, but of course until +# EnumMeta finishes running the first time the Enum class doesn't exist. This +# is also why there are checks in EnumMeta like `if Enum is not None` +Enum = None + + +class EnumMeta(type): + """Metaclass for Enum""" + @classmethod + def __prepare__(metacls, cls, bases): + return _EnumDict() + + def __new__(metacls, cls, bases, classdict): + # an Enum class is final once enumeration items have been defined; it + # cannot be mixed with other types (int, float, etc.) if it has an + # inherited __new__ unless a new __new__ is defined (or the resulting + # class will fail). + if type(classdict) is dict: + original_dict = classdict + classdict = _EnumDict() + for k, v in original_dict.items(): + classdict[k] = v + + member_type, first_enum = metacls._get_mixins_(bases) + __new__, save_new, use_args = metacls._find_new_(classdict, member_type, + first_enum) + # save enum items into separate mapping so they don't get baked into + # the new class + members = dict((k, classdict[k]) for k in classdict._member_names) + for name in classdict._member_names: + del classdict[name] + + # py2 support for definition order + _order_ = classdict.get('_order_') + if _order_ is None: + if pyver < 3.0: + try: + _order_ = [name for (name, value) in sorted(members.items(), key=lambda item: item[1])] + except TypeError: + _order_ = [name for name in sorted(members.keys())] + else: + _order_ = classdict._member_names + else: + del classdict['_order_'] + if pyver < 3.0: + _order_ = _order_.replace(',', ' ').split() + aliases = [name for name in members if name not in _order_] + _order_ += aliases + + # check for illegal enum names (any others?) + invalid_names = set(members) & set(['mro']) + if invalid_names: + raise ValueError('Invalid enum member name(s): %s' % ( + ', '.join(invalid_names), )) + + # save attributes from super classes so we know if we can take + # the shortcut of storing members in the class dict + base_attributes = set([a for b in bases for a in b.__dict__]) + # create our new Enum type + enum_class = super(EnumMeta, metacls).__new__(metacls, cls, bases, classdict) + enum_class._member_names_ = [] # names in random order + if OrderedDict is not None: + enum_class._member_map_ = OrderedDict() + else: + enum_class._member_map_ = {} # name->value map + enum_class._member_type_ = member_type + + # Reverse value->name map for hashable values. + enum_class._value2member_map_ = {} + + # instantiate them, checking for duplicates as we go + # we instantiate first instead of checking for duplicates first in case + # a custom __new__ is doing something funky with the values -- such as + # auto-numbering ;) + if __new__ is None: + __new__ = enum_class.__new__ + for member_name in _order_: + value = members[member_name] + if not isinstance(value, tuple): + args = (value, ) + else: + args = value + if member_type is tuple: # special case for tuple enums + args = (args, ) # wrap it one more time + if not use_args or not args: + enum_member = __new__(enum_class) + if not hasattr(enum_member, '_value_'): + enum_member._value_ = value + else: + enum_member = __new__(enum_class, *args) + if not hasattr(enum_member, '_value_'): + enum_member._value_ = member_type(*args) + value = enum_member._value_ + enum_member._name_ = member_name + enum_member.__objclass__ = enum_class + enum_member.__init__(*args) + # If another member with the same value was already defined, the + # new member becomes an alias to the existing one. + for name, canonical_member in enum_class._member_map_.items(): + if canonical_member.value == enum_member._value_: + enum_member = canonical_member + break + else: + # Aliases don't appear in member names (only in __members__). + enum_class._member_names_.append(member_name) + # performance boost for any member that would not shadow + # a DynamicClassAttribute (aka _RouteClassAttributeToGetattr) + if member_name not in base_attributes: + setattr(enum_class, member_name, enum_member) + # now add to _member_map_ + enum_class._member_map_[member_name] = enum_member + try: + # This may fail if value is not hashable. We can't add the value + # to the map, and by-value lookups for this value will be + # linear. + enum_class._value2member_map_[value] = enum_member + except TypeError: + pass + + + # If a custom type is mixed into the Enum, and it does not know how + # to pickle itself, pickle.dumps will succeed but pickle.loads will + # fail. Rather than have the error show up later and possibly far + # from the source, sabotage the pickle protocol for this class so + # that pickle.dumps also fails. + # + # However, if the new class implements its own __reduce_ex__, do not + # sabotage -- it's on them to make sure it works correctly. We use + # __reduce_ex__ instead of any of the others as it is preferred by + # pickle over __reduce__, and it handles all pickle protocols. + unpicklable = False + if '__reduce_ex__' not in classdict: + if member_type is not object: + methods = ('__getnewargs_ex__', '__getnewargs__', + '__reduce_ex__', '__reduce__') + if not any(m in member_type.__dict__ for m in methods): + _make_class_unpicklable(enum_class) + unpicklable = True + + + # double check that repr and friends are not the mixin's or various + # things break (such as pickle) + for name in ('__repr__', '__str__', '__format__', '__reduce_ex__'): + class_method = getattr(enum_class, name) + obj_method = getattr(member_type, name, None) + enum_method = getattr(first_enum, name, None) + if name not in classdict and class_method is not enum_method: + if name == '__reduce_ex__' and unpicklable: + continue + setattr(enum_class, name, enum_method) + + # method resolution and int's are not playing nice + # Python's less than 2.6 use __cmp__ + + if pyver < 2.6: + + if issubclass(enum_class, int): + setattr(enum_class, '__cmp__', getattr(int, '__cmp__')) + + elif pyver < 3.0: + + if issubclass(enum_class, int): + for method in ( + '__le__', + '__lt__', + '__gt__', + '__ge__', + '__eq__', + '__ne__', + '__hash__', + ): + setattr(enum_class, method, getattr(int, method)) + + # replace any other __new__ with our own (as long as Enum is not None, + # anyway) -- again, this is to support pickle + if Enum is not None: + # if the user defined their own __new__, save it before it gets + # clobbered in case they subclass later + if save_new: + setattr(enum_class, '__member_new__', enum_class.__dict__['__new__']) + setattr(enum_class, '__new__', Enum.__dict__['__new__']) + return enum_class + + def __bool__(cls): + """ + classes/types should always be True. + """ + return True + + def __call__(cls, value, names=None, module=None, type=None, start=1): + """Either returns an existing member, or creates a new enum class. + + This method is used both when an enum class is given a value to match + to an enumeration member (i.e. Color(3)) and for the functional API + (i.e. Color = Enum('Color', names='red green blue')). + + When used for the functional API: `module`, if set, will be stored in + the new class' __module__ attribute; `type`, if set, will be mixed in + as the first base class. + + Note: if `module` is not set this routine will attempt to discover the + calling module by walking the frame stack; if this is unsuccessful + the resulting class will not be pickleable. + + """ + if names is None: # simple value lookup + return cls.__new__(cls, value) + # otherwise, functional API: we're creating a new Enum type + return cls._create_(value, names, module=module, type=type, start=start) + + def __contains__(cls, member): + return isinstance(member, cls) and member.name in cls._member_map_ + + def __delattr__(cls, attr): + # nicer error message when someone tries to delete an attribute + # (see issue19025). + if attr in cls._member_map_: + raise AttributeError( + "%s: cannot delete Enum member." % cls.__name__) + super(EnumMeta, cls).__delattr__(attr) + + def __dir__(self): + return (['__class__', '__doc__', '__members__', '__module__'] + + self._member_names_) + + @property + def __members__(cls): + """Returns a mapping of member name->value. + + This mapping lists all enum members, including aliases. Note that this + is a copy of the internal mapping. + + """ + return cls._member_map_.copy() + + def __getattr__(cls, name): + """Return the enum member matching `name` + + We use __getattr__ instead of descriptors or inserting into the enum + class' __dict__ in order to support `name` and `value` being both + properties for enum members (which live in the class' __dict__) and + enum members themselves. + + """ + if _is_dunder(name): + raise AttributeError(name) + try: + return cls._member_map_[name] + except KeyError: + raise AttributeError(name) + + def __getitem__(cls, name): + return cls._member_map_[name] + + def __iter__(cls): + return (cls._member_map_[name] for name in cls._member_names_) + + def __reversed__(cls): + return (cls._member_map_[name] for name in reversed(cls._member_names_)) + + def __len__(cls): + return len(cls._member_names_) + + __nonzero__ = __bool__ + + def __repr__(cls): + return "<enum %r>" % cls.__name__ + + def __setattr__(cls, name, value): + """Block attempts to reassign Enum members. + + A simple assignment to the class namespace only changes one of the + several possible ways to get an Enum member from the Enum class, + resulting in an inconsistent Enumeration. + + """ + member_map = cls.__dict__.get('_member_map_', {}) + if name in member_map: + raise AttributeError('Cannot reassign members.') + super(EnumMeta, cls).__setattr__(name, value) + + def _create_(cls, class_name, names=None, module=None, type=None, start=1): + """Convenience method to create a new Enum class. + + `names` can be: + + * A string containing member names, separated either with spaces or + commas. Values are auto-numbered from 1. + * An iterable of member names. Values are auto-numbered from 1. + * An iterable of (member name, value) pairs. + * A mapping of member name -> value. + + """ + if pyver < 3.0: + # if class_name is unicode, attempt a conversion to ASCII + if isinstance(class_name, unicode): + try: + class_name = class_name.encode('ascii') + except UnicodeEncodeError: + raise TypeError('%r is not representable in ASCII' % class_name) + metacls = cls.__class__ + if type is None: + bases = (cls, ) + else: + bases = (type, cls) + classdict = metacls.__prepare__(class_name, bases) + _order_ = [] + + # special processing needed for names? + if isinstance(names, basestring): + names = names.replace(',', ' ').split() + if isinstance(names, (tuple, list)) and isinstance(names[0], basestring): + names = [(e, i+start) for (i, e) in enumerate(names)] + + # Here, names is either an iterable of (name, value) or a mapping. + item = None # in case names is empty + for item in names: + if isinstance(item, basestring): + member_name, member_value = item, names[item] + else: + member_name, member_value = item + classdict[member_name] = member_value + _order_.append(member_name) + # only set _order_ in classdict if name/value was not from a mapping + if not isinstance(item, basestring): + classdict['_order_'] = ' '.join(_order_) + enum_class = metacls.__new__(metacls, class_name, bases, classdict) + + # TODO: replace the frame hack if a blessed way to know the calling + # module is ever developed + if module is None: + try: + module = _sys._getframe(2).f_globals['__name__'] + except (AttributeError, ValueError): + pass + if module is None: + _make_class_unpicklable(enum_class) + else: + enum_class.__module__ = module + + return enum_class + + @staticmethod + def _get_mixins_(bases): + """Returns the type for creating enum members, and the first inherited + enum class. + + bases: the tuple of bases that was given to __new__ + + """ + if not bases or Enum is None: + return object, Enum + + + # double check that we are not subclassing a class with existing + # enumeration members; while we're at it, see if any other data + # type has been mixed in so we can use the correct __new__ + member_type = first_enum = None + for base in bases: + if (base is not Enum and + issubclass(base, Enum) and + base._member_names_): + raise TypeError("Cannot extend enumerations") + # base is now the last base in bases + if not issubclass(base, Enum): + raise TypeError("new enumerations must be created as " + "`ClassName([mixin_type,] enum_type)`") + + # get correct mix-in type (either mix-in type of Enum subclass, or + # first base if last base is Enum) + if not issubclass(bases[0], Enum): + member_type = bases[0] # first data type + first_enum = bases[-1] # enum type + else: + for base in bases[0].__mro__: + # most common: (IntEnum, int, Enum, object) + # possible: (<Enum 'AutoIntEnum'>, <Enum 'IntEnum'>, + # <class 'int'>, <Enum 'Enum'>, + # <class 'object'>) + if issubclass(base, Enum): + if first_enum is None: + first_enum = base + else: + if member_type is None: + member_type = base + + return member_type, first_enum + + if pyver < 3.0: + @staticmethod + def _find_new_(classdict, member_type, first_enum): + """Returns the __new__ to be used for creating the enum members. + + classdict: the class dictionary given to __new__ + member_type: the data type whose __new__ will be used by default + first_enum: enumeration to check for an overriding __new__ + + """ + # now find the correct __new__, checking to see of one was defined + # by the user; also check earlier enum classes in case a __new__ was + # saved as __member_new__ + __new__ = classdict.get('__new__', None) + if __new__: + return None, True, True # __new__, save_new, use_args + + N__new__ = getattr(None, '__new__') + O__new__ = getattr(object, '__new__') + if Enum is None: + E__new__ = N__new__ + else: + E__new__ = Enum.__dict__['__new__'] + # check all possibles for __member_new__ before falling back to + # __new__ + for method in ('__member_new__', '__new__'): + for possible in (member_type, first_enum): + try: + target = possible.__dict__[method] + except (AttributeError, KeyError): + target = getattr(possible, method, None) + if target not in [ + None, + N__new__, + O__new__, + E__new__, + ]: + if method == '__member_new__': + classdict['__new__'] = target + return None, False, True + if isinstance(target, staticmethod): + target = target.__get__(member_type) + __new__ = target + break + if __new__ is not None: + break + else: + __new__ = object.__new__ + + # if a non-object.__new__ is used then whatever value/tuple was + # assigned to the enum member name will be passed to __new__ and to the + # new enum member's __init__ + if __new__ is object.__new__: + use_args = False + else: + use_args = True + + return __new__, False, use_args + else: + @staticmethod + def _find_new_(classdict, member_type, first_enum): + """Returns the __new__ to be used for creating the enum members. + + classdict: the class dictionary given to __new__ + member_type: the data type whose __new__ will be used by default + first_enum: enumeration to check for an overriding __new__ + + """ + # now find the correct __new__, checking to see of one was defined + # by the user; also check earlier enum classes in case a __new__ was + # saved as __member_new__ + __new__ = classdict.get('__new__', None) + + # should __new__ be saved as __member_new__ later? + save_new = __new__ is not None + + if __new__ is None: + # check all possibles for __member_new__ before falling back to + # __new__ + for method in ('__member_new__', '__new__'): + for possible in (member_type, first_enum): + target = getattr(possible, method, None) + if target not in ( + None, + None.__new__, + object.__new__, + Enum.__new__, + ): + __new__ = target + break + if __new__ is not None: + break + else: + __new__ = object.__new__ + + # if a non-object.__new__ is used then whatever value/tuple was + # assigned to the enum member name will be passed to __new__ and to the + # new enum member's __init__ + if __new__ is object.__new__: + use_args = False + else: + use_args = True + + return __new__, save_new, use_args + + +######################################################## +# In order to support Python 2 and 3 with a single +# codebase we have to create the Enum methods separately +# and then use the `type(name, bases, dict)` method to +# create the class. +######################################################## +temp_enum_dict = {} +temp_enum_dict['__doc__'] = "Generic enumeration.\n\n Derive from this class to define new enumerations.\n\n" + +def __new__(cls, value): + # all enum instances are actually created during class construction + # without calling this method; this method is called by the metaclass' + # __call__ (i.e. Color(3) ), and by pickle + if type(value) is cls: + # For lookups like Color(Color.red) + value = value.value + #return value + # by-value search for a matching enum member + # see if it's in the reverse mapping (for hashable values) + try: + if value in cls._value2member_map_: + return cls._value2member_map_[value] + except TypeError: + # not there, now do long search -- O(n) behavior + for member in cls._member_map_.values(): + if member.value == value: + return member + raise ValueError("%s is not a valid %s" % (value, cls.__name__)) +temp_enum_dict['__new__'] = __new__ +del __new__ + +def __repr__(self): + return "<%s.%s: %r>" % ( + self.__class__.__name__, self._name_, self._value_) +temp_enum_dict['__repr__'] = __repr__ +del __repr__ + +def __str__(self): + return "%s.%s" % (self.__class__.__name__, self._name_) +temp_enum_dict['__str__'] = __str__ +del __str__ + +if pyver >= 3.0: + def __dir__(self): + added_behavior = [ + m + for cls in self.__class__.mro() + for m in cls.__dict__ + if m[0] != '_' and m not in self._member_map_ + ] + return (['__class__', '__doc__', '__module__', ] + added_behavior) + temp_enum_dict['__dir__'] = __dir__ + del __dir__ + +def __format__(self, format_spec): + # mixed-in Enums should use the mixed-in type's __format__, otherwise + # we can get strange results with the Enum name showing up instead of + # the value + + # pure Enum branch + if self._member_type_ is object: + cls = str + val = str(self) + # mix-in branch + else: + cls = self._member_type_ + val = self.value + return cls.__format__(val, format_spec) +temp_enum_dict['__format__'] = __format__ +del __format__ + + +#################################### +# Python's less than 2.6 use __cmp__ + +if pyver < 2.6: + + def __cmp__(self, other): + if type(other) is self.__class__: + if self is other: + return 0 + return -1 + return NotImplemented + raise TypeError("unorderable types: %s() and %s()" % (self.__class__.__name__, other.__class__.__name__)) + temp_enum_dict['__cmp__'] = __cmp__ + del __cmp__ + +else: + + def __le__(self, other): + raise TypeError("unorderable types: %s() <= %s()" % (self.__class__.__name__, other.__class__.__name__)) + temp_enum_dict['__le__'] = __le__ + del __le__ + + def __lt__(self, other): + raise TypeError("unorderable types: %s() < %s()" % (self.__class__.__name__, other.__class__.__name__)) + temp_enum_dict['__lt__'] = __lt__ + del __lt__ + + def __ge__(self, other): + raise TypeError("unorderable types: %s() >= %s()" % (self.__class__.__name__, other.__class__.__name__)) + temp_enum_dict['__ge__'] = __ge__ + del __ge__ + + def __gt__(self, other): + raise TypeError("unorderable types: %s() > %s()" % (self.__class__.__name__, other.__class__.__name__)) + temp_enum_dict['__gt__'] = __gt__ + del __gt__ + + +def __eq__(self, other): + if type(other) is self.__class__: + return self is other + return NotImplemented +temp_enum_dict['__eq__'] = __eq__ +del __eq__ + +def __ne__(self, other): + if type(other) is self.__class__: + return self is not other + return NotImplemented +temp_enum_dict['__ne__'] = __ne__ +del __ne__ + +def __hash__(self): + return hash(self._name_) +temp_enum_dict['__hash__'] = __hash__ +del __hash__ + +def __reduce_ex__(self, proto): + return self.__class__, (self._value_, ) +temp_enum_dict['__reduce_ex__'] = __reduce_ex__ +del __reduce_ex__ + +# _RouteClassAttributeToGetattr is used to provide access to the `name` +# and `value` properties of enum members while keeping some measure of +# protection from modification, while still allowing for an enumeration +# to have members named `name` and `value`. This works because enumeration +# members are not set directly on the enum class -- __getattr__ is +# used to look them up. + +@_RouteClassAttributeToGetattr +def name(self): + return self._name_ +temp_enum_dict['name'] = name +del name + +@_RouteClassAttributeToGetattr +def value(self): + return self._value_ +temp_enum_dict['value'] = value +del value + +@classmethod +def _convert(cls, name, module, filter, source=None): + """ + Create a new Enum subclass that replaces a collection of global constants + """ + # convert all constants from source (or module) that pass filter() to + # a new Enum called name, and export the enum and its members back to + # module; + # also, replace the __reduce_ex__ method so unpickling works in + # previous Python versions + module_globals = vars(_sys.modules[module]) + if source: + source = vars(source) + else: + source = module_globals + members = dict((name, value) for name, value in source.items() if filter(name)) + cls = cls(name, members, module=module) + cls.__reduce_ex__ = _reduce_ex_by_name + module_globals.update(cls.__members__) + module_globals[name] = cls + return cls +temp_enum_dict['_convert'] = _convert +del _convert + +Enum = EnumMeta('Enum', (object, ), temp_enum_dict) +del temp_enum_dict + +# Enum has now been created +########################### + +class IntEnum(int, Enum): + """Enum where members are also (and must be) ints""" + +def _reduce_ex_by_name(self, proto): + return self.name + +def unique(enumeration): + """Class decorator that ensures only unique members exist in an enumeration.""" + duplicates = [] + for name, member in enumeration.__members__.items(): + if name != member.name: + duplicates.append((name, member.name)) + if duplicates: + duplicate_names = ', '.join( + ["%s -> %s" % (alias, name) for (alias, name) in duplicates] + ) + raise ValueError('duplicate names found in %r: %s' % + (enumeration, duplicate_names) + ) + return enumeration diff --git a/python/examples/angr_plugin.py b/python/examples/angr_plugin.py index c6e6f87d..90217d65 100644 --- a/python/examples/angr_plugin.py +++ b/python/examples/angr_plugin.py @@ -1,3 +1,24 @@ +# 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. + + # This plugin assumes angr is already installed and available on the system. See the angr documentation # for information about installing angr. It should be installed using the virtualenv method. # @@ -9,14 +30,20 @@ # virtual environment. A later update may allow for a manual override to link to the required version # of Python. -__name__ = "__console__" # angr looks for this, it won't load from within a UI without it -import angr -from binaryninja import * import tempfile -import threading import logging import os +__name__ = "__console__" # angr looks for this, it won't load from within a UI without it + +import angr +# For the lazy instead you can just import everything 'from binaryninja import *'' +from binaryninja.binaryview import BinaryView +from binaryninja.plugin import BackgroundTaskThread, PluginCommand +from binaryninja.interaction import show_plain_text_report, show_message_box +from binaryninja.highlight import HighlightColor +from binaryninja.enums import HighlightStandardColor, MessageBoxButtonSet + # Disable warning logs as they show up as errors in the UI logging.disable(logging.WARNING) @@ -24,9 +51,11 @@ logging.disable(logging.WARNING) BinaryView.set_default_session_data("angr_find", set()) BinaryView.set_default_session_data("angr_avoid", set()) + def escaped_output(str): return '\n'.join([s.encode("string_escape") for s in str.split('\n')]) + # Define a background thread object for solving in the background class Solver(BackgroundTaskThread): def __init__(self, find, avoid, view): @@ -81,37 +110,41 @@ class Solver(BackgroundTaskThread): else: show_plain_text_report("Results from angr", text_report) + def find_instr(bv, addr): # Highlight the instruction in green blocks = bv.get_basic_blocks_at(addr) for block in blocks: - block.set_auto_highlight(HighlightColor(GreenHighlightColor, alpha = 128)) - block.function.set_auto_instr_highlight(block.arch, addr, GreenHighlightColor) + block.set_auto_highlight(HighlightColor(HighlightStandardColor.GreenHighlightColor, alpha = 128)) + block.function.set_auto_instr_highlight(addr, HighlightStandardColor.GreenHighlightColor) # Add the instruction to the list associated with the current view bv.session_data.angr_find.add(addr) + def avoid_instr(bv, addr): # Highlight the instruction in red blocks = bv.get_basic_blocks_at(addr) for block in blocks: - block.set_auto_highlight(HighlightColor(RedHighlightColor, alpha = 128)) - block.function.set_auto_instr_highlight(block.arch, addr, RedHighlightColor) + block.set_auto_highlight(HighlightColor(HighlightStandardColor.RedHighlightColor, alpha = 128)) + block.function.set_auto_instr_highlight(addr, HighlightStandardColor.RedHighlightColor) # Add the instruction to the list associated with the current view bv.session_data.angr_avoid.add(addr) + def solve(bv): if len(bv.session_data.angr_find) == 0: show_message_box("Angr Solve", "You have not specified a goal instruction.\n\n" + "Please right click on the goal instruction and select \"Find Path to This Instruction\" to " + - "continue.", OKButtonSet, ErrorIcon) + "continue.", MessageBoxButtonSet.OKButtonSet, MessageBoxButtonSet.ErrorIcon) return # Start a solver thread for the path associated with the view s = Solver(bv.session_data.angr_find, bv.session_data.angr_avoid, bv) s.start() + # Register commands for the user to interact with the plugin PluginCommand.register_for_address("Find Path to This Instruction", "When solving, find a path that gets to this instruction", find_instr) diff --git a/python/examples/bin_info.py b/python/examples/bin_info.py index 48073894..4c4ab8fd 100644 --- a/python/examples/bin_info.py +++ b/python/examples/bin_info.py @@ -1,34 +1,73 @@ #!/usr/bin/env python -import sys, binaryninja, time -if sys.platform.lower().startswith("linux"): - bintype="ELF" -elif sys.platform.lower() == "darwin": - bintype="Mach-O" -else: - raise Exception, "%s is not supported on this plugin" % sys.platform +# 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. -if len(sys.argv) > 1: - target = sys.argv[1] -else: - target = "/bin/ls" +import sys +import binaryninja.log as log +from binaryninja.binaryview import BinaryViewType +import binaryninja.interaction as interaction +from binaryninja.plugin import PluginCommand + + +def get_bininfo(bv): + if bv is None: + filename = "" + if len(sys.argv) > 1: + filename = sys.argv[1] + else: + filename = interaction.get_open_filename_input("Filename:") + if filename is None: + log.log_warn("No file specified") + sys.exit(1) -bv = binaryninja.BinaryViewType[bintype].open(target) -bv.update_analysis_and_wait() + bv = BinaryViewType.get_view_of_file(filename) + log.redirect_output_to_log() + log.log_to_stdout(True) -print "-------- %s --------" % target -print "START: 0x%x" % bv.start -print "ENTRY: 0x%x" % bv.entry_point -print "ARCH: %s" % bv.arch.name -print "\n-------- Function List --------" + contents = "## %s ##\n" % bv.file.filename + contents += "- START: 0x%x\n\n" % bv.start + contents += "- ENTRY: 0x%x\n\n" % bv.entry_point + contents += "- ARCH: %s\n\n" % bv.arch.name + contents += "### First 10 Functions ###\n" -for func in bv.functions: - print func.symbol.name + contents += "| Start | Name |\n" + contents += "|------:|:-------|\n" + for i in xrange(min(10, len(bv.functions))): + contents += "| 0x%x | %s |\n" % (bv.functions[i].start, bv.functions[i].symbol.full_name) + contents += "### First 10 Strings ###\n" + contents += "| Start | Length | String |\n" + contents += "|------:|-------:|:-------|\n" + for i in xrange(min(10, len(bv.strings))): + start = bv.strings[i].start + length = bv.strings[i].length + string = bv.read(start, length) + contents += "| 0x%x |%d | %s |\n" % (start, length, string) + return contents -print "\n-------- First 10 strings --------" -for i in xrange(10): - start = bv.strings[i].start - length = bv.strings[i].length - string = bv.read(start,length) - print "0x%x (%d):\t%s" % (start, length, string) +def display_bininfo(bv): + interaction.show_markdown_report("Binary Info Report", get_bininfo(bv)) + + +if __name__ == "__main__": + print get_bininfo(None) +else: + PluginCommand.register("Binary Info", "Display basic info about the binary", display_bininfo) diff --git a/python/examples/breakpoint.py b/python/examples/breakpoint.py index 44df19e2..b1297e26 100644 --- a/python/examples/breakpoint.py +++ b/python/examples/breakpoint.py @@ -1,4 +1,27 @@ -from binaryninja import * +# 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. + + +from binaryninja.plugin import PluginCommand +from binaryninja.log import log_error + def write_breakpoint(view, start, length): """Sample function to show registering a plugin menu item for a range of bytes. Also possible: @@ -6,11 +29,22 @@ def write_breakpoint(view, start, length): register_for_address register_for_function """ - if view.arch.name.startswith("x86"): - view.write(start, "\xcc" * length) - elif view.arch.name == "armv7": - view.write(start, "\x7a\x00\x20\xe1" * (length/4)) - else: - log_error("No support for breakpoint on %s" % view.arch.name) + bkpt_str = { + "x86": "int3", + "x86_64": "int3", + "armv7": "bkpt", + "aarch64": "brk #0", + "mips32": "break"} + + if view.arch.name not in bkpt_str: + log_error("Architecture %s not supported" % view.arch.name) + return + + bkpt, err = view.arch.assemble(bkpt_str[view.arch.name]) + if bkpt is None: + log_error(err) + return + view.write(start, bkpt * length / len(bkpt)) + PluginCommand.register_for_range("Convert to breakpoint", "Fill region with breakpoint instructions.", write_breakpoint) diff --git a/python/examples/export_svg.py b/python/examples/export_svg.py index a54dc879..89bc41a1 100755 --- a/python/examples/export_svg.py +++ b/python/examples/export_svg.py @@ -1,11 +1,14 @@ -from binaryninja import * +# from binaryninja import * import os import webbrowser try: - from urllib import pathname2url # Python 2.x + from urllib import pathname2url # Python 2.x except: - from urllib.request import pathname2url # Python 3.x + from urllib.request import pathname2url # Python 3.x +from binaryninja.interaction import get_save_filename_input, show_message_box +from binaryninja.enums import MessageBoxButtonSet, MessageBoxIcon, MessageBoxButtonResult, InstructionTextTokenType, BranchType +from binaryninja.plugin import PluginCommand colors = {'green': [162, 217, 175], 'red': [222, 143, 151], 'blue': [128, 198, 233], 'cyan': [142, 230, 237], 'lightCyan': [176, 221, 228], 'orange': [237, 189, 129], 'yellow': [237, 223, 179], 'magenta': [218, 196, 209], 'none': [74, 74, 74]} @@ -17,47 +20,58 @@ escape_table = { ' ': " " } -def escape(string): - string=string.decode('utf-8').encode('ascii','xmlcharrefreplace') #handle extended unicode - return ''.join(escape_table.get(i,i) for i in string) #still escape the basics -def save_svg(bv,function): - address = hex(function.start).replace('L','') +def escape(toescape): + toescape = toescape.decode('utf-8').encode('ascii', 'xmlcharrefreplace') # handle extended unicode + return ''.join(escape_table.get(i, i) for i in toescape) # still escape the basics + + +def save_svg(bv, function): + address = hex(function.start).replace('L', '') path = os.path.dirname(bv.file.filename) origname = os.path.basename(bv.file.filename) - filename = os.path.join(path,'binaryninja-{filename}-{function}.html'.format(filename=origname,function=address)) + filename = os.path.join(path, 'binaryninja-{filename}-{function}.html'.format(filename=origname, function=address)) outputfile = get_save_filename_input('File name for export_svg', 'HTML files (*.html)', filename) if outputfile is None: return content = render_svg(function) - output = open(outputfile,'w') + output = open(outputfile, 'w') output.write(content) output.close() - if show_message_box("Open SVG", "Would you like to view the exported SVG?", buttons = core.YesNoButtonSet, icon = core.QuestionIcon) == core.YesButton: + result = show_message_box("Open SVG", "Would you like to view the exported SVG?", + buttons = MessageBoxButtonSet.YesNoButtonSet, icon = MessageBoxIcon.QuestionIcon) + if result == MessageBoxButtonResult.YesButton: url = 'file:{}'.format(pathname2url(outputfile)) webbrowser.open(url) -def instruction_data_flow(function,address): + +def instruction_data_flow(function, address): ''' TODO: Extract data flow information ''' - length = function.view.get_instruction_length(function.arch,address) + length = function.view.get_instruction_length(address) bytes = function.view.read(address, length) hex = bytes.encode('hex') - padded = ' '.join([hex[i:i+2] for i in range(0, len(hex), 2)]) + padded = ' '.join([hex[i:i + 2] for i in range(0, len(hex), 2)]) return 'Opcode: {bytes}'.format(bytes=padded) + def render_svg(function): graph = function.create_graph() graph.layout_and_wait() heightconst = 15 ratio = 0.48 - widthconst = heightconst*ratio + widthconst = heightconst * ratio output = '''<html> <head> <style type="text/css"> @import url(https://fonts.googleapis.com/css?family=Source+Code+Pro); + body { + background-color: rgb(42, 42, 42); + } svg { background-color: rgb(42, 42, 42); + display: block; + margin: 0 auto; } .basicblock { stroke: rgb(224, 224, 224); @@ -130,59 +144,64 @@ def render_svg(function): <path d="M 0 0 L 10 5 L 0 10 z" /> </marker> </defs> - '''.format(width=graph.width*widthconst, height=graph.height*heightconst) + '''.format(width=graph.width * widthconst + 20, height=graph.height * heightconst + 20) output += ''' <g id="functiongraph0" class="functiongraph"> <title>Function Graph 0</title> ''' edges = '' - for i,block in enumerate(graph.blocks): + for i, block in enumerate(graph.blocks): - #Calculate basic block location and coordinates + # Calculate basic block location and coordinates x = ((block.x) * widthconst) y = ((block.y) * heightconst) width = ((block.width) * widthconst) height = ((block.height) * heightconst) - #Render block + # Render block output += ' <g id="basicblock{i}">\n'.format(i=i) output += ' <title>Basic Block {i}</title>\n'.format(i=i) - rgb=colors['none'] + rgb = colors['none'] try: bb = block.basic_block color_code = bb.highlight.color color_str = bb.highlight._standard_color_to_str(color_code) if color_str in colors: - rgb=colors[color_str] + rgb = colors[color_str] except: pass - output += ' <rect class="basicblock" x="{x}" y="{y}" fill-opacity="0.4" height="{height}" width="{width}" fill="rgb({r},{g},{b})"/>\n'.format(x=x,y=y,width=width,height=height,r=rgb[0],g=rgb[1],b=rgb[2]) + output += ' <rect class="basicblock" x="{x}" y="{y}" fill-opacity="0.4" height="{height}" width="{width}" fill="rgb({r},{g},{b})"/>\n'.format(x=x, y=y, width=width + 16, height=height + 12, r=rgb[0], g=rgb[1], b=rgb[2]) - #Render instructions, unfortunately tspans don't allow copying/pasting more - #than one line at a time, need SVG 1.2 textarea tags for that it looks like + # Render instructions, unfortunately tspans don't allow copying/pasting more + # than one line at a time, need SVG 1.2 textarea tags for that it looks like - output += ' <text x="{x}" y="{y}">\n'.format(x=x,y=y + (i + 1) * heightconst) - for i,line in enumerate(block.lines): - output += ' <tspan id="instr-{address}" x="{x}" y="{y}">'.format(x=x,y=y + (i + 0.7) * heightconst,address=hex(line.address)[:-1]) + output += ' <text x="{x}" y="{y}">\n'.format(x=x, y=y + (i + 1) * heightconst) + for i, line in enumerate(block.lines): + output += ' <tspan id="instr-{address}" x="{x}" y="{y}">'.format(x=x + 6, y=y + 6 + (i + 0.7) * heightconst, address=hex(line.address)[:-1]) hover = instruction_data_flow(function, line.address) output += '<title>{hover}</title>'.format(hover=hover) for token in line.tokens: # TODO: add hover for hex, function, and reg tokens - output+='<tspan class="{tokentype}">{text}</tspan>'.format(text=escape(token.text),tokentype=token.type) + output += '<tspan class="{tokentype}">{text}</tspan>'.format(text=escape(token.text), tokentype=InstructionTextTokenType(token.type).name) output += '</tspan>\n' output += ' </text>\n' output += ' </g>\n' - #Edges are rendered in a seperate chunk so they have priority over the - #basic blocks or else they'd render below them + # Edges are rendered in a seperate chunk so they have priority over the + # basic blocks or else they'd render below them for edge in block.outgoing_edges: points = "" - for x,y in edge.points: - points += str(x*widthconst)+","+str(y*heightconst) + " " - edges += ' <polyline class="edge {type}" points="{points}" marker-end="url(#arrow-{type})"/>\n'.format(type=edge.type,points=points) + x, y = edge.points[0] + points += str(x * widthconst) + "," + str(y * heightconst + 12) + " " + for x, y in edge.points[1:-1]: + points += str(x * widthconst) + "," + str(y * heightconst) + " " + x, y = edge.points[-1] + points += str(x * widthconst) + "," + str(y * heightconst + 0) + " " + edges += ' <polyline class="edge {type}" points="{points}" marker-end="url(#arrow-{type})"/>\n'.format(type=BranchType(edge.type).name, points=points) output += ' ' + edges + '\n' output += ' </g>\n' output += '</svg></html>' return output + PluginCommand.register_for_function("Export to SVG", "Exports an SVG of the current function", save_svg) diff --git a/python/examples/instruction_iterator.py b/python/examples/instruction_iterator.py index 43bc000e..7ff2d692 100644 --- a/python/examples/instruction_iterator.py +++ b/python/examples/instruction_iterator.py @@ -1,49 +1,53 @@ #!/usr/bin/env python +# 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 sys -try: - import binaryninja -except ImportError: - sys.path.append("/Applications/Binary Ninja.app/Contents/Resources/python/") - import binaryninja -import time - -if sys.platform.lower().startswith("linux"): - bintype="ELF" -elif sys.platform.lower() == "darwin": - bintype="Mach-O" -else: - raise Exception, "%s is not supported on this plugin" % sys.platform +import binaryninja as binja if len(sys.argv) > 1: target = sys.argv[1] -else: - target = "/bin/ls" - -bv = binaryninja.BinaryViewType[bintype].open(target) -bv.update_analysis_and_wait() -print "-------- %s --------" % target -print "START: 0x%x" % bv.start -print "ENTRY: 0x%x" % bv.entry_point -print "ARCH: %s" % bv.arch.name -print "\n-------- Function List --------" +bv = binja.BinaryViewType.get_view_of_file(target) +binja.log_to_stdout(True) +binja.log_info("-------- %s --------" % target) +binja.log_info("START: 0x%x" % bv.start) +binja.log_info("ENTRY: 0x%x" % bv.entry_point) +binja.log_info("ARCH: %s" % bv.arch.name) +binja.log_info("\n-------- Function List --------") """ print all the functions, their basic blocks, and their il instructions """ for func in bv.functions: - print repr(func) - for block in func.low_level_il: - print "\t{0}".format(block) + binja.log_info(repr(func)) + for block in func.low_level_il: + binja.log_info("\t{0}".format(block)) - for insn in block: - print "\t\t{0}".format(insn) + for insn in block: + binja.log_info("\t\t{0}".format(insn)) """ print all the functions, their basic blocks, and their mc instructions """ for func in bv.functions: - print repr(func) - for block in func: - print "\t{0}".format(block) + binja.log_info(repr(func)) + for block in func: + binja.log_info("\t{0}".format(block)) - for insn in block: - print "\t\t{0}".format(insn) + for insn in block: + binja.log_info("\t\t{0}".format(insn)) diff --git a/python/examples/jump_table.py b/python/examples/jump_table.py index 39fed1a5..439e2ab6 100644 --- a/python/examples/jump_table.py +++ b/python/examples/jump_table.py @@ -1,8 +1,30 @@ +# 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. + # This plugin will attempt to resolve simple jump tables (an array of code pointers) and add the destinations # as indirect branch targets so that the flow graph reflects the jump table's control flow. -from binaryninja import * +from binaryninja.plugin import PluginCommand +from binaryninja.enums import InstructionTextTokenType import struct + def find_jump_table(bv, addr): for block in bv.get_basic_blocks_at(addr): func = block.function @@ -28,7 +50,7 @@ def find_jump_table(bv, addr): # Collect the branch targets for any tables referenced by the clicked instruction branches = [] for token in tokens: - if token.type == "PossibleAddressToken": # Table addresses will be a "possible address" token + if InstructionTextTokenType(token.type) == InstructionTextTokenType.PossibleAddressToken: # Table addresses will be a "possible address" token tbl = token.value print "Found possible table at 0x%x" % tbl i = 0 @@ -56,7 +78,8 @@ def find_jump_table(bv, addr): i += 1 # Set the indirect branch targets on the jump instruction to be the list of targets discovered - func.set_user_indirect_branches(arch, jump_addr, branches) + func.set_user_indirect_branches(jump_addr, branches) + # Create a plugin command so that the user can right click on an instruction referencing a jump table and # invoke the command diff --git a/python/examples/nds.py b/python/examples/nds.py index 5300018c..ff137b4b 100644 --- a/python/examples/nds.py +++ b/python/examples/nds.py @@ -1,88 +1,117 @@ -from binaryninja import *
-import struct
-import traceback
-import os
-
-def crc16(data):
- crc = 0xffff
- for ch in data:
- crc ^= ord(ch)
- for bit in xrange(0, 8):
- if (crc & 1) == 1:
- crc = (crc >> 1) ^ 0xa001
- else:
- crc >>= 1
- return crc
-
-class DSView(BinaryView):
- def __init__(self, data):
- BinaryView.__init__(self, file_metadata = data.file, parent_view = data)
- self.raw = data
-
- @classmethod
- def is_valid_for_data(self, data):
- hdr = data.read(0, 0x160)
- if len(hdr) < 0x160:
- return False
- if struct.unpack("<H", hdr[0x15e:0x160])[0] != crc16(hdr[0:0x15e]):
- return False
- if struct.unpack("<H", hdr[0x15c:0x15e])[0] != crc16(hdr[0xc0:0x15c]):
- return False
- return True
-
- def init_common(self):
- self.platform = Architecture["armv7"].standalone_platform
- self.hdr = self.raw.read(0, 0x160)
-
- def init_arm9(self):
- try:
- self.init_common()
- self.arm9_offset = struct.unpack("<L", self.hdr[0x20:0x24])[0]
- self.arm_entry_addr = struct.unpack("<L", self.hdr[0x24:0x28])[0]
- self.arm9_load_addr = struct.unpack("<L", self.hdr[0x28:0x2C])[0]
- self.arm9_size = struct.unpack("<L", self.hdr[0x2C:0x30])[0]
- self.add_auto_segment(self.arm9_load_addr, self.arm9_size, self.arm9_offset, self.arm9_size,
- SegmentReadable | SegmentExecutable)
- self.add_entry_point(Architecture['armv7'].standalone_platform, self.arm_entry_addr)
- return True
- except:
- log_error(traceback.format_exc())
- return False
-
- def init_arm7(self):
- try:
- self.init_common()
- self.arm7_offset = struct.unpack("<L", self.hdr[0x30:0x34])[0]
- self.arm_entry_addr = struct.unpack("<L", self.hdr[0x34:0x38])[0]
- self.arm7_load_addr = struct.unpack("<L", self.hdr[0x38:0x3C])[0]
- self.arm7_size = struct.unpack("<L", self.hdr[0x3C:0x40])[0]
- self.add_auto_segment(self.arm7_load_addr, self.arm7_size, self.arm7_offset, self.arm7_size,
- SegmentReadable | SegmentExecutable)
- self.add_entry_point(Architecture['armv7'].standalone_platform, self.arm_entry_addr)
- return True
- except:
- log_error(traceback.format_exc())
- return False
-
- def perform_is_executable(self):
- return True
-
- def perform_get_entry_point(self):
- return self.arm_entry_addr
-
-class DSARM9View(DSView):
- name = "DSARM9"
- long_name = "DS ARM9 ROM"
-
- def init(self):
- return self.init_arm9()
-
-class DSARM7View(DSView):
- name = "DSARM7"
- long_name = "DS ARM7 ROM"
-
- def init(self):
- return self.init_arm7()
-
-DSARM9View.register()
-DSARM7View.register()
+# 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. + +# from binaryninja import * +from binaryninja.binaryview import BinaryView +from binaryninja.architecture import Architecture +from binaryninja.enums import SegmentFlag +from binaryninja.log import log_error + +import struct +import traceback + + +def crc16(data): + crc = 0xffff + for ch in data: + crc ^= ord(ch) + for bit in xrange(0, 8): + if (crc & 1) == 1: + crc = (crc >> 1) ^ 0xa001 + else: + crc >>= 1 + return crc + + +class DSView(BinaryView): + def __init__(self, data): + BinaryView.__init__(self, file_metadata = data.file, parent_view = data) + self.raw = data + + @classmethod + def is_valid_for_data(self, data): + hdr = data.read(0, 0x160) + if len(hdr) < 0x160: + return False + if struct.unpack("<H", hdr[0x15e:0x160])[0] != crc16(hdr[0:0x15e]): + return False + if struct.unpack("<H", hdr[0x15c:0x15e])[0] != crc16(hdr[0xc0:0x15c]): + return False + return True + + def init_common(self): + self.platform = Architecture["armv7"].standalone_platform + self.hdr = self.raw.read(0, 0x160) + + def init_arm9(self): + try: + self.init_common() + self.arm9_offset = struct.unpack("<L", self.hdr[0x20:0x24])[0] + self.arm_entry_addr = struct.unpack("<L", self.hdr[0x24:0x28])[0] + self.arm9_load_addr = struct.unpack("<L", self.hdr[0x28:0x2C])[0] + self.arm9_size = struct.unpack("<L", self.hdr[0x2C:0x30])[0] + self.add_auto_segment(self.arm9_load_addr, self.arm9_size, self.arm9_offset, self.arm9_size, + SegmentFlag.SegmentReadable | SegmentFlag.SegmentExecutable) + self.add_entry_point(Architecture['armv7'].standalone_platform, self.arm_entry_addr) + return True + except: + log_error(traceback.format_exc()) + return False + + def init_arm7(self): + try: + self.init_common() + self.arm7_offset = struct.unpack("<L", self.hdr[0x30:0x34])[0] + self.arm_entry_addr = struct.unpack("<L", self.hdr[0x34:0x38])[0] + self.arm7_load_addr = struct.unpack("<L", self.hdr[0x38:0x3C])[0] + self.arm7_size = struct.unpack("<L", self.hdr[0x3C:0x40])[0] + self.add_auto_segment(self.arm7_load_addr, self.arm7_size, self.arm7_offset, self.arm7_size, + SegmentFlag.SegmentReadable | SegmentFlag.SegmentExecutable) + self.add_entry_point(Architecture['armv7'].standalone_platform, self.arm_entry_addr) + return True + except: + log_error(traceback.format_exc()) + return False + + def perform_is_executable(self): + return True + + def perform_get_entry_point(self): + return self.arm_entry_addr + + +class DSARM9View(DSView): + name = "DSARM9" + long_name = "DS ARM9 ROM" + + def init(self): + return self.init_arm9() + + +class DSARM7View(DSView): + name = "DSARM7" + long_name = "DS ARM7 ROM" + + def init(self): + return self.init_arm7() + + +DSARM9View.register() +DSARM7View.register() diff --git a/python/examples/nes.py b/python/examples/nes.py index 23f5f3d8..4122cde0 100644 --- a/python/examples/nes.py +++ b/python/examples/nes.py @@ -18,44 +18,52 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. -from binaryninja import * import struct import traceback import os +from binaryninja.architecture import Architecture +from binaryninja.lowlevelil import LowLevelILLabel, LLIL_TEMP +from binaryninja.function import RegisterInfo, InstructionInfo, InstructionTextToken +from binaryninja.binaryview import BinaryView +from binaryninja.types import Symbol +from binaryninja.log import log_error +from binaryninja.enums import (BranchType, InstructionTextTokenType, + LowLevelILOperation, LowLevelILFlagCondition, FlagRole, SegmentFlag, SymbolType) + InstructionNames = [ - "brk", "ora", None, None, None, "ora", "asl", None, # 0x00 - "php", "ora", "asl@", None, None, "ora", "asl", None, # 0x08 - "bpl", "ora", None, None, None, "ora", "asl", None, # 0x10 - "clc", "ora", None, None, None, "ora", "asl", None, # 0x18 - "jsr", "and", None, None, "bit", "and", "rol", None, # 0x20 - "plp", "and", "rol@", None, "bit", "and", "rol", None, # 0x28 - "bmi", "and", None, None, None, "and", "rol", None, # 0x30 - "sec", "and", None, None, None, "and", "rol", None, # 0x38 - "rti", "eor", None, None, None, "eor", "lsr", None, # 0x40 - "pha", "eor", "lsr@", None, "jmp", "eor", "lsr", None, # 0x48 - "bvc", "eor", None, None, None, "eor", "lsr", None, # 0x50 - "cli", "eor", None, None, None, "eor", "lsr", None, # 0x58 - "rts", "adc", None, None, None, "adc", "ror", None, # 0x60 - "pla", "adc", "ror@", None, "jmp", "adc", "ror", None, # 0x68 - "bvs", "adc", None, None, None, "adc", "ror", None, # 0x70 - "sei", "adc", None, None, None, "adc", "ror", None, # 0x78 - None, "sta", None, None, "sty", "sta", "stx", None, # 0x80 - "dey", None, "txa", None, "sty", "sta", "stx", None, # 0x88 - "bcc", "sta", None, None, "sty", "sta", "stx", None, # 0x90 - "tya", "sta", "txs", None, None, "sta", None, None, # 0x98 - "ldy", "lda", "ldx", None, "ldy", "lda", "ldx", None, # 0xa0 - "tay", "lda", "tax", None, "ldy", "lda", "ldx", None, # 0xa8 - "bcs", "lda", None, None, "ldy", "lda", "ldx", None, # 0xb0 - "clv", "lda", "tsx", None, "ldy", "lda", "ldx", None, # 0xb8 - "cpy", "cmp", None, None, "cpy", "cmp", "dec", None, # 0xc0 - "iny", "cmp", "dex", None, "cpy", "cmp", "dec", None, # 0xc8 - "bne", "cmp", None, None, None, "cmp", "dec", None, # 0xd0 - "cld", "cmp", None, None, None, "cmp", "dec", None, # 0xd8 - "cpx", "sbc", None, None, "cpx", "sbc", "inc", None, # 0xe0 - "inx", "sbc", "nop", None, "cpx", "sbc", "inc", None, # 0xe8 - "beq", "sbc", None, None, None, "sbc", "inc", None, # 0xf0 - "sed", "sbc", None, None, None, "sbc", "inc", None # 0xf8 + "brk", "ora", None, None, None, "ora", "asl", None, # 0x00 + "php", "ora", "asl@", None, None, "ora", "asl", None, # 0x08 + "bpl", "ora", None, None, None, "ora", "asl", None, # 0x10 + "clc", "ora", None, None, None, "ora", "asl", None, # 0x18 + "jsr", "and", None, None, "bit", "and", "rol", None, # 0x20 + "plp", "and", "rol@", None, "bit", "and", "rol", None, # 0x28 + "bmi", "and", None, None, None, "and", "rol", None, # 0x30 + "sec", "and", None, None, None, "and", "rol", None, # 0x38 + "rti", "eor", None, None, None, "eor", "lsr", None, # 0x40 + "pha", "eor", "lsr@", None, "jmp", "eor", "lsr", None, # 0x48 + "bvc", "eor", None, None, None, "eor", "lsr", None, # 0x50 + "cli", "eor", None, None, None, "eor", "lsr", None, # 0x58 + "rts", "adc", None, None, None, "adc", "ror", None, # 0x60 + "pla", "adc", "ror@", None, "jmp", "adc", "ror", None, # 0x68 + "bvs", "adc", None, None, None, "adc", "ror", None, # 0x70 + "sei", "adc", None, None, None, "adc", "ror", None, # 0x78 + None, "sta", None, None, "sty", "sta", "stx", None, # 0x80 + "dey", None, "txa", None, "sty", "sta", "stx", None, # 0x88 + "bcc", "sta", None, None, "sty", "sta", "stx", None, # 0x90 + "tya", "sta", "txs", None, None, "sta", None, None, # 0x98 + "ldy", "lda", "ldx", None, "ldy", "lda", "ldx", None, # 0xa0 + "tay", "lda", "tax", None, "ldy", "lda", "ldx", None, # 0xa8 + "bcs", "lda", None, None, "ldy", "lda", "ldx", None, # 0xb0 + "clv", "lda", "tsx", None, "ldy", "lda", "ldx", None, # 0xb8 + "cpy", "cmp", None, None, "cpy", "cmp", "dec", None, # 0xc0 + "iny", "cmp", "dex", None, "cpy", "cmp", "dec", None, # 0xc8 + "bne", "cmp", None, None, None, "cmp", "dec", None, # 0xd0 + "cld", "cmp", None, None, None, "cmp", "dec", None, # 0xd8 + "cpx", "sbc", None, None, "cpx", "sbc", "inc", None, # 0xe0 + "inx", "sbc", "nop", None, "cpx", "sbc", "inc", None, # 0xe8 + "beq", "sbc", None, None, None, "sbc", "inc", None, # 0xf0 + "sed", "sbc", None, None, None, "sbc", "inc", None # 0xf8 ] NONE = 0 @@ -81,105 +89,106 @@ ZERO_X_DEST = 19 ZERO_Y = 20 ZERO_Y_DEST = 21 InstructionOperandTypes = [ - NONE, IND_X, NONE, NONE, NONE, ZERO, ZERO_DEST, NONE, # 0x00 - NONE, IMMED, ACCUM, NONE, NONE, ABS, ABS_DEST, NONE, # 0x08 - REL, IND_Y, NONE, NONE, NONE, ZERO_X, ZERO_X_DEST, NONE, # 0x10 - NONE, ABS_Y, NONE, NONE, NONE, ABS_X, ABS_X_DEST, NONE, # 0x18 - ADDR, IND_X, NONE, NONE, ZERO, ZERO, ZERO_DEST, NONE, # 0x20 - NONE, IMMED, ACCUM, NONE, ABS, ABS, ABS_DEST, NONE, # 0x28 - REL, IND_Y, NONE, NONE, NONE, ZERO_X, ZERO_X_DEST, NONE, # 0x30 - NONE, ABS_Y, NONE, NONE, NONE, ABS_X, ABS_X_DEST, NONE, # 0x38 - NONE, IND_X, NONE, NONE, NONE, ZERO, ZERO_DEST, NONE, # 0x40 - NONE, IMMED, ACCUM, NONE, ADDR, ABS, ABS_DEST, NONE, # 0x48 - REL, IND_Y, NONE, NONE, NONE, ZERO_X, ZERO_X_DEST, NONE, # 0x50 - NONE, ABS_Y, NONE, NONE, NONE, ABS_X, ABS_X_DEST, NONE, # 0x58 - NONE, IND_X, NONE, NONE, NONE, ZERO, ZERO_DEST, NONE, # 0x60 - NONE, IMMED, ACCUM, NONE, IND, ABS, ABS_DEST, NONE, # 0x68 - REL, IND_Y, NONE, NONE, NONE, ZERO_X, ZERO_X_DEST, NONE, # 0x70 - NONE, ABS_Y, NONE, NONE, NONE, ABS_X, ABS_X_DEST, NONE, # 0x78 - NONE, IND_X_DEST, NONE, NONE, ZERO_DEST, ZERO_DEST, ZERO_DEST, NONE, # 0x80 - NONE, NONE, NONE, NONE, ABS_DEST, ABS_DEST, ABS_DEST, NONE, # 0x88 - REL, IND_Y_DEST, NONE, NONE, ZERO_X_DEST, ZERO_X_DEST, ZERO_Y_DEST, NONE, # 0x90 - NONE, ABS_Y_DEST, NONE, NONE, NONE, ABS_X_DEST, NONE, NONE, # 0x98 - IMMED, IND_X, IMMED, NONE, ZERO, ZERO, ZERO, NONE, # 0xa0 - NONE, IMMED, NONE, NONE, ABS, ABS, ABS, NONE, # 0xa8 - REL, IND_Y, NONE, NONE, ZERO_X, ZERO_X, ZERO_Y, NONE, # 0xb0 - NONE, ABS_Y, NONE, NONE, ABS_X, ABS_X, ABS_Y, NONE, # 0xb8 - IMMED, IND_X, NONE, NONE, ZERO, ZERO, ZERO_DEST, NONE, # 0xc0 - NONE, IMMED, NONE, NONE, ABS, ABS, ABS_DEST, NONE, # 0xc8 - REL, IND_Y, NONE, NONE, NONE, ZERO_X, ZERO_X_DEST, NONE, # 0xd0 - NONE, ABS_Y, NONE, NONE, NONE, ABS_X, ABS_X_DEST, NONE, # 0xd8 - IMMED, IND_X, NONE, NONE, ZERO, ZERO, ZERO_DEST, NONE, # 0xe0 - NONE, IMMED, NONE, NONE, ABS, ABS, ABS_DEST, NONE, # 0xe8 - REL, IND_Y, NONE, NONE, NONE, ZERO_X, ZERO_X_DEST, NONE, # 0xf0 - NONE, ABS_Y, NONE, NONE, NONE, ABS_X, ABS_X_DEST, NONE # 0xf8 + NONE, IND_X, NONE, NONE, NONE, ZERO, ZERO_DEST, NONE, # 0x00 + NONE, IMMED, ACCUM, NONE, NONE, ABS, ABS_DEST, NONE, # 0x08 + REL, IND_Y, NONE, NONE, NONE, ZERO_X, ZERO_X_DEST, NONE, # 0x10 + NONE, ABS_Y, NONE, NONE, NONE, ABS_X, ABS_X_DEST, NONE, # 0x18 + ADDR, IND_X, NONE, NONE, ZERO, ZERO, ZERO_DEST, NONE, # 0x20 + NONE, IMMED, ACCUM, NONE, ABS, ABS, ABS_DEST, NONE, # 0x28 + REL, IND_Y, NONE, NONE, NONE, ZERO_X, ZERO_X_DEST, NONE, # 0x30 + NONE, ABS_Y, NONE, NONE, NONE, ABS_X, ABS_X_DEST, NONE, # 0x38 + NONE, IND_X, NONE, NONE, NONE, ZERO, ZERO_DEST, NONE, # 0x40 + NONE, IMMED, ACCUM, NONE, ADDR, ABS, ABS_DEST, NONE, # 0x48 + REL, IND_Y, NONE, NONE, NONE, ZERO_X, ZERO_X_DEST, NONE, # 0x50 + NONE, ABS_Y, NONE, NONE, NONE, ABS_X, ABS_X_DEST, NONE, # 0x58 + NONE, IND_X, NONE, NONE, NONE, ZERO, ZERO_DEST, NONE, # 0x60 + NONE, IMMED, ACCUM, NONE, IND, ABS, ABS_DEST, NONE, # 0x68 + REL, IND_Y, NONE, NONE, NONE, ZERO_X, ZERO_X_DEST, NONE, # 0x70 + NONE, ABS_Y, NONE, NONE, NONE, ABS_X, ABS_X_DEST, NONE, # 0x78 + NONE, IND_X_DEST, NONE, NONE, ZERO_DEST, ZERO_DEST, ZERO_DEST, NONE, # 0x80 + NONE, NONE, NONE, NONE, ABS_DEST, ABS_DEST, ABS_DEST, NONE, # 0x88 + REL, IND_Y_DEST, NONE, NONE, ZERO_X_DEST, ZERO_X_DEST, ZERO_Y_DEST, NONE, # 0x90 + NONE, ABS_Y_DEST, NONE, NONE, NONE, ABS_X_DEST, NONE, NONE, # 0x98 + IMMED, IND_X, IMMED, NONE, ZERO, ZERO, ZERO, NONE, # 0xa0 + NONE, IMMED, NONE, NONE, ABS, ABS, ABS, NONE, # 0xa8 + REL, IND_Y, NONE, NONE, ZERO_X, ZERO_X, ZERO_Y, NONE, # 0xb0 + NONE, ABS_Y, NONE, NONE, ABS_X, ABS_X, ABS_Y, NONE, # 0xb8 + IMMED, IND_X, NONE, NONE, ZERO, ZERO, ZERO_DEST, NONE, # 0xc0 + NONE, IMMED, NONE, NONE, ABS, ABS, ABS_DEST, NONE, # 0xc8 + REL, IND_Y, NONE, NONE, NONE, ZERO_X, ZERO_X_DEST, NONE, # 0xd0 + NONE, ABS_Y, NONE, NONE, NONE, ABS_X, ABS_X_DEST, NONE, # 0xd8 + IMMED, IND_X, NONE, NONE, ZERO, ZERO, ZERO_DEST, NONE, # 0xe0 + NONE, IMMED, NONE, NONE, ABS, ABS, ABS_DEST, NONE, # 0xe8 + REL, IND_Y, NONE, NONE, NONE, ZERO_X, ZERO_X_DEST, NONE, # 0xf0 + NONE, ABS_Y, NONE, NONE, NONE, ABS_X, ABS_X_DEST, NONE # 0xf8 ] OperandLengths = [ - 0, # NONE - 2, # ABS - 2, # ABS_DEST - 2, # ABS_X - 2, # ABS_X_DEST - 2, # ABS_Y - 2, # ABS_Y_DEST - 0, # ACCUM - 2, # ADDR - 1, # IMMED - 2, # IND - 1, # IND_X - 1, # IND_X_DEST - 1, # IND_Y - 1, # IND_Y_DEST - 1, # REL - 1, # ZERO - 1, # ZREO_DEST - 1, # ZERO_X - 1, # ZERO_X_DEST - 1, # ZERO_Y - 1 # ZERO_Y_DEST + 0, # NONE + 2, # ABS + 2, # ABS_DEST + 2, # ABS_X + 2, # ABS_X_DEST + 2, # ABS_Y + 2, # ABS_Y_DEST + 0, # ACCUM + 2, # ADDR + 1, # IMMED + 2, # IND + 1, # IND_X + 1, # IND_X_DEST + 1, # IND_Y + 1, # IND_Y_DEST + 1, # REL + 1, # ZERO + 1, # ZREO_DEST + 1, # ZERO_X + 1, # ZERO_X_DEST + 1, # ZERO_Y + 1 # ZERO_Y_DEST ] OperandTokens = [ - lambda value: [], # NONE - lambda value: [InstructionTextToken(PossibleAddressToken, "$%.4x" % value, value)], # ABS - lambda value: [InstructionTextToken(PossibleAddressToken, "$%.4x" % value, value)], # ABS_DEST - lambda value: [InstructionTextToken(PossibleAddressToken, "$%.4x" % value, value), - InstructionTextToken(TextToken, ", "), InstructionTextToken(RegisterToken, "x")], # ABS_X - lambda value: [InstructionTextToken(PossibleAddressToken, "$%.4x" % value, value), - InstructionTextToken(TextToken, ", "), InstructionTextToken(RegisterToken, "x")], # ABS_X_DEST - lambda value: [InstructionTextToken(PossibleAddressToken, "$%.4x" % value, value), - InstructionTextToken(TextToken, ", "), InstructionTextToken(RegisterToken, "y")], # ABS_Y - lambda value: [InstructionTextToken(PossibleAddressToken, "$%.4x" % value, value), - InstructionTextToken(TextToken, ", "), InstructionTextToken(RegisterToken, "y")], # ABS_Y_DEST - lambda value: [InstructionTextToken(RegisterToken, "a")], # ACCUM - lambda value: [InstructionTextToken(PossibleAddressToken, "$%.4x" % value, value)], # ADDR - lambda value: [InstructionTextToken(TextToken, "#"), InstructionTextToken(IntegerToken, "$%.2x" % value, value)], # IMMED - lambda value: [InstructionTextToken(TextToken, "["), InstructionTextToken(PossibleAddressToken, "$%.4x" % value, value), - InstructionTextToken(TextToken, "]")], # IND - lambda value: [InstructionTextToken(TextToken, "["), InstructionTextToken(PossibleAddressToken, "$%.2x" % value, value), - InstructionTextToken(TextToken, ", "), InstructionTextToken(RegisterToken, "x"), - InstructionTextToken(TextToken, "]")], # IND_X - lambda value: [InstructionTextToken(TextToken, "["), InstructionTextToken(PossibleAddressToken, "$%.2x" % value, value), - InstructionTextToken(TextToken, ", "), InstructionTextToken(RegisterToken, "x"), - InstructionTextToken(TextToken, "]")], # IND_X_DEST - lambda value: [InstructionTextToken(TextToken, "["), InstructionTextToken(PossibleAddressToken, "$%.2x" % value, value), - InstructionTextToken(TextToken, "], "), InstructionTextToken(RegisterToken, "y")], # IND_Y - lambda value: [InstructionTextToken(TextToken, "["), InstructionTextToken(PossibleAddressToken, "$%.2x" % value, value), - InstructionTextToken(TextToken, "], "), InstructionTextToken(RegisterToken, "y")], # IND_Y_DEST - lambda value: [InstructionTextToken(PossibleAddressToken, "$%.4x" % value, value)], # REL - lambda value: [InstructionTextToken(PossibleAddressToken, "$%.2x" % value, value)], # ZERO - lambda value: [InstructionTextToken(PossibleAddressToken, "$%.2x" % value, value)], # ZERO_DEST - lambda value: [InstructionTextToken(PossibleAddressToken, "$%.2x" % value, value), - InstructionTextToken(TextToken, ", "), InstructionTextToken(RegisterToken, "x")], # ZERO_X - lambda value: [InstructionTextToken(PossibleAddressToken, "$%.2x" % value, value), - InstructionTextToken(TextToken, ", "), InstructionTextToken(RegisterToken, "x")], # ZERO_X_DEST - lambda value: [InstructionTextToken(PossibleAddressToken, "$%.2x" % value, value), - InstructionTextToken(TextToken, ", "), InstructionTextToken(RegisterToken, "y")], # ZERO_Y - lambda value: [InstructionTextToken(PossibleAddressToken, "$%.2x" % value, value), - InstructionTextToken(TextToken, ", "), InstructionTextToken(RegisterToken, "y")] # ZERO_Y_DEST + lambda value: [], # NONE + lambda value: [InstructionTextToken(InstructionTextTokenType.PossibleAddressToken, "$%.4x" % value, value)], # ABS + lambda value: [InstructionTextToken(InstructionTextTokenType.PossibleAddressToken, "$%.4x" % value, value)], # ABS_DEST + lambda value: [InstructionTextToken(InstructionTextTokenType.PossibleAddressToken, "$%.4x" % value, value), + InstructionTextToken(InstructionTextTokenType.TextToken, ", "), InstructionTextToken(InstructionTextTokenType.RegisterToken, "x")], # ABS_X + lambda value: [InstructionTextToken(InstructionTextTokenType.PossibleAddressToken, "$%.4x" % value, value), + InstructionTextToken(InstructionTextTokenType.TextToken, ", "), InstructionTextToken(InstructionTextTokenType.RegisterToken, "x")], # ABS_X_DEST + lambda value: [InstructionTextToken(InstructionTextTokenType.PossibleAddressToken, "$%.4x" % value, value), + InstructionTextToken(InstructionTextTokenType.TextToken, ", "), InstructionTextToken(InstructionTextTokenType.RegisterToken, "y")], # ABS_Y + lambda value: [InstructionTextToken(InstructionTextTokenType.PossibleAddressToken, "$%.4x" % value, value), + InstructionTextToken(InstructionTextTokenType.TextToken, ", "), InstructionTextToken(InstructionTextTokenType.RegisterToken, "y")], # ABS_Y_DEST + lambda value: [InstructionTextToken(InstructionTextTokenType.RegisterToken, "a")], # ACCUM + lambda value: [InstructionTextToken(InstructionTextTokenType.PossibleAddressToken, "$%.4x" % value, value)], # ADDR + lambda value: [InstructionTextToken(InstructionTextTokenType.TextToken, "#"), InstructionTextToken(InstructionTextTokenType.IntegerToken, "$%.2x" % value, value)], # IMMED + lambda value: [InstructionTextToken(InstructionTextTokenType.TextToken, "["), InstructionTextToken(InstructionTextTokenType.PossibleAddressToken, "$%.4x" % value, value), + InstructionTextToken(InstructionTextTokenType.TextToken, "]")], # IND + lambda value: [InstructionTextToken(InstructionTextTokenType.TextToken, "["), InstructionTextToken(InstructionTextTokenType.PossibleAddressToken, "$%.2x" % value, value), + InstructionTextToken(InstructionTextTokenType.TextToken, ", "), InstructionTextToken(InstructionTextTokenType.RegisterToken, "x"), + InstructionTextToken(InstructionTextTokenType.TextToken, "]")], # IND_X + lambda value: [InstructionTextToken(InstructionTextTokenType.TextToken, "["), InstructionTextToken(InstructionTextTokenType.PossibleAddressToken, "$%.2x" % value, value), + InstructionTextToken(InstructionTextTokenType.TextToken, ", "), InstructionTextToken(InstructionTextTokenType.RegisterToken, "x"), + InstructionTextToken(InstructionTextTokenType.TextToken, "]")], # IND_X_DEST + lambda value: [InstructionTextToken(InstructionTextTokenType.TextToken, "["), InstructionTextToken(InstructionTextTokenType.PossibleAddressToken, "$%.2x" % value, value), + InstructionTextToken(InstructionTextTokenType.TextToken, "], "), InstructionTextToken(InstructionTextTokenType.RegisterToken, "y")], # IND_Y + lambda value: [InstructionTextToken(InstructionTextTokenType.TextToken, "["), InstructionTextToken(InstructionTextTokenType.PossibleAddressToken, "$%.2x" % value, value), + InstructionTextToken(InstructionTextTokenType.TextToken, "], "), InstructionTextToken(InstructionTextTokenType.RegisterToken, "y")], # IND_Y_DEST + lambda value: [InstructionTextToken(InstructionTextTokenType.PossibleAddressToken, "$%.4x" % value, value)], # REL + lambda value: [InstructionTextToken(InstructionTextTokenType.PossibleAddressToken, "$%.2x" % value, value)], # ZERO + lambda value: [InstructionTextToken(InstructionTextTokenType.PossibleAddressToken, "$%.2x" % value, value)], # ZERO_DEST + lambda value: [InstructionTextToken(InstructionTextTokenType.PossibleAddressToken, "$%.2x" % value, value), + InstructionTextToken(InstructionTextTokenType.TextToken, ", "), InstructionTextToken(InstructionTextTokenType.RegisterToken, "x")], # ZERO_X + lambda value: [InstructionTextToken(InstructionTextTokenType.PossibleAddressToken, "$%.2x" % value, value), + InstructionTextToken(InstructionTextTokenType.TextToken, ", "), InstructionTextToken(InstructionTextTokenType.RegisterToken, "x")], # ZERO_X_DEST + lambda value: [InstructionTextToken(InstructionTextTokenType.PossibleAddressToken, "$%.2x" % value, value), + InstructionTextToken(InstructionTextTokenType.TextToken, ", "), InstructionTextToken(InstructionTextTokenType.RegisterToken, "y")], # ZERO_Y + lambda value: [InstructionTextToken(InstructionTextTokenType.PossibleAddressToken, "$%.2x" % value, value), + InstructionTextToken(InstructionTextTokenType.TextToken, ", "), InstructionTextToken(InstructionTextTokenType.RegisterToken, "y")] # ZERO_Y_DEST ] + def indirect_load(il, value): if (value & 0xff) == 0xff: lo_addr = il.const(2, value) @@ -189,8 +198,9 @@ def indirect_load(il, value): return il.or_expr(2, lo, hi) return il.load(2, il.const(2, value)) + def load_zero_page_16(il, value): - if il[value].operation == "LLIL_CONST": + if il[value].operation == LowLevelILOperation.LLIL_CONST: if il[value].value == 0xff: lo = il.zero_extend(2, il.load(1, il.const(2, 0xff))) hi = il.shift_left(2, il.zero_extend(2, il.load(1, il.const(2, 0)), il.const(2, 8))) @@ -204,34 +214,36 @@ def load_zero_page_16(il, value): hi = il.shift_left(2, il.zero_extend(2, il.load(1, hi_addr)), il.const(2, 8)) return il.or_expr(2, lo, hi) + OperandIL = [ - lambda il, value: None, # NONE - lambda il, value: il.load(1, il.const(2, value)), # ABS - lambda il, value: il.const(2, value), # ABS_DEST - lambda il, value: il.load(1, il.add(2, il.const(2, value), il.zero_extend(2, il.reg(1, "x")))), # ABS_X - lambda il, value: il.add(2, il.const(2, value), il.zero_extend(2, il.reg(1, "x"))), # ABS_X_DEST - lambda il, value: il.load(1, il.add(2, il.const(2, value), il.zero_extend(2, il.reg(1, "y")))), # ABS_Y - lambda il, value: il.add(2, il.const(2, value), il.zero_extend(2, il.reg(1, "y"))), # ABS_Y_DEST - lambda il, value: il.reg(1, "a"), # ACCUM - lambda il, value: il.const(2, value), # ADDR - lambda il, value: il.const(1, value), # IMMED - lambda il, value: indirect_load(il, value), # IND - lambda il, value: il.load(1, load_zero_page_16(il, il.add(1, il.const(1, value), il.reg(1, "x")))), # IND_X - lambda il, value: load_zero_page_16(il, il.add(1, il.const(1, value), il.reg(1, "x"))), # IND_X_DEST - lambda il, value: il.load(1, il.add(2, load_zero_page_16(il, il.const(1, value)), il.reg(1, "y"))), # IND_Y - lambda il, value: il.add(2, load_zero_page_16(il, il.const(1, value)), il.reg(1, "y")), # IND_Y_DEST - lambda il, value: il.const(2, value), # REL - lambda il, value: il.load(1, il.const(2, value)), # ZERO - lambda il, value: il.const(2, value), # ZERO_DEST - lambda il, value: il.load(1, il.zero_extend(2, il.add(1, il.const(1, value), il.reg(1, "x")))), # ZERO_X - lambda il, value: il.zero_extend(2, il.add(1, il.const(1, value), il.reg(1, "x"))), # ZERO_X_DEST - lambda il, value: il.load(1, il.zero_extend(2, il.add(1, il.const(1, value), il.reg(1, "y")))), # ZERO_Y - lambda il, value: il.zero_extend(2, il.add(1, il.const(1, value), il.reg(1, "y"))) # ZERO_Y_DEST + lambda il, value: None, # NONE + lambda il, value: il.load(1, il.const(2, value)), # ABS + lambda il, value: il.const(2, value), # ABS_DEST + lambda il, value: il.load(1, il.add(2, il.const(2, value), il.zero_extend(2, il.reg(1, "x")))), # ABS_X + lambda il, value: il.add(2, il.const(2, value), il.zero_extend(2, il.reg(1, "x"))), # ABS_X_DEST + lambda il, value: il.load(1, il.add(2, il.const(2, value), il.zero_extend(2, il.reg(1, "y")))), # ABS_Y + lambda il, value: il.add(2, il.const(2, value), il.zero_extend(2, il.reg(1, "y"))), # ABS_Y_DEST + lambda il, value: il.reg(1, "a"), # ACCUM + lambda il, value: il.const(2, value), # ADDR + lambda il, value: il.const(1, value), # IMMED + lambda il, value: indirect_load(il, value), # IND + lambda il, value: il.load(1, load_zero_page_16(il, il.add(1, il.const(1, value), il.reg(1, "x")))), # IND_X + lambda il, value: load_zero_page_16(il, il.add(1, il.const(1, value), il.reg(1, "x"))), # IND_X_DEST + lambda il, value: il.load(1, il.add(2, load_zero_page_16(il, il.const(1, value)), il.reg(1, "y"))), # IND_Y + lambda il, value: il.add(2, load_zero_page_16(il, il.const(1, value)), il.reg(1, "y")), # IND_Y_DEST + lambda il, value: il.const(2, value), # REL + lambda il, value: il.load(1, il.const(2, value)), # ZERO + lambda il, value: il.const(2, value), # ZERO_DEST + lambda il, value: il.load(1, il.zero_extend(2, il.add(1, il.const(1, value), il.reg(1, "x")))), # ZERO_X + lambda il, value: il.zero_extend(2, il.add(1, il.const(1, value), il.reg(1, "x"))), # ZERO_X_DEST + lambda il, value: il.load(1, il.zero_extend(2, il.add(1, il.const(1, value), il.reg(1, "y")))), # ZERO_Y + lambda il, value: il.zero_extend(2, il.add(1, il.const(1, value), il.reg(1, "y"))) # ZERO_Y_DEST ] + def cond_branch(il, cond, dest): t = None - if il[dest].operation == LLIL_CONST: + if il[dest].operation == LowLevelILOperation.LLIL_CONST: t = il.get_label_for_address(Architecture['6502'], il[dest].value) if t is None: t = LowLevelILLabel() @@ -246,9 +258,10 @@ def cond_branch(il, cond, dest): il.mark_label(f) return None + def jump(il, dest): label = None - if il[dest].operation == LLIL_CONST: + if il[dest].operation == LowLevelILOperation.LLIL_CONST: label = il.get_label_for_address(Architecture['6502'], il[dest].value) if label is None: il.append(il.jump(dest)) @@ -256,6 +269,7 @@ def jump(il, dest): il.append(il.goto(label)) return None + def get_p_value(il): c = il.flag_bit(1, "c", 0) z = il.flag_bit(1, "z", 1) @@ -267,6 +281,7 @@ def get_p_value(il): return il.or_expr(1, il.or_expr(1, il.or_expr(1, il.or_expr(1, il.or_expr(1, il.or_expr(1, c, z), i), d), b), v), s) + def set_p_value(il, value): il.append(il.set_reg(1, LLIL_TEMP(0), value)) il.append(il.set_flag("c", il.test_bit(1, il.reg(1, LLIL_TEMP(0)), il.const(1, 0x01)))) @@ -278,22 +293,24 @@ def set_p_value(il, value): il.append(il.set_flag("s", il.test_bit(1, il.reg(1, LLIL_TEMP(0)), il.const(1, 0x80)))) return None + def rti(il): set_p_value(il, il.pop(1)) return il.ret(il.pop(2)) + InstructionIL = { "adc": lambda il, operand: il.set_reg(1, "a", il.add_carry(1, il.reg(1, "a"), operand, flags = "*")), "asl": lambda il, operand: il.store(1, operand, il.shift_left(1, il.load(1, operand), il.const(1, 1), flags = "czs")), "asl@": lambda il, operand: il.set_reg(1, "a", il.shift_left(1, operand, il.const(1, 1), flags = "czs")), "and": lambda il, operand: il.set_reg(1, "a", il.and_expr(1, il.reg(1, "a"), operand, flags = "zs")), - "bcc": lambda il, operand: cond_branch(il, il.flag_condition(LLFC_UGE), operand), - "bcs": lambda il, operand: cond_branch(il, il.flag_condition(LLFC_ULT), operand), - "beq": lambda il, operand: cond_branch(il, il.flag_condition(LLFC_E), operand), + "bcc": lambda il, operand: cond_branch(il, il.flag_condition(LowLevelILFlagCondition.LLFC_UGE), operand), + "bcs": lambda il, operand: cond_branch(il, il.flag_condition(LowLevelILFlagCondition.LLFC_ULT), operand), + "beq": lambda il, operand: cond_branch(il, il.flag_condition(LowLevelILFlagCondition.LLFC_E), operand), "bit": lambda il, operand: il.and_expr(1, il.reg(1, "a"), operand, flags = "czs"), - "bmi": lambda il, operand: cond_branch(il, il.flag_condition(LLFC_NEG), operand), - "bne": lambda il, operand: cond_branch(il, il.flag_condition(LLFC_NE), operand), - "bpl": lambda il, operand: cond_branch(il, il.flag_condition(LLFC_POS), operand), + "bmi": lambda il, operand: cond_branch(il, il.flag_condition(LowLevelILFlagCondition.LLFC_NEG), operand), + "bne": lambda il, operand: cond_branch(il, il.flag_condition(LowLevelILFlagCondition.LLFC_NE), operand), + "bpl": lambda il, operand: cond_branch(il, il.flag_condition(LowLevelILFlagCondition.LLFC_POS), operand), "brk": lambda il, operand: il.system_call(), "bvc": lambda il, operand: cond_branch(il, il.not_expr(0, il.flag("v")), operand), "bvs": lambda il, operand: cond_branch(il, il.flag("v"), operand), @@ -345,6 +362,7 @@ InstructionIL = { "tya": lambda il, operand: il.set_reg(1, "a", il.reg(1, "y"), flags = "zs") } + class M6502(Architecture): name = "6502" address_size = 2 @@ -360,18 +378,18 @@ class M6502(Architecture): flags = ["c", "z", "i", "d", "b", "v", "s"] flag_write_types = ["*", "czs", "zvs", "zs"] flag_roles = { - "c": SpecialFlagRole, # Not a normal carry flag, subtract result is inverted - "z": ZeroFlagRole, - "v": OverflowFlagRole, - "s": NegativeSignFlagRole + "c": FlagRole.SpecialFlagRole, # Not a normal carry flag, subtract result is inverted + "z": FlagRole.ZeroFlagRole, + "v": FlagRole.OverflowFlagRole, + "s": FlagRole.NegativeSignFlagRole } flags_required_for_flag_condition = { - LLFC_UGE: ["c"], - LLFC_ULT: ["c"], - LLFC_E: ["z"], - LLFC_NE: ["z"], - LLFC_NEG: ["s"], - LLFC_POS: ["s"] + LowLevelILFlagCondition.LLFC_UGE: ["c"], + LowLevelILFlagCondition.LLFC_ULT: ["c"], + LowLevelILFlagCondition.LLFC_E: ["z"], + LowLevelILFlagCondition.LLFC_NE: ["z"], + LowLevelILFlagCondition.LLFC_NEG: ["s"], + LowLevelILFlagCondition.LLFC_POS: ["s"] } flags_written_by_flag_write_type = { "*": ["c", "z", "v", "s"], @@ -413,17 +431,17 @@ class M6502(Architecture): result.length = length if instr == "jmp": if operand == ADDR: - result.add_branch(UnconditionalBranch, struct.unpack("<H", data[1:3])[0]) + result.add_branch(BranchType.UnconditionalBranch, struct.unpack("<H", data[1:3])[0]) else: - result.add_branch(UnresolvedBranch) + result.add_branch(BranchType.UnresolvedBranch) elif instr == "jsr": - result.add_branch(CallDestination, struct.unpack("<H", data[1:3])[0]) + result.add_branch(BranchType.CallDestination, struct.unpack("<H", data[1:3])[0]) elif instr in ["rti", "rts"]: - result.add_branch(FunctionReturn) + result.add_branch(BranchType.FunctionReturn) if instr in ["bcc", "bcs", "beq", "bmi", "bne", "bpl", "bvc", "bvs"]: dest = (addr + 2 + struct.unpack("b", data[1])[0]) & 0xffff - result.add_branch(TrueBranch, dest) - result.add_branch(FalseBranch, addr + 2) + result.add_branch(BranchType.TrueBranch, dest) + result.add_branch(BranchType.FalseBranch, addr + 2) return result def perform_get_instruction_text(self, data, addr): @@ -432,7 +450,7 @@ class M6502(Architecture): return None tokens = [] - tokens.append(InstructionTextToken(TextToken, "%-7s " % instr.replace("@", ""))) + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, "%-7s " % instr.replace("@", ""))) tokens += OperandTokens[operand](value) return tokens, length @@ -488,12 +506,14 @@ class M6502(Architecture): return None return "\xa9" + chr(value & 0xff) + "\xea" + class NESView(BinaryView): name = "NES" long_name = "NES ROM" def __init__(self, data): BinaryView.__init__(self, parent_view = data, file_metadata = data.file) + self.platform = Architecture['6502'].standalone_platform @classmethod def is_valid_for_data(self, data): @@ -521,55 +541,55 @@ class NESView(BinaryView): self.rom_length = self.rom_banks * 0x4000 # Add mapping for RAM and hardware registers, not backed by file contents - self.add_auto_segment(0, 0x8000, 0, 0, SegmentReadable | SegmentWritable | SegmentExecutable) + self.add_auto_segment(0, 0x8000, 0, 0, SegmentFlag.SegmentReadable | SegmentFlag.SegmentWritable | SegmentFlag.SegmentExecutable) # Add ROM mappings self.add_auto_segment(0x8000, 0x4000, self.rom_offset + (self.__class__.bank * 0x4000), 0x4000, - SegmentReadable | SegmentExecutable) + SegmentFlag.SegmentReadable | SegmentFlag.SegmentExecutable) self.add_auto_segment(0xc000, 0x4000, self.rom_offset + self.rom_length - 0x4000, 0x4000, - SegmentReadable | SegmentExecutable) + SegmentFlag.SegmentReadable | SegmentFlag.SegmentExecutable) nmi = struct.unpack("<H", self.read(0xfffa, 2))[0] start = struct.unpack("<H", self.read(0xfffc, 2))[0] irq = struct.unpack("<H", self.read(0xfffe, 2))[0] - self.define_auto_symbol(Symbol(FunctionSymbol, nmi, "_nmi")) - self.define_auto_symbol(Symbol(FunctionSymbol, start, "_start")) - self.define_auto_symbol(Symbol(FunctionSymbol, irq, "_irq")) - self.add_function(Architecture['6502'].standalone_platform, nmi) - self.add_function(Architecture['6502'].standalone_platform, irq) - self.add_entry_point(Architecture['6502'].standalone_platform, start) + self.define_auto_symbol(Symbol(SymbolType.FunctionSymbol, nmi, "_nmi")) + self.define_auto_symbol(Symbol(SymbolType.FunctionSymbol, start, "_start")) + self.define_auto_symbol(Symbol(SymbolType.FunctionSymbol, irq, "_irq")) + self.add_function(nmi) + self.add_function(irq) + self.add_entry_point(start) # Hardware registers - self.define_auto_symbol(Symbol(DataSymbol, 0x2000, "PPUCTRL")) - self.define_auto_symbol(Symbol(DataSymbol, 0x2001, "PPUMASK")) - self.define_auto_symbol(Symbol(DataSymbol, 0x2002, "PPUSTATUS")) - self.define_auto_symbol(Symbol(DataSymbol, 0x2003, "OAMADDR")) - self.define_auto_symbol(Symbol(DataSymbol, 0x2004, "OAMDATA")) - self.define_auto_symbol(Symbol(DataSymbol, 0x2005, "PPUSCROLL")) - self.define_auto_symbol(Symbol(DataSymbol, 0x2006, "PPUADDR")) - self.define_auto_symbol(Symbol(DataSymbol, 0x2007, "PPUDATA")) - self.define_auto_symbol(Symbol(DataSymbol, 0x4000, "SQ1_VOL")) - self.define_auto_symbol(Symbol(DataSymbol, 0x4001, "SQ1_SWEEP")) - self.define_auto_symbol(Symbol(DataSymbol, 0x4002, "SQ1_LO")) - self.define_auto_symbol(Symbol(DataSymbol, 0x4003, "SQ1_HI")) - self.define_auto_symbol(Symbol(DataSymbol, 0x4004, "SQ2_VOL")) - self.define_auto_symbol(Symbol(DataSymbol, 0x4005, "SQ2_SWEEP")) - self.define_auto_symbol(Symbol(DataSymbol, 0x4006, "SQ2_LO")) - self.define_auto_symbol(Symbol(DataSymbol, 0x4007, "SQ2_HI")) - self.define_auto_symbol(Symbol(DataSymbol, 0x4008, "TRI_LINEAR")) - self.define_auto_symbol(Symbol(DataSymbol, 0x400a, "TRI_LO")) - self.define_auto_symbol(Symbol(DataSymbol, 0x400b, "TRI_HI")) - self.define_auto_symbol(Symbol(DataSymbol, 0x400c, "NOISE_VOL")) - self.define_auto_symbol(Symbol(DataSymbol, 0x400e, "NOISE_LO")) - self.define_auto_symbol(Symbol(DataSymbol, 0x400f, "NOISE_HI")) - self.define_auto_symbol(Symbol(DataSymbol, 0x4010, "DMC_FREQ")) - self.define_auto_symbol(Symbol(DataSymbol, 0x4011, "DMC_RAW")) - self.define_auto_symbol(Symbol(DataSymbol, 0x4012, "DMC_START")) - self.define_auto_symbol(Symbol(DataSymbol, 0x4013, "DMC_LEN")) - self.define_auto_symbol(Symbol(DataSymbol, 0x4014, "OAMDMA")) - self.define_auto_symbol(Symbol(DataSymbol, 0x4015, "SND_CHN")) - self.define_auto_symbol(Symbol(DataSymbol, 0x4016, "JOY1")) - self.define_auto_symbol(Symbol(DataSymbol, 0x4017, "JOY2")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x2000, "PPUCTRL")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x2001, "PPUMASK")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x2002, "PPUSTATUS")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x2003, "OAMADDR")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x2004, "OAMDATA")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x2005, "PPUSCROLL")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x2006, "PPUADDR")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x2007, "PPUDATA")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4000, "SQ1_VOL")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4001, "SQ1_SWEEP")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4002, "SQ1_LO")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4003, "SQ1_HI")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4004, "SQ2_VOL")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4005, "SQ2_SWEEP")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4006, "SQ2_LO")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4007, "SQ2_HI")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4008, "TRI_LINEAR")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x400a, "TRI_LO")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x400b, "TRI_HI")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x400c, "NOISE_VOL")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x400e, "NOISE_LO")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x400f, "NOISE_HI")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4010, "DMC_FREQ")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4011, "DMC_RAW")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4012, "DMC_START")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4013, "DMC_LEN")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4014, "OAMDMA")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4015, "SND_CHN")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4016, "JOY1")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4017, "JOY2")) sym_files = [self.file.filename + ".%x.nl" % self.__class__.bank, self.file.filename + ".ram.nl", @@ -584,9 +604,9 @@ class NESView(BinaryView): break addr = int(sym[0][1:], 16) name = sym[1] - self.define_auto_symbol(Symbol(FunctionSymbol, addr, name)) + self.define_auto_symbol(Symbol(SymbolType.FunctionSymbol, addr, name)) if addr >= 0x8000: - self.add_function(Architecture['6502'].standalone_platform, addr) + self.add_function(addr) return True except: @@ -599,6 +619,7 @@ class NESView(BinaryView): def perform_get_entry_point(self): return struct.unpack("<H", str(self.perform_read(0xfffc, 2)))[0] + banks = [] for i in xrange(0, 32): class NESViewBank(NESView): diff --git a/python/examples/nsf.py b/python/examples/nsf.py new file mode 100644 index 00000000..b1bac3a8 --- /dev/null +++ b/python/examples/nsf.py @@ -0,0 +1,145 @@ +# 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. +# +# +# Simple NSF file loader, primarily for analyzing: +# https://scarybeastsecurity.blogspot.com/2016/11/0day-exploit-compromising-linux-desktop.html +# + +from binaryninja.binaryview import BinaryView +from binaryninja.architecture import Architecture +from binaryninja.log import log_error, log_info +from binaryninja.types import Symbol +from binaryninja.enums import SymbolType, SegmentFlag + +import struct +import traceback + + +class NSFView(BinaryView): + name = "NSF" + long_name = "Nintendo Sound Format" + + def __init__(self, data): + BinaryView.__init__(self, parent_view=data, file_metadata=data.file) + self.platform = Architecture["6502"].standalone_platform + + @classmethod + def is_valid_for_data(self, data): + hdr = data.read(0, 128) + if len(hdr) < 128: + return False + if hdr[0:5] != "NESM\x1a": + return False + song_count = struct.unpack("B", hdr[6])[0] + if song_count < 1: + log_info("Appears to be an NSF, but no songs.") + return False + return True + + def init(self): + try: + hdr = self.parent_view.read(0, 128) + self.version = struct.unpack("B", hdr[5])[0] + self.song_count = struct.unpack("B", hdr[6])[0] + self.starting_song = struct.unpack("B", hdr[7])[0] + self.load_address = struct.unpack("<H", hdr[8:10])[0] + self.init_address = struct.unpack("<H", hdr[10:12])[0] + self.play_address = struct.unpack("<H", hdr[12:14])[0] + self.song_name = hdr[15].split('\0')[0] + self.artist_name = hdr[46].split('\0')[0] + self.copyright_name = hdr[78].split('\0')[0] + self.play_speed_ntsc = struct.unpack("<H", hdr[110:112])[0] + self.bank_switching = hdr[112:120] + self.play_speed_pal = struct.unpack("<H", hdr[120:122])[0] + self.pal_ntsc_bits = struct.unpack("B", hdr[122])[0] + self.pal = True if (self.pal_ntsc_bits & 1) == 1 else False + self.ntsc = not self.pal + if self.pal_ntsc_bits & 2 == 2: + self.pal = True + self.ntsc = True + self.extra_sound_bits = struct.unpack("B", hdr[123])[0] + + if self.bank_switching == "\0" * 8: + # no bank switching + self.load_address & 0xFFF + self.rom_offset = 128 + + else: + # bank switching not implemented + log_info("Bank switching not implemented in this loader.") + + # Add mapping for RAM and hardware registers, not backed by file contents + self.add_auto_segment(0, 0x8000, 0, 0, SegmentFlag.SegmentReadable | SegmentFlag.SegmentWritable | SegmentFlag.SegmentExecutable) + + # Add ROM mappings + self.add_auto_segment(0x8000, 0x4000, self.rom_offset, 0x4000, + SegmentFlag.SegmentReadable | SegmentFlag.SegmentExecutable) + + self.define_auto_symbol(Symbol(SymbolType.FunctionSymbol, self.play_address, "_play")) + self.define_auto_symbol(Symbol(SymbolType.FunctionSymbol, self.init_address, "_init")) + self.add_entry_point(self.init_address) + self.add_function(self.play_address) + + # Hardware registers + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x2000, "PPUCTRL")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x2001, "PPUMASK")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x2002, "PPUSTATUS")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x2003, "OAMADDR")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x2004, "OAMDATA")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x2005, "PPUSCROLL")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x2006, "PPUADDR")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x2007, "PPUDATA")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4000, "SQ1_VOL")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4001, "SQ1_SWEEP")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4002, "SQ1_LO")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4003, "SQ1_HI")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4004, "SQ2_VOL")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4005, "SQ2_SWEEP")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4006, "SQ2_LO")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4007, "SQ2_HI")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4008, "TRI_LINEAR")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x400a, "TRI_LO")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x400b, "TRI_HI")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x400c, "NOISE_VOL")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x400e, "NOISE_LO")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x400f, "NOISE_HI")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4010, "DMC_FREQ")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4011, "DMC_RAW")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4012, "DMC_START")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4013, "DMC_LEN")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4014, "OAMDMA")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4015, "SND_CHN")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4016, "JOY1")) + self.define_auto_symbol(Symbol(SymbolType.DataSymbol, 0x4017, "JOY2")) + + return True + except: + log_error(traceback.format_exc()) + return False + + def perform_is_executable(self): + return True + + def perform_get_entry_point(self): + return struct.unpack("<H", str(self.perform_read(0x0a, 2)))[0] + + +NSFView.register() diff --git a/python/examples/print_syscalls.py b/python/examples/print_syscalls.py index 7e93356c..2af4d38d 100644 --- a/python/examples/print_syscalls.py +++ b/python/examples/print_syscalls.py @@ -1,50 +1,55 @@ #!/usr/bin/env python -""" - Thanks to @theqlabs from arm.ninja for the nice writeup and idea for this plugin: - http://arm.ninja/2016/03/08/intro-to-binary-ninja-api/ -""" -import sys -from itertools import chain - -from binaryninja import BinaryView, core - +# 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. -def print_syscalls(bv): - """ Print Syscall numbers for a provided binaryview """ - calling_convention = bv.platform.system_call_convention - if not calling_convention: - print('Error: No syscall convention available for {:s}'.format(bv.platform)) - return - - register = calling_convention.int_arg_regs[0] - - for func in bv.functions: - syscalls = (il for il in chain.from_iterable(func.low_level_il) - if il.operation == core.LLIL_SYSCALL) - for il in syscalls: - value = func.get_reg_value_at(bv.arch, il.address, register).value - print("System call address: {:#x} - {:d}".format(il.address, value)) +# Thanks to @theqlabs from arm.ninja for the nice writeup and idea for this plugin: +# http://arm.ninja/2016/03/08/intro-to-binary-ninja-api/ +import sys +from itertools import chain -def main(): - if len(sys.argv) != 2: - print('Usage: {} <file>'.format(sys.argv[0])) - return -1 +from binaryninja.binaryview import BinaryViewType +from binaryninja.enums import LowLevelILOperation - target = sys.argv[1] - bv = BinaryView.open(target) - view_type = next(bvt for bvt in bv.available_view_types if bvt.name != 'Raw') - if not view_type: - print('Error: Unable to get any other view type besides Raw') - return -1 +def print_syscalls(fileName): + """ Print Syscall numbers for a provided file """ + bv = BinaryViewType.get_view_of_file(fileName) + calling_convention = bv.platform.system_call_convention + if calling_convention is None: + print('Error: No syscall convention available for {:s}'.format(bv.platform)) + return - bv = bv.file.get_view_of_type(view_type.name) - bv.update_analysis_and_wait() + register = calling_convention.int_arg_regs[0] - print_syscalls(bv) + for func in bv.functions: + syscalls = (il for il in chain.from_iterable(func.low_level_il) + if il.operation == LowLevelILOperation.LLIL_SYSCALL) + for il in syscalls: + value = func.get_reg_value_at(il.address, register).value + print("System call address: {:#x} - {:d}".format(il.address, value)) if __name__ == "__main__": - sys.exit(main()) + if len(sys.argv) != 2: + print('Usage: {} <file>'.format(sys.argv[0])) + else: + print_syscalls(sys.argv[1]) diff --git a/python/examples/version_switcher.py b/python/examples/version_switcher.py index 6199c578..bc4f576c 100644 --- a/python/examples/version_switcher.py +++ b/python/examples/version_switcher.py @@ -1,4 +1,24 @@ #!/usr/bin/env python +# 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 sys import binaryninja import datetime @@ -7,10 +27,11 @@ chandefault = binaryninja.UpdateChannel.list[0].name channel = None versions = [] + def load_channel(newchannel): global channel global versions - if (channel != None and newchannel == channel.name): + if (channel is None and newchannel == channel.name): print "Same channel, not updating." else: try: @@ -22,6 +43,7 @@ def load_channel(newchannel): print "%s is not a valid channel name. Defaulting to " % chandefault channel = binaryninja.UpdateChannel[chandefault] + def select(version): done = False date = datetime.datetime.fromtimestamp(version.time).strftime('%c') @@ -54,34 +76,37 @@ def select(version): print "binaryninja.core_version %s" % binaryninja.core_version print "Updating..." print version.update() - #forward updating won't work without reloading + # forward updating won't work without reloading sys.exit() else: print "Invalid selection" + def list_channels(): done = False print "\tSelect channel:\n" while not done: channel_list = binaryninja.UpdateChannel.list for index, item in enumerate(channel_list): - print "\t%d)\t%s" % (index+1, item.name) - print "\t%d)\t%s" % (len(channel_list)+1, "Main Menu") + print "\t%d)\t%s" % (index + 1, item.name) + print "\t%d)\t%s" % (len(channel_list) + 1, "Main Menu") selection = raw_input('Choice: ') if selection.isdigit(): selection = int(selection) else: selection = 0 - if (selection <= 0 or selection > len(channel_list)+1): + if (selection <= 0 or selection > len(channel_list) + 1): print "%s is an invalid choice." % selection else: done = True if (selection != len(channel_list) + 1): load_channel(channel_list[selection - 1].name) + def toggle_updates(): binaryninja.set_auto_updates_enabled(not binaryninja.are_auto_updates_enabled()) + def main(): global channel done = False diff --git a/python/fileaccessor.py b/python/fileaccessor.py new file mode 100644 index 00000000..8fec43a1 --- /dev/null +++ b/python/fileaccessor.py @@ -0,0 +1,88 @@ +# 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 traceback +import ctypes + +# Binary Ninja components +import _binaryninjacore as core +import log + + +class FileAccessor(object): + def __init__(self): + self._cb = core.BNFileAccessor() + self._cb.context = 0 + self._cb.getLength = self._cb.getLength.__class__(self._get_length) + self._cb.read = self._cb.read.__class__(self._read) + self._cb.write = self._cb.write.__class__(self._write) + + def __len__(self): + return self.get_length() + + def _get_length(self, ctxt): + try: + return self.get_length() + except: + log.log_error(traceback.format_exc()) + return 0 + + def _read(self, ctxt, dest, offset, length): + try: + data = self.read(offset, length) + if data is None: + return 0 + if len(data) > length: + data = data[0:length] + ctypes.memmove(dest, 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.write(offset, data.raw) + except: + log.log_error(traceback.format_exc()) + return 0 + + +class CoreFileAccessor(FileAccessor): + def __init__(self, accessor): + self._cb.context = accessor.context + self._cb.getLength = accessor.getLength + self._cb.read = accessor.read + self._cb.write = accessor.write + + def get_length(self): + return self._cb.getLength(self._cb.context) + + def read(self, offset, length): + data = ctypes.create_string_buffer(length) + length = self._cb.read(self._cb.context, data, offset, length) + return data.raw[0:length] + + def write(self, offset, value): + value = str(value) + data = ctypes.create_string_buffer(value) + return self._cb.write(self._cb.context, offset, data, len(value)) diff --git a/python/filemetadata.py b/python/filemetadata.py new file mode 100644 index 00000000..f6593405 --- /dev/null +++ b/python/filemetadata.py @@ -0,0 +1,340 @@ +# 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 traceback +import ctypes + +# Binary Ninja components +import _binaryninjacore as core +import startup +import associateddatastore +import log +import binaryview + + +class NavigationHandler(object): + def _register(self, handle): + self._cb = core.BNNavigationHandler() + self._cb.context = 0 + self._cb.getCurrentView = self._cb.getCurrentView.__class__(self._get_current_view) + self._cb.getCurrentOffset = self._cb.getCurrentOffset.__class__(self._get_current_offset) + self._cb.navigate = self._cb.navigate.__class__(self._navigate) + core.BNSetFileMetadataNavigationHandler(handle, self._cb) + + def _get_current_view(self, ctxt): + try: + view = self.get_current_view() + except: + log.log_error(traceback.format_exc()) + view = "" + return core.BNAllocString(view) + + def _get_current_offset(self, ctxt): + try: + return self.get_current_offset() + except: + log.log_error(traceback.format_exc()) + return 0 + + def _navigate(self, ctxt, view, offset): + try: + return self.navigate(view, offset) + except: + log.log_error(traceback.format_exc()) + return False + + +class _FileMetadataAssociatedDataStore(associateddatastore._AssociatedDataStore): + _defaults = {} + + +class FileMetadata(object): + _associated_data = {} + + """ + ``class FileMetadata`` represents the file being analyzed by Binary Ninja. It is responsible for opening, + closing, creating the database (.bndb) files, and is used to keep track of undoable actions. + """ + def __init__(self, filename = None, handle = None): + """ + Instantiates a new FileMetadata class. + + :param filename: The string path to the file to be opened. Defaults to None. + :param handle: A handle to the underlying C FileMetadata object. Defaults to None. + """ + if handle is not None: + self.handle = core.handle_of_type(handle, core.BNFileMetadata) + else: + startup._init_plugins() + self.handle = core.BNCreateFileMetadata() + if filename is not None: + core.BNSetFilename(self.handle, str(filename)) + self.nav = None + + def __del__(self): + if self.navigation is not None: + core.BNSetFileMetadataNavigationHandler(self.handle, None) + core.BNFreeFileMetadata(self.handle) + + @classmethod + def _unregister(cls, f): + handle = ctypes.cast(f, 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): + _FileMetadataAssociatedDataStore.set_default(name, value) + + @property + def filename(self): + """The name of the file (read/write)""" + return core.BNGetFilename(self.handle) + + @filename.setter + def filename(self, value): + core.BNSetFilename(self.handle, str(value)) + + @property + def modified(self): + """Boolean result of whether the file is modified (Inverse of 'saved' property) (read/write)""" + return core.BNIsFileModified(self.handle) + + @modified.setter + def modified(self, value): + if value: + core.BNMarkFileModified(self.handle) + else: + core.BNMarkFileSaved(self.handle) + + @property + def analysis_changed(self): + """Boolean result of whether the auto-analysis results have changed (read-only)""" + return core.BNIsAnalysisChanged(self.handle) + + @property + def has_database(self): + """Whether the FileMetadata is backed by a database (read-only)""" + return core.BNIsBackedByDatabase(self.handle) + + @property + def view(self): + return core.BNGetCurrentView(self.handle) + + @view.setter + def view(self, value): + core.BNNavigate(self.handle, str(value), core.BNGetCurrentOffset(self.handle)) + + @property + def offset(self): + """The current offset into the file (read/write)""" + return core.BNGetCurrentOffset(self.handle) + + @offset.setter + def offset(self, value): + core.BNNavigate(self.handle, core.BNGetCurrentView(self.handle), value) + + @property + def raw(self): + """Gets the "Raw" BinaryView of the file""" + view = core.BNGetFileViewOfType(self.handle, "Raw") + if view is None: + return None + return binaryview.BinaryView(file_metadata = self, handle = view) + + @property + def saved(self): + """Boolean result of whether the file has been saved (Inverse of 'modified' property) (read/write)""" + return not core.BNIsFileModified(self.handle) + + @saved.setter + def saved(self, value): + if value: + core.BNMarkFileSaved(self.handle) + else: + core.BNMarkFileModified(self.handle) + + @property + def navigation(self): + return self.nav + + @navigation.setter + def navigation(self, value): + value._register(self.handle) + self.nav = value + + @property + def session_data(self): + """Dictionary object where plugins can store arbitrary data associated with the file""" + handle = ctypes.cast(self.handle, ctypes.c_void_p) + if handle.value not in FileMetadata._associated_data: + obj = _FileMetadataAssociatedDataStore() + FileMetadata._associated_data[handle.value] = obj + return obj + else: + return FileMetadata._associated_data[handle.value] + + def close(self): + """ + Closes the underlying file handle. It is recommended that this is done in a + `finally` clause to avoid handle leaks. + """ + core.BNCloseFile(self.handle) + + def begin_undo_actions(self): + """ + ``begin_undo_actions`` start recording actions taken so the can be undone at some point. + + :rtype: None + :Example: + + >>> bv.get_disassembly(0x100012f1) + 'xor eax, eax' + >>> bv.begin_undo_actions() + >>> bv.convert_to_nop(0x100012f1) + True + >>> bv.commit_undo_actions() + >>> bv.get_disassembly(0x100012f1) + 'nop' + >>> bv.undo() + >>> bv.get_disassembly(0x100012f1) + 'xor eax, eax' + >>> + """ + core.BNBeginUndoActions(self.handle) + + def commit_undo_actions(self): + """ + ``commit_undo_actions`` commit the actions taken since the last commit to the undo database. + + :rtype: None + :Example: + + >>> bv.get_disassembly(0x100012f1) + 'xor eax, eax' + >>> bv.begin_undo_actions() + >>> bv.convert_to_nop(0x100012f1) + True + >>> bv.commit_undo_actions() + >>> bv.get_disassembly(0x100012f1) + 'nop' + >>> bv.undo() + >>> bv.get_disassembly(0x100012f1) + 'xor eax, eax' + >>> + """ + core.BNCommitUndoActions(self.handle) + + def undo(self): + """ + ``undo`` undo the last commited action in the undo database. + + :rtype: None + :Example: + + >>> bv.get_disassembly(0x100012f1) + 'xor eax, eax' + >>> bv.begin_undo_actions() + >>> bv.convert_to_nop(0x100012f1) + True + >>> bv.commit_undo_actions() + >>> bv.get_disassembly(0x100012f1) + 'nop' + >>> bv.undo() + >>> bv.get_disassembly(0x100012f1) + 'xor eax, eax' + >>> bv.redo() + >>> bv.get_disassembly(0x100012f1) + 'nop' + >>> + """ + core.BNUndo(self.handle) + + def redo(self): + """ + ``redo`` redo the last commited action in the undo database. + + :rtype: None + :Example: + + >>> bv.get_disassembly(0x100012f1) + 'xor eax, eax' + >>> bv.begin_undo_actions() + >>> bv.convert_to_nop(0x100012f1) + True + >>> bv.commit_undo_actions() + >>> bv.get_disassembly(0x100012f1) + 'nop' + >>> bv.undo() + >>> bv.get_disassembly(0x100012f1) + 'xor eax, eax' + >>> bv.redo() + >>> bv.get_disassembly(0x100012f1) + 'nop' + >>> + """ + core.BNRedo(self.handle) + + def navigate(self, view, offset): + return core.BNNavigate(self.handle, str(view), offset) + + def create_database(self, filename, progress_func = None): + if progress_func is None: + return core.BNCreateDatabase(self.raw.handle, str(filename)) + else: + return core.BNCreateDatabaseWithProgress(self.raw.handle, str(filename), None, + ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.c_ulonglong, ctypes.c_ulonglong)( + lambda ctxt, cur, total: progress_func(cur, total))) + + def open_existing_database(self, filename, progress_func = None): + if progress_func is None: + view = core.BNOpenExistingDatabase(self.handle, str(filename)) + else: + view = core.BNOpenExistingDatabaseWithProgress(self.handle, str(filename), None, + ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.c_ulonglong, ctypes.c_ulonglong)( + lambda ctxt, cur, total: progress_func(cur, total))) + if view is None: + return None + return binaryview.BinaryView(file_metadata = self, handle = view) + + def save_auto_snapshot(self, progress_func = None): + if progress_func is None: + return core.BNSaveAutoSnapshot(self.raw.handle) + else: + return core.BNSaveAutoSnapshotWithProgress(self.raw.handle, None, + ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.c_ulonglong, ctypes.c_ulonglong)( + lambda ctxt, cur, total: progress_func(cur, total))) + + def get_view_of_type(self, name): + view = core.BNGetFileViewOfType(self.handle, str(name)) + if view is None: + view_type = core.BNGetBinaryViewTypeByName(str(name)) + if view_type is None: + return None + view = core.BNCreateBinaryViewOfType(view_type, self.raw.handle) + if view is None: + return None + return binaryview.BinaryView(file_metadata = self, handle = view) + + def __setattr__(self, name, value): + try: + object.__setattr__(self, name, value) + except AttributeError: + raise AttributeError("attribute '%s' is read only" % name) diff --git a/python/function.py b/python/function.py new file mode 100644 index 00000000..7d03585c --- /dev/null +++ b/python/function.py @@ -0,0 +1,1251 @@ +# 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 threading +import traceback +import ctypes + +# Binary Ninja components +import _binaryninjacore as core +from enums import (FunctionGraphType, BranchType, SymbolType, InstructionTextTokenType, + HighlightStandardColor, HighlightColorStyle, RegisterValueType, ImplicitRegisterExtend, + DisassemblyOption, IntegerDisplayType) +import architecture +import highlight +import associateddatastore +import types +import basicblock +import lowlevelil +import binaryview +import log + + +class LookupTableEntry(object): + def __init__(self, from_values, to_value): + self.from_values = from_values + self.to_value = to_value + + def __repr__(self): + return "[%s] -> %#x" % (', '.join(["%#x" % i for i in self.from_values]), self.to_value) + + +class RegisterValue(object): + def __init__(self, arch, value): + self.type = RegisterValueType(value.state) + if value.state == RegisterValueType.EntryValue: + self.reg = arch.get_reg_name(value.reg) + elif value.state == RegisterValueType.OffsetFromEntryValue: + self.reg = arch.get_reg_name(value.reg) + self.offset = value.value + elif value.state == RegisterValueType.ConstantValue: + self.value = value.value + elif value.state == RegisterValueType.StackFrameOffset: + self.offset = value.value + elif value.state == RegisterValueType.SignedRangeValue: + self.offset = value.value + self.start = value.rangeStart + self.end = value.rangeEnd + self.step = value.rangeStep + if self.start & (1 << 63): + self.start |= ~((1 << 63) - 1) + if self.end & (1 << 63): + self.end |= ~((1 << 63) - 1) + elif value.state == RegisterValueType.UnsignedRangeValue: + self.offset = value.value + self.start = value.rangeStart + self.end = value.rangeEnd + self.step = value.rangeStep + elif value.state == RegisterValueType.LookupTableValue: + self.table = [] + self.mapping = {} + for i in xrange(0, value.rangeEnd): + from_list = [] + for j in xrange(0, value.table[i].fromCount): + from_list.append(value.table[i].fromValues[j]) + self.mapping[value.table[i].fromValues[j]] = value.table[i].toValue + self.table.append(LookupTableEntry(from_list, value.table[i].toValue)) + elif value.state == RegisterValueType.OffsetFromUndeterminedValue: + self.offset = value.value + + def __repr__(self): + if self.type == RegisterValueType.EntryValue: + return "<entry %s>" % self.reg + if self.type == RegisterValueType.OffsetFromEntryValue: + return "<entry %s + %#x>" % (self.reg, self.offset) + if self.type == RegisterValueType.ConstantValue: + return "<const %#x>" % self.value + if self.type == RegisterValueType.StackFrameOffset: + return "<stack frame offset %#x>" % self.offset + if (self.type == RegisterValueType.SignedRangeValue) or (self.type == RegisterValueType.UnsignedRangeValue): + if self.step == 1: + return "<range: %#x to %#x>" % (self.start, self.end) + return "<range: %#x to %#x, step %#x>" % (self.start, self.end, self.step) + if self.type == RegisterValueType.LookupTableValue: + return "<table: %s>" % ', '.join([repr(i) for i in self.table]) + if self.type == RegisterValueType.OffsetFromUndeterminedValue: + return "<undetermined with offset %#x>" % self.offset + return "<undetermined>" + + +class StackVariable(object): + def __init__(self, ofs, name, t): + self.offset = ofs + self.name = name + self.type = t + + def __repr__(self): + return "<var@%x: %s %s>" % (self.offset, self.type, self.name) + + def __str__(self): + return self.name + + +class StackVariableReference(object): + def __init__(self, src_operand, t, name, start_ofs, ref_ofs): + self.source_operand = src_operand + self.type = t + self.name = name + self.starting_offset = start_ofs + self.referenced_offset = ref_ofs + if self.source_operand == 0xffffffff: + self.source_operand = None + + def __repr__(self): + if self.source_operand is None: + if self.referenced_offset != self.starting_offset: + return "<ref to %s%+#x>" % (self.name, self.referenced_offset - self.starting_offset) + return "<ref to %s>" % self.name + if self.referenced_offset != self.starting_offset: + return "<operand %d ref to %s%+#x>" % (self.source_operand, self.name, self.referenced_offset) + return "<operand %d ref to %s>" % (self.source_operand, self.name) + + +class ConstantReference(object): + def __init__(self, val, size): + self.value = val + self.size = size + + def __repr__(self): + if self.size == 0: + return "<constant %#x>" % self.value + return "<constant %#x size %d>" % (self.value, self.size) + + +class IndirectBranchInfo(object): + def __init__(self, source_arch, source_addr, dest_arch, dest_addr, auto_defined): + self.source_arch = source_arch + self.source_addr = source_addr + self.dest_arch = dest_arch + self.dest_addr = dest_addr + self.auto_defined = auto_defined + + def __repr__(self): + return "<branch %s:%#x -> %s:%#x>" % (self.source_arch.name, self.source_addr, self.dest_arch.name, self.dest_addr) + + +class _FunctionAssociatedDataStore(associateddatastore._AssociatedDataStore): + _defaults = {} + + +class Function(object): + _associated_data = {} + + def __init__(self, view, handle): + self._view = view + self.handle = core.handle_of_type(handle, core.BNFunction) + self._advanced_analysis_requests = 0 + + def __del__(self): + if self._advanced_analysis_requests > 0: + core.BNReleaseAdvancedFunctionAnalysisDataMultiple(self.handle, self._advanced_analysis_requests) + core.BNFreeFunction(self.handle) + + @classmethod + def _unregister(cls, func): + handle = ctypes.cast(func, 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): + _FunctionAssociatedDataStore.set_default(name, value) + + @property + def name(self): + """Symbol name for the function""" + return self.symbol.name + + @name.setter + def name(self, value): + if value is None: + if self.symbol is not None: + self.view.undefine_user_symbol(self.symbol) + else: + symbol = types.Symbol(SymbolType.FunctionSymbol, self.start, value) + self.view.define_user_symbol(symbol) + + @property + def view(self): + """Function view (read-only)""" + return self._view + + @property + def arch(self): + """Function architecture (read-only)""" + arch = core.BNGetFunctionArchitecture(self.handle) + if arch is None: + return None + return architecture.Architecture(arch) + + @property + def platform(self): + """Function platform (read-only)""" + platform = core.BNGetFunctionPlatform(self.handle) + if platform is None: + return None + return platform.Platform(None, handle = platform) + + @property + def start(self): + """Function start (read-only)""" + return core.BNGetFunctionStart(self.handle) + + @property + def symbol(self): + """Function symbol(read-only)""" + sym = core.BNGetFunctionSymbol(self.handle) + if sym is None: + return None + return types.Symbol(None, None, None, handle = sym) + + @property + def auto(self): + """Whether function was automatically discovered (read-only)""" + return core.BNWasFunctionAutomaticallyDiscovered(self.handle) + + @property + def can_return(self): + """Whether function can return (read-only)""" + return core.BNCanFunctionReturn(self.handle) + + @property + def explicitly_defined_type(self): + """Whether function has explicitly defined types (read-only)""" + return core.BNHasExplicitlyDefinedType(self.handle) + + @property + def needs_update(self): + """Whether the function has analysis that needs to be updated (read-only)""" + return core.BNIsFunctionUpdateNeeded(self.handle) + + @property + def basic_blocks(self): + """List of basic blocks (read-only)""" + count = ctypes.c_ulonglong() + blocks = core.BNGetFunctionBasicBlockList(self.handle, count) + result = [] + for i in xrange(0, count.value): + result.append(basicblock.BasicBlock(self._view, core.BNNewBasicBlockReference(blocks[i]))) + core.BNFreeBasicBlockList(blocks, count.value) + return result + + @property + def comments(self): + """Dict of comments (read-only)""" + count = ctypes.c_ulonglong() + addrs = core.BNGetCommentedAddresses(self.handle, count) + result = {} + for i in xrange(0, count.value): + result[addrs[i]] = self.get_comment_at(addrs[i]) + core.BNFreeAddressList(addrs) + return result + + @property + def low_level_il(self): + """Function low level IL (read-only)""" + return lowlevelil.LowLevelILFunction(self.arch, core.BNGetFunctionLowLevelIL(self.handle), self) + + @property + def lifted_il(self): + """Function lifted IL (read-only)""" + return lowlevelil.LowLevelILFunction(self.arch, core.BNGetFunctionLiftedIL(self.handle), self) + + @property + def function_type(self): + """Function type object""" + return types.Type(core.BNGetFunctionType(self.handle)) + + @function_type.setter + def function_type(self, value): + self.set_user_type(value) + + @property + def stack_layout(self): + """List of function stack (read-only)""" + count = ctypes.c_ulonglong() + v = core.BNGetStackLayout(self.handle, count) + result = [] + for i in xrange(0, count.value): + result.append(StackVariable(v[i].offset, v[i].name, types.Type(handle = core.BNNewTypeReference(v[i].type)))) + result.sort(key = lambda x: x.offset) + core.BNFreeStackLayout(v, count.value) + return result + + @property + def indirect_branches(self): + """List of indirect branches (read-only)""" + count = ctypes.c_ulonglong() + branches = core.BNGetIndirectBranches(self.handle, count) + result = [] + for i in xrange(0, count.value): + result.append(IndirectBranchInfo(architecture.Architecture(branches[i].sourceArch), branches[i].sourceAddr, architecture.Architecture(branches[i].destArch), branches[i].destAddr, branches[i].autoDefined)) + core.BNFreeIndirectBranchList(branches) + return result + + @property + def session_data(self): + """Dictionary object where plugins can store arbitrary data associated with the function""" + handle = ctypes.cast(self.handle, ctypes.c_void_p) + if handle.value not in Function._associated_data: + obj = _FunctionAssociatedDataStore() + Function._associated_data[handle.value] = obj + return obj + else: + return Function._associated_data[handle.value] + + def __iter__(self): + count = ctypes.c_ulonglong() + blocks = core.BNGetFunctionBasicBlockList(self.handle, count) + try: + for i in xrange(0, count.value): + yield basicblock.BasicBlock(self._view, core.BNNewBasicBlockReference(blocks[i])) + finally: + core.BNFreeBasicBlockList(blocks, count.value) + + def __setattr__(self, name, value): + try: + object.__setattr__(self, name, value) + except AttributeError: + raise AttributeError("attribute '%s' is read only" % name) + + def __repr__(self): + arch = self.arch + if arch: + return "<func: %s@%#x>" % (arch.name, self.start) + else: + return "<func: %#x>" % self.start + + def mark_recent_use(self): + core.BNMarkFunctionAsRecentlyUsed(self.handle) + + def get_comment_at(self, addr): + return core.BNGetCommentForAddress(self.handle, addr) + + def set_comment(self, addr, comment): + core.BNSetCommentForAddress(self.handle, addr, comment) + + def get_low_level_il_at(self, addr, arch=None): + """ + ``get_low_level_il_at`` gets the LowLevelIL instruction address corresponding to the given virtual address + + :param int addr: virtual address of the function to be queried + :param Architecture arch: (optional) Architecture for the given function + :rtype: int + :Example: + + >>> func = bv.functions[0] + >>> func.get_low_level_il_at(func.start) + 0L + """ + if arch is None: + arch = self.arch + return core.BNGetLowLevelILForInstruction(self.handle, arch.handle, addr) + + def get_low_level_il_exits_at(self, addr, arch=None): + if arch is None: + arch = self.arch + count = ctypes.c_ulonglong() + exits = core.BNGetLowLevelILExitsForInstruction(self.handle, arch.handle, addr, count) + result = [] + for i in xrange(0, count.value): + result.append(exits[i]) + core.BNFreeLowLevelILInstructionList(exits) + return result + + def get_reg_value_at(self, addr, reg, arch=None): + """ + ``get_reg_value_at`` gets the value the provided string register address corresponding to the given virtual address + + :param int addr: virtual address of the instruction to query + :param str reg: string value of native register to query + :param Architecture arch: (optional) Architecture for the given function + :rtype: function.RegisterValue + :Example: + + >>> func.get_reg_value_at(0x400dbe, 'rdi') + <const 0x2> + """ + if arch is None: + arch = self.arch + if isinstance(reg, str): + reg = arch.regs[reg].index + value = core.BNGetRegisterValueAtInstruction(self.handle, arch.handle, addr, reg) + result = RegisterValue(arch, value) + core.BNFreeRegisterValue(value) + return result + + def get_reg_value_after(self, addr, reg, arch=None): + """ + ``get_reg_value_after`` gets the value instruction address corresponding to the given virtual address + + :param int addr: virtual address of the instruction to query + :param str reg: string value of native register to query + :param Architecture arch: (optional) Architecture for the given function + :rtype: function.RegisterValue + :Example: + + >>> func.get_reg_value_after(0x400dbe, 'rdi') + <undetermined> + """ + if arch is None: + arch = self.arch + if isinstance(reg, str): + reg = arch.regs[reg].index + value = core.BNGetRegisterValueAfterInstruction(self.handle, arch.handle, addr, reg) + result = RegisterValue(arch, value) + core.BNFreeRegisterValue(value) + return result + + def get_reg_value_at_low_level_il_instruction(self, i, reg, arch=None): + """ + ``get_reg_value_at_low_level_il_instruction`` returns the value of the specified register ``reg`` at the il address + i + + :param int i: il address of instruction to query + :param Architecture arch: (optional) Architecture for the given function + :rtype: function.RegisterValue + :Example: + + >>> func.get_reg_value_at_low_level_il_instruction(15, 'rdi') + <const 0x2> + """ + if arch is None: + arch = self.arch + if isinstance(reg, str): + reg = self.arch.regs[reg].index + value = core.BNGetRegisterValueAtLowLevelILInstruction(self.handle, i, reg) + result = RegisterValue(arch, value) + core.BNFreeRegisterValue(value) + return result + + def get_reg_value_after_low_level_il_instruction(self, i, reg): + if isinstance(reg, str): + reg = self.arch.regs[reg].index + value = core.BNGetRegisterValueAfterLowLevelILInstruction(self.handle, i, reg) + result = RegisterValue(self.arch, value) + core.BNFreeRegisterValue(value) + return result + + def get_stack_contents_at(self, addr, offset, size, arch=None): + """ + ``get_stack_contents_at`` returns the RegisterValue for the item on the stack in the current function at the + given virtual address ``addr``, stack offset ``offset`` and size of ``size``. Optionally specifying the architecture. + + :param int addr: virtual address of the instruction to query + :param int offset: stack offset base of stack + :param int size: size of memory to query + :param Architecture arch: (optional) Architecture for the given function + :rtype: function.RegisterValue + + .. note:: Stack base is zero on entry into the function unless the architecture places the return address on the + stack as in (x86/x86_64) where the stack base will start at address_size + + :Example: + + >>> func.get_stack_contents_at(0x400fad, -16, 4) + <range: 0x8 to 0xffffffff> + """ + if arch is None: + arch = self.arch + value = core.BNGetStackContentsAtInstruction(self.handle, arch.handle, addr, offset, size) + result = RegisterValue(arch, value) + core.BNFreeRegisterValue(value) + return result + + def get_stack_contents_after(self, addr, offset, size, arch=None): + if arch is None: + arch = self.arch + value = core.BNGetStackContentsAfterInstruction(self.handle, arch.handle, addr, offset, size) + result = RegisterValue(arch, value) + core.BNFreeRegisterValue(value) + return result + + def get_stack_contents_at_low_level_il_instruction(self, i, offset, size): + value = core.BNGetStackContentsAtLowLevelILInstruction(self.handle, i, offset, size) + result = RegisterValue(self.arch, value) + core.BNFreeRegisterValue(value) + return result + + def get_stack_contents_after_low_level_il_instruction(self, i, offset, size): + value = core.BNGetStackContentsAfterInstruction(self.handle, i, offset, size) + result = RegisterValue(self.arch, value) + core.BNFreeRegisterValue(value) + return result + + def get_parameter_at(self, addr, func_type, i, arch=None): + if arch is None: + arch = self.arch + if func_type is not None: + func_type = func_type.handle + value = core.BNGetParameterValueAtInstruction(self.handle, arch.handle, addr, func_type, i) + result = RegisterValue(arch, value) + core.BNFreeRegisterValue(value) + return result + + def get_parameter_at_low_level_il_instruction(self, instr, func_type, i): + if func_type is not None: + func_type = func_type.handle + value = core.BNGetParameterValueAtLowLevelILInstruction(self.handle, instr, func_type, i) + result = RegisterValue(self.arch, value) + core.BNFreeRegisterValue(value) + return result + + def get_regs_read_by(self, addr, arch=None): + if arch is None: + arch = self.arch + count = ctypes.c_ulonglong() + regs = core.BNGetRegistersReadByInstruction(self.handle, arch.handle, addr, count) + result = [] + for i in xrange(0, count.value): + result.append(arch.get_reg_name(regs[i])) + core.BNFreeRegisterList(regs) + return result + + def get_regs_written_by(self, addr, arch=None): + if arch is None: + arch = self.arch + count = ctypes.c_ulonglong() + regs = core.BNGetRegistersWrittenByInstruction(self.handle, arch.handle, addr, count) + result = [] + for i in xrange(0, count.value): + result.append(arch.get_reg_name(regs[i])) + core.BNFreeRegisterList(regs) + return result + + def get_stack_vars_referenced_by(self, addr, arch=None): + if arch is None: + arch = self.arch + count = ctypes.c_ulonglong() + refs = core.BNGetStackVariablesReferencedByInstruction(self.handle, arch.handle, addr, count) + result = [] + for i in xrange(0, count.value): + result.append(StackVariableReference(refs[i].sourceOperand, types.Type(core.BNNewTypeReference(refs[i].type)), + refs[i].name, refs[i].startingOffset, refs[i].referencedOffset)) + core.BNFreeStackVariableReferenceList(refs, count.value) + return result + + def get_constants_referenced_by(self, addr, arch=None): + if arch is None: + arch = self.arch + count = ctypes.c_ulonglong() + refs = core.BNGetConstantsReferencedByInstruction(self.handle, arch.handle, addr, count) + result = [] + for i in xrange(0, count.value): + result.append(ConstantReference(refs[i].value, refs[i].size)) + core.BNFreeConstantReferenceList(refs) + return result + + def get_lifted_il_at(self, addr, arch=None): + if arch is None: + arch = self.arch + return core.BNGetLiftedILForInstruction(self.handle, arch.handle, addr) + + def get_lifted_il_flag_uses_for_definition(self, i, flag): + if isinstance(flag, str): + flag = self.arch._flags[flag] + count = ctypes.c_ulonglong() + instrs = core.BNGetLiftedILFlagUsesForDefinition(self.handle, i, flag, count) + result = [] + for i in xrange(0, count.value): + result.append(instrs[i]) + core.BNFreeLowLevelILInstructionList(instrs) + return result + + def get_lifted_il_flag_definitions_for_use(self, i, flag): + if isinstance(flag, str): + flag = self.arch._flags[flag] + count = ctypes.c_ulonglong() + instrs = core.BNGetLiftedILFlagDefinitionsForUse(self.handle, i, flag, count) + result = [] + for i in xrange(0, count.value): + result.append(instrs[i]) + core.BNFreeLowLevelILInstructionList(instrs) + return result + + def get_flags_read_by_lifted_il_instruction(self, i): + count = ctypes.c_ulonglong() + flags = core.BNGetFlagsReadByLiftedILInstruction(self.handle, i, count) + result = [] + for i in xrange(0, count.value): + result.append(self.arch._flags_by_index[flags[i]]) + core.BNFreeRegisterList(flags) + return result + + def get_flags_written_by_lifted_il_instruction(self, i): + count = ctypes.c_ulonglong() + flags = core.BNGetFlagsWrittenByLiftedILInstruction(self.handle, i, count) + result = [] + for i in xrange(0, count.value): + result.append(self.arch._flags_by_index[flags[i]]) + core.BNFreeRegisterList(flags) + return result + + def create_graph(self): + return FunctionGraph(self._view, core.BNCreateFunctionGraph(self.handle)) + + def apply_imported_types(self, sym): + core.BNApplyImportedTypes(self.handle, sym.handle) + + def apply_auto_discovered_type(self, func_type): + core.BNApplyAutoDiscoveredFunctionType(self.handle, func_type.handle) + + def set_auto_indirect_branches(self, source, branches, source_arch=None): + if source_arch is None: + source_arch = self.arch + branch_list = (core.BNArchitectureAndAddress * len(branches))() + for i in xrange(len(branches)): + branch_list[i].arch = branches[i][0].handle + branch_list[i].address = branches[i][1] + core.BNSetAutoIndirectBranches(self.handle, source_arch.handle, source, branch_list, len(branches)) + + def set_user_indirect_branches(self, source, branches, source_arch=None): + if source_arch is None: + source_arch = self.arch + branch_list = (core.BNArchitectureAndAddress * len(branches))() + for i in xrange(len(branches)): + branch_list[i].arch = branches[i][0].handle + branch_list[i].address = branches[i][1] + core.BNSetUserIndirectBranches(self.handle, source_arch.handle, source, branch_list, len(branches)) + + def get_indirect_branches_at(self, addr, arch=None): + if arch is None: + arch = self.arch + count = ctypes.c_ulonglong() + branches = core.BNGetIndirectBranchesAt(self.handle, arch.handle, addr, count) + result = [] + for i in xrange(0, count.value): + result.append(IndirectBranchInfo(architecture.Architecture(branches[i].sourceArch), branches[i].sourceAddr, architecture.Architecture(branches[i].destArch), branches[i].destAddr, branches[i].autoDefined)) + core.BNFreeIndirectBranchList(branches) + return result + + def get_block_annotations(self, addr, arch=None): + if arch is None: + arch = self.arch + count = ctypes.c_ulonglong(0) + lines = core.BNGetFunctionBlockAnnotations(self.handle, arch.handle, addr, count) + result = [] + for i in xrange(0, count.value): + tokens = [] + for j in xrange(0, lines[i].count): + token_type = InstructionTextTokenType(lines[i].tokens[j].type) + text = lines[i].tokens[j].text + value = lines[i].tokens[j].value + size = lines[i].tokens[j].size + operand = lines[i].tokens[j].operand + tokens.append(InstructionTextToken(token_type, text, value, size, operand)) + result.append(tokens) + core.BNFreeInstructionTextLines(lines, count.value) + return result + + def set_auto_type(self, value): + core.BNSetFunctionAutoType(self.handle, value.handle) + + def set_user_type(self, value): + core.BNSetFunctionUserType(self.handle, value.handle) + + def get_int_display_type(self, instr_addr, value, operand, arch=None): + if arch is None: + arch = self.arch + return IntegerDisplayType(core.BNGetIntegerConstantDisplayType(self.handle, arch.handle, instr_addr, value, operand)) + + def set_int_display_type(self, instr_addr, value, operand, display_type, arch=None): + """ + + :param int instr_addr: + :param int value: + :param int operand: + :param IntegerDisplayTypeEnum display_type: + :param Architecture arch: (optional) + """ + if arch is None: + arch = self.arch + if isinstance(display_type, str): + display_type = IntegerDisplayType[display_type] + core.BNSetIntegerConstantDisplayType(self.handle, arch.handle, instr_addr, value, operand, display_type) + + def reanalyze(self): + """ + ``reanalyze`` causes this functions to be reanalyzed. This function does not wait for the analysis to finish. + + :rtype: None + """ + core.BNReanalyzeFunction(self.handle) + + def request_advanced_analysis_data(self): + core.BNRequestAdvancedFunctionAnalysisData(self.handle) + self._advanced_analysis_requests += 1 + + def release_advanced_analysis_data(self): + core.BNReleaseAdvancedFunctionAnalysisData(self.handle) + self._advanced_analysis_requests -= 1 + + def get_basic_block_at(self, addr, arch=None): + """ + ``get_basic_block_at`` returns the BasicBlock of the optionally specified Architecture ``arch`` at the given + address ``addr``. + + :param int addr: Address of the BasicBlock to retrieve. + :param Architecture arch: (optional) Architecture of the basic block if different from the Function's self.arch + :Example: + >>> current_function.get_basic_block_at(current_function.start) + <block: x86_64@0x100000f30-0x100000f50> + """ + if arch is None: + arch = self.arch + block = core.BNGetFunctionBasicBlockAtAddress(self.handle, arch.handle, addr) + if not block: + return None + return basicblock.BasicBlock(self._view, handle = block) + + def get_instr_highlight(self, addr, arch=None): + """ + :Example: + >>> current_function.set_user_instr_highlight(here, highlight.HighlightColor(red=0xff, blue=0xff, green=0)) + >>> current_function.get_instr_highlight(here) + <color: #ff00ff> + """ + if arch is None: + arch = self.arch + color = core.BNGetInstructionHighlight(self.handle, arch.handle, addr) + if color.style == HighlightColorStyle.StandardHighlightColor: + return highlight.HighlightColor(color = color.color, alpha = color.alpha) + elif color.style == HighlightColorStyle.MixedHighlightColor: + return highlight.HighlightColor(color = color.color, mix_color = color.mixColor, mix = color.mix, alpha = color.alpha) + elif color.style == HighlightColorStyle.CustomHighlightColor: + return highlight.HighlightColor(red = color.r, green = color.g, blue = color.b, alpha = color.alpha) + return highlight.HighlightColor(color = HighlightStandardColor.NoHighlightColor) + + def set_auto_instr_highlight(self, addr, color, arch=None): + """ + ``set_auto_instr_highlight`` highlights the instruction at the specified address with the supplied color + + .warning:: Use only in analysis plugins. Do not use in regular plugins, as colors won't be saved to the database. + + :param int addr: virtual address of the instruction to be highlighted + :param HighlightStandardColor or highlight.HighlightColor color: Color value to use for highlighting + :param Architecture arch: (optional) Architecture of the instruction if different from self.arch + """ + if arch is None: + arch = self.arch + if not isinstance(color, highlight.HighlightColor): + color = highlight.HighlightColor(color = color) + core.BNSetAutoInstructionHighlight(self.handle, arch.handle, addr, color._get_core_struct()) + + def set_user_instr_highlight(self, addr, color, arch=None): + """ + ``set_user_instr_highlight`` highlights the instruction at the specified address with the supplied color + + :param int addr: virtual address of the instruction to be highlighted + :param HighlightStandardColor or highlight.HighlightColor color: Color value to use for highlighting + :param Architecture arch: (optional) Architecture of the instruction if different from self.arch + :Example: + + >>> current_function.set_user_instr_highlight(here, HighlightStandardColor.BlueHighlightColor) + >>> current_function.set_user_instr_highlight(here, highlight.HighlightColor(red=0xff, blue=0xff, green=0)) + """ + if arch is None: + arch = self.arch + if not isinstance(color, HighlightStandardColor) and not isinstance(color, highlight.HighlightColor): + raise ValueError("Specified color is not one of HighlightStandardColor, highlight.HighlightColor") + core.BNSetUserInstructionHighlight(self.handle, arch.handle, addr, color._get_core_struct()) + + +class AdvancedFunctionAnalysisDataRequestor(object): + def __init__(self, func = None): + self._function = func + if self._function is not None: + self._function.request_advanced_analysis_data() + + def __del__(self): + if self._function is not None: + self._function.release_advanced_analysis_data() + + @property + def function(self): + return self._function + + @function.setter + def function(self, func): + if self._function is not None: + self._function.release_advanced_analysis_data() + self._function = func + if self._function is not None: + self._function.request_advanced_analysis_data() + + def close(self): + if self._function is not None: + self._function.release_advanced_analysis_data() + self._function = None + + +class DisassemblyTextLine(object): + def __init__(self, addr, tokens): + self.address = addr + self.tokens = tokens + + def __str__(self): + result = "" + for token in self.tokens: + result += token.text + return result + + def __repr__(self): + return "<%#x: %s>" % (self.address, str(self)) + + +class FunctionGraphEdge(object): + def __init__(self, branch_type, arch, target, points): + self.type = BranchType(branch_type) + self.arch = arch + self.target = target + self.points = points + + def __repr__(self): + if self.arch: + return "<%s: %s@%#x>" % (self.type.name, self.arch.name, self.target) + return "<%s: %#x>" % (self.type, self.target) + + +class FunctionGraphBlock(object): + def __init__(self, handle): + self.handle = handle + + def __del__(self): + core.BNFreeFunctionGraphBlock(self.handle) + + @property + def basic_block(self): + """Basic block associated with this part of the function graph (read-only)""" + block = core.BNGetFunctionGraphBasicBlock(self.handle) + func = core.BNGetBasicBlockFunction(block) + if func is None: + core.BNFreeBasicBlock(block) + block = None + else: + block = basicblock.BasicBlock(binaryview.BinaryView(handle = core.BNGetFunctionData(func)), block) + core.BNFreeFunction(func) + return block + + @property + def arch(self): + """Function graph block architecture (read-only)""" + arch = core.BNGetFunctionGraphBlockArchitecture(self.handle) + if arch is None: + return None + return architecture.Architecture(arch) + + @property + def start(self): + """Function graph block start (read-only)""" + return core.BNGetFunctionGraphBlockStart(self.handle) + + @property + def end(self): + """Function graph block end (read-only)""" + return core.BNGetFunctionGraphBlockEnd(self.handle) + + @property + def x(self): + """Function graph block X (read-only)""" + return core.BNGetFunctionGraphBlockX(self.handle) + + @property + def y(self): + """Function graph block Y (read-only)""" + return core.BNGetFunctionGraphBlockY(self.handle) + + @property + def width(self): + """Function graph block width (read-only)""" + return core.BNGetFunctionGraphBlockWidth(self.handle) + + @property + def height(self): + """Function graph block height (read-only)""" + return core.BNGetFunctionGraphBlockHeight(self.handle) + + @property + def lines(self): + """Function graph block list of lines (read-only)""" + count = ctypes.c_ulonglong() + lines = core.BNGetFunctionGraphBlockLines(self.handle, count) + result = [] + for i in xrange(0, count.value): + addr = lines[i].addr + tokens = [] + for j in xrange(0, lines[i].count): + token_type = InstructionTextTokenType(lines[i].tokens[j].type) + text = lines[i].tokens[j].text + value = lines[i].tokens[j].value + size = lines[i].tokens[j].size + operand = lines[i].tokens[j].operand + tokens.append(InstructionTextToken(token_type, text, value, size, operand)) + result.append(DisassemblyTextLine(addr, tokens)) + core.BNFreeDisassemblyTextLines(lines, count.value) + return result + + @property + def outgoing_edges(self): + """Function graph block list of outgoing edges (read-only)""" + count = ctypes.c_ulonglong() + edges = core.BNGetFunctionGraphBlockOutgoingEdges(self.handle, count) + result = [] + for i in xrange(0, count.value): + branch_type = BranchType(edges[i].type) + target = edges[i].target + arch = None + if edges[i].arch is not None: + arch = architecture.Architecture(edges[i].arch) + points = [] + for j in xrange(0, edges[i].pointCount): + points.append((edges[i].points[j].x, edges[i].points[j].y)) + result.append(FunctionGraphEdge(branch_type, arch, target, points)) + core.BNFreeFunctionGraphBlockOutgoingEdgeList(edges, count.value) + return result + + def __setattr__(self, name, value): + try: + object.__setattr__(self, name, value) + except AttributeError: + raise AttributeError("attribute '%s' is read only" % name) + + def __repr__(self): + arch = self.arch + if arch: + return "<graph block: %s@%#x-%#x>" % (arch.name, self.start, self.end) + else: + return "<graph block: %#x-%#x>" % (self.start, self.end) + + def __iter__(self): + count = ctypes.c_ulonglong() + lines = core.BNGetFunctionGraphBlockLines(self.handle, count) + try: + for i in xrange(0, count.value): + addr = lines[i].addr + tokens = [] + for j in xrange(0, lines[i].count): + token_type = InstructionTextTokenType(lines[i].tokens[j].type) + text = lines[i].tokens[j].text + value = lines[i].tokens[j].value + size = lines[i].tokens[j].size + operand = lines[i].tokens[j].operand + tokens.append(InstructionTextToken(token_type, text, value, size, operand)) + yield DisassemblyTextLine(addr, tokens) + finally: + core.BNFreeDisassemblyTextLines(lines, count.value) + + +class DisassemblySettings(object): + def __init__(self, handle = None): + if handle is None: + self.handle = core.BNCreateDisassemblySettings() + else: + self.handle = handle + + def __del__(self): + core.BNFreeDisassemblySettings(self.handle) + + @property + def width(self): + return core.BNGetDisassemblyWidth(self.handle) + + @width.setter + def width(self, value): + core.BNSetDisassemblyWidth(self.handle, value) + + @property + def max_symbol_width(self): + return core.BNGetDisassemblyMaximumSymbolWidth(self.handle) + + @max_symbol_width.setter + def max_symbol_width(self, value): + core.BNSetDisassemblyMaximumSymbolWidth(self.handle, value) + + def is_option_set(self, option): + if isinstance(option, str): + option = DisassemblyOption[option] + return core.BNIsDisassemblySettingsOptionSet(self.handle, option) + + def set_option(self, option, state = True): + if isinstance(option, str): + option = DisassemblyOption[option] + core.BNSetDisassemblySettingsOption(self.handle, option, state) + + +class FunctionGraph(object): + def __init__(self, view, handle): + self.view = view + self.handle = handle + self._on_complete = None + self._cb = ctypes.CFUNCTYPE(None, ctypes.c_void_p)(self._complete) + + def __del__(self): + self.abort() + core.BNFreeFunctionGraph(self.handle) + + @property + def function(self): + """Function for a function graph (read-only)""" + func = core.BNGetFunctionForFunctionGraph(self.handle) + if func is None: + return None + return Function(self.view, func) + + @property + def complete(self): + """Whether function graph layout is complete (read-only)""" + return core.BNIsFunctionGraphLayoutComplete(self.handle) + + @property + def type(self): + """Function graph type (read-only)""" + return FunctionGraphType(core.BNGetFunctionGraphType(self.handle)) + + @property + def blocks(self): + """List of basic blocks in function (read-only)""" + count = ctypes.c_ulonglong() + blocks = core.BNGetFunctionGraphBlocks(self.handle, count) + result = [] + for i in xrange(0, count.value): + result.append(FunctionGraphBlock(core.BNNewFunctionGraphBlockReference(blocks[i]))) + core.BNFreeFunctionGraphBlockList(blocks, count.value) + return result + + @property + def width(self): + """Function graph width (read-only)""" + return core.BNGetFunctionGraphWidth(self.handle) + + @property + def height(self): + """Function graph height (read-only)""" + return core.BNGetFunctionGraphHeight(self.handle) + + @property + def horizontal_block_margin(self): + return core.BNGetHorizontalFunctionGraphBlockMargin(self.handle) + + @horizontal_block_margin.setter + def horizontal_block_margin(self, value): + core.BNSetFunctionGraphBlockMargins(self.handle, value, self.vertical_block_margin) + + @property + def vertical_block_margin(self): + return core.BNGetVerticalFunctionGraphBlockMargin(self.handle) + + @vertical_block_margin.setter + def vertical_block_margin(self, value): + core.BNSetFunctionGraphBlockMargins(self.handle, self.horizontal_block_margin, value) + + @property + def settings(self): + return DisassemblySettings(core.BNGetFunctionGraphSettings(self.handle)) + + def __setattr__(self, name, value): + try: + object.__setattr__(self, name, value) + except AttributeError: + raise AttributeError("attribute '%s' is read only" % name) + + def __repr__(self): + return "<graph of %s>" % repr(self.function) + + def __iter__(self): + count = ctypes.c_ulonglong() + blocks = core.BNGetFunctionGraphBlocks(self.handle, count) + try: + for i in xrange(0, count.value): + yield FunctionGraphBlock(core.BNNewFunctionGraphBlockReference(blocks[i])) + finally: + core.BNFreeFunctionGraphBlockList(blocks, count.value) + + def _complete(self, ctxt): + try: + if self._on_complete is not None: + self._on_complete() + except: + log.log_error(traceback.format_exc()) + + def layout(self, graph_type = FunctionGraphType.NormalFunctionGraph): + if isinstance(graph_type, str): + graph_type = FunctionGraphType[graph_type] + core.BNStartFunctionGraphLayout(self.handle, graph_type) + + def _wait_complete(self): + self._wait_cond.acquire() + self._wait_cond.notify() + self._wait_cond.release() + + def layout_and_wait(self, graph_type=FunctionGraphType.NormalFunctionGraph): + self._wait_cond = threading.Condition() + self.on_complete(self._wait_complete) + self.layout(graph_type) + + self._wait_cond.acquire() + while not self.complete: + self._wait_cond.wait() + self._wait_cond.release() + + def on_complete(self, callback): + self._on_complete = callback + core.BNSetFunctionGraphCompleteCallback(self.handle, None, self._cb) + + def abort(self): + core.BNAbortFunctionGraph(self.handle) + + def get_blocks_in_region(self, left, top, right, bottom): + count = ctypes.c_ulonglong() + blocks = core.BNGetFunctionGraphBlocksInRegion(self.handle, left, top, right, bottom, count) + result = [] + for i in xrange(0, count.value): + result.append(FunctionGraphBlock(core.BNNewFunctionGraphBlockReference(blocks[i]))) + core.BNFreeFunctionGraphBlockList(blocks, count.value) + return result + + def is_option_set(self, option): + if isinstance(option, str): + option = DisassemblyOption[option] + return core.BNIsFunctionGraphOptionSet(self.handle, option) + + def set_option(self, option, state = True): + if isinstance(option, str): + option = DisassemblyOption[option] + core.BNSetFunctionGraphOption(self.handle, option, state) + + +class RegisterInfo(object): + def __init__(self, full_width_reg, size, offset=0, extend=ImplicitRegisterExtend.NoExtend, index=None): + self.full_width_reg = full_width_reg + self.offset = offset + self.size = size + self.extend = extend + self.index = index + + def __repr__(self): + if self.extend == ImplicitRegisterExtend.ZeroExtendToFullWidth: + extend = ", zero extend" + elif self.extend == ImplicitRegisterExtend.SignExtendToFullWidth: + extend = ", sign extend" + else: + extend = "" + return "<reg: size %d, offset %d in %s%s>" % (self.size, self.offset, self.full_width_reg, extend) + + +class InstructionBranch(object): + def __init__(self, branch_type, target = 0, arch = None): + self.type = branch_type + self.target = target + self.arch = arch + + def __repr__(self): + branch_type = self.type + if self.arch is not None: + return "<%s: %s@%#x>" % (branch_type.name, self.arch.name, self.target) + return "<%s: %#x>" % (branch_type, self.target) + + +class InstructionInfo(object): + def __init__(self): + self.length = 0 + self.branch_delay = False + self.branches = [] + + def add_branch(self, branch_type, target = 0, arch = None): + self.branches.append(InstructionBranch(branch_type, target, arch)) + + def __repr__(self): + branch_delay = "" + if self.branch_delay: + branch_delay = ", delay slot" + return "<instr: %d bytes%s, %s>" % (self.length, branch_delay, repr(self.branches)) + + +class InstructionTextToken(object): + """ + ``class InstructionTextToken`` is used to tell the core about the various components in the disassembly views. + + ========================== ============================================ + InstructionTextTokenType Description + ========================== ============================================ + TextToken Text that doesn't fit into the other tokens + InstructionToken The instruction mnemonic + OperandSeparatorToken The comma or whatever else separates tokens + RegisterToken Registers + IntegerToken Integers + PossibleAddressToken Integers that are likely addresses + BeginMemoryOperandToken The start of memory operand + EndMemoryOperandToken The end of a memory operand + FloatingPointToken Floating point number + AnnotationToken **For internal use only** + CodeRelativeAddressToken **For internal use only** + StackVariableTypeToken **For internal use only** + DataVariableTypeToken **For internal use only** + FunctionReturnTypeToken **For internal use only** + FunctionAttributeToken **For internal use only** + ArgumentTypeToken **For internal use only** + ArgumentNameToken **For internal use only** + HexDumpByteValueToken **For internal use only** + HexDumpSkippedByteToken **For internal use only** + HexDumpInvalidByteToken **For internal use only** + HexDumpTextToken **For internal use only** + OpcodeToken **For internal use only** + StringToken **For internal use only** + CharacterConstantToken **For internal use only** + CodeSymbolToken **For internal use only** + DataSymbolToken **For internal use only** + StackVariableToken **For internal use only** + ImportToken **For internal use only** + AddressDisplayToken **For internal use only** + ========================== ============================================ + + """ + def __init__(self, token_type, text, value = 0, size = 0, operand = 0xffffffff): + self.type = InstructionTextTokenType(token_type) + self.text = text + self.value = value + self.size = size + self.operand = operand + + def __str__(self): + return self.text + + def __repr__(self): + return repr(self.text) diff --git a/python/functionrecognizer.py b/python/functionrecognizer.py new file mode 100644 index 00000000..960aee2f --- /dev/null +++ b/python/functionrecognizer.py @@ -0,0 +1,64 @@ +# 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 traceback + +# Binary Ninja components +import _binaryninjacore as core +import function +import filemetadata +import binaryview +import lowlevelil +import log + + +class FunctionRecognizer(object): + _instance = None + + def __init__(self): + self._cb = core.BNFunctionRecognizer() + self._cb.context = 0 + self._cb.recognizeLowLevelIL = self._cb.recognizeLowLevelIL.__class__(self._recognize_low_level_il) + + @classmethod + def register_global(cls): + if cls._instance is None: + cls._instance = cls() + core.BNRegisterGlobalFunctionRecognizer(cls._instance._cb) + + @classmethod + def register_arch(cls, arch): + if cls._instance is None: + cls._instance = cls() + core.BNRegisterArchitectureFunctionRecognizer(arch.handle, cls._instance._cb) + + def _recognize_low_level_il(self, ctxt, data, func, il): + try: + file_metadata = filemetadata.FileMetadata(handle = core.BNGetFileForView(data)) + view = binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(data)) + func = function.Function(view, handle = core.BNNewFunctionReference(func)) + il = lowlevelil.LowLevelILFunction(func.arch, handle = core.BNNewLowLevelILFunctionReference(il)) + return self.recognize_low_level_il(view, func, il) + except: + log.log_error(traceback.format_exc()) + return False + + def recognize_low_level_il(self, data, func, il): + return False diff --git a/python/generator.cpp b/python/generator.cpp index 485655c2..1c0e7b16 100644 --- a/python/generator.cpp +++ b/python/generator.cpp @@ -98,7 +98,17 @@ void OutputType(FILE* out, Type* type, bool isReturnType = false, bool isCallbac fprintf(out, "ctypes.c_double"); break; case NamedTypeReferenceClass: - fprintf(out, "%s", type->GetNamedTypeReference()->GetName().GetString().c_str()); + if (type->GetNamedTypeReference()->GetTypeClass() == EnumNamedTypeClass) + { + string name = type->GetNamedTypeReference()->GetName().GetString(); + if (name.size() > 2 && name.substr(0, 2) == "BN") + name = name.substr(2); + fprintf(out, "%sEnum", name.c_str()); + } + else + { + fprintf(out, "%s", type->GetNamedTypeReference()->GetName().GetString().c_str()); + } break; case PointerTypeClass: if (isCallback || (type->GetChildType()->GetClass() == VoidTypeClass)) @@ -144,9 +154,9 @@ void OutputType(FILE* out, Type* type, bool isReturnType = false, bool isCallbac int main(int argc, char* argv[]) { - if (argc < 3) + if (argc < 4) { - fprintf(stderr, "Usage: generator <header> <output>\n"); + fprintf(stderr, "Usage: generator <header> <output> <output_enum>\n"); return 1; } @@ -161,27 +171,30 @@ int main(int argc, char* argv[]) return 1; FILE* out = fopen(argv[2], "w"); + FILE* enums = fopen(argv[3], "w"); + fprintf(out, "from __future__ import absolute_import\n"); fprintf(out, "import ctypes, os\n\n"); + fprintf(enums, "import enum"); fprintf(out, "# Load core module\n"); -#if defined(__APPLE__) - fprintf(out, "_base_path = os.path.join(os.path.dirname(__file__), \"..\", \"..\", \"..\", \"MacOS\")\n"); -#else - fprintf(out, "_base_path = os.path.join(os.path.dirname(__file__), \"..\", \"..\")\n"); -#endif - -#ifdef WIN32 - fprintf(out, "core = ctypes.CDLL(os.path.join(_base_path, \"binaryninjacore.dll\"))\n\n"); -#elif defined(__APPLE__) - fprintf(out, "core = ctypes.CDLL(os.path.join(_base_path, \"libbinaryninjacore.dylib\"))\n\n"); -#else - fprintf(out, "core = ctypes.CDLL(os.path.join(_base_path, \"libbinaryninjacore.so.1\"))\n\n"); -#endif + fprintf(out, "import platform\n"); + fprintf(out, "core = None\n"); + fprintf(out, "_base_path = None\n"); + fprintf(out, "if platform.system() == \"Darwin\":\n"); + fprintf(out, "\t_base_path = os.path.join(os.path.dirname(__file__), \"..\", \"..\", \"..\", \"MacOS\")\n"); + fprintf(out, "\tcore = ctypes.CDLL(os.path.join(_base_path, \"libbinaryninjacore.dylib\"))\n\n"); + fprintf(out, "elif platform.system() == \"Linux\":\n"); + fprintf(out, "\t_base_path = os.path.join(os.path.dirname(__file__), \"..\", \"..\")\n"); + fprintf(out, "\tcore = ctypes.CDLL(os.path.join(_base_path, \"libbinaryninjacore.so.1\"))\n\n"); + fprintf(out, "elif platform.system() == \"Windows\":\n"); + fprintf(out, "\t_base_path = os.path.join(os.path.dirname(__file__), \"..\", \"..\")\n"); + fprintf(out, "\tcore = ctypes.CDLL(os.path.join(_base_path, \"binaryninjacore.dll\"))\n"); + fprintf(out, "else:\n"); + fprintf(out, "\traise Exception(\"OS not supported\")\n\n"); // Create type objects fprintf(out, "# Type definitions\n"); - map<string, int64_t> enumMembers; for (auto& i : types) { string name; @@ -191,23 +204,20 @@ int main(int argc, char* argv[]) if (i.second->GetClass() == StructureTypeClass) { fprintf(out, "class %s(ctypes.Structure):\n", name.c_str()); - fprintf(out, " pass\n"); + fprintf(out, "\tpass\n"); } else if (i.second->GetClass() == EnumerationTypeClass) { - fprintf(out, "%s = ctypes.c_int\n", name.c_str()); - for (auto& j : i.second->GetEnumeration()->GetMembers()) - fprintf(out, "%s = %" PRId64 "\n", j.name.c_str(), j.value); - fprintf(out, "%s_names = {\n", name.c_str()); - for (auto& j : i.second->GetEnumeration()->GetMembers()) - fprintf(out, " %" PRId64 ": \"%s\",\n", j.value, j.name.c_str()); - fprintf(out, "}\n"); - fprintf(out, "%s_by_name = {\n", name.c_str()); - for (auto& j : i.second->GetEnumeration()->GetMembers()) - fprintf(out, " \"%s\": %" PRId64 ",\n", j.name.c_str(), j.value); - fprintf(out, "}\n"); + if (name.size() > 2 && name.substr(0, 2) == "BN") + name = name.substr(2); + + fprintf(out, "%sEnum = ctypes.c_int\n", name.c_str()); + + fprintf(enums, "\n\nclass %s(enum.IntEnum):\n", name.c_str()); for (auto& j : i.second->GetEnumeration()->GetMembers()) - enumMembers[j.name] = j.value; + { + fprintf(enums, "\t%s = %" PRId64 "\n", j.name.c_str(), j.value); + } } else if ((i.second->GetClass() == BoolTypeClass) || (i.second->GetClass() == IntegerTypeClass) || (i.second->GetClass() == FloatTypeClass) || (i.second->GetClass() == ArrayTypeClass)) @@ -218,10 +228,6 @@ int main(int argc, char* argv[]) } } - fprintf(out, "all_enum_values = {\n"); - for (auto& i : enumMembers) - fprintf(out, " \"%s\": %" PRId64 ",\n", i.first.c_str(), i.second); - fprintf(out, "}\n"); fprintf(out, "\n# Structure definitions\n"); for (auto& i : types) @@ -235,11 +241,11 @@ int main(int argc, char* argv[]) fprintf(out, "%s._fields_ = [\n", name.c_str()); for (auto& j : i.second->GetStructure()->GetMembers()) { - fprintf(out, " (\"%s\", ", j.name.c_str()); + fprintf(out, "\t\t(\"%s\", ", j.name.c_str()); OutputType(out, j.type); fprintf(out, "),\n"); } - fprintf(out, " ]\n"); + fprintf(out, "\t]\n"); } } @@ -281,7 +287,7 @@ int main(int argc, char* argv[]) fprintf(out, "%s.argtypes = [\n", funcName.c_str()); for (auto& j : i.second->GetParameters()) { - fprintf(out, " "); + fprintf(out, "\t\t"); if (name == "BNFreeString") { // BNFreeString expects a pointer to a string allocated by the core, so do not use @@ -295,38 +301,39 @@ int main(int argc, char* argv[]) } fprintf(out, ",\n"); } - fprintf(out, " ]\n"); + fprintf(out, "\t]\n"); } if (stringResult) { // Emit wrapper to get Python string and free native memory fprintf(out, "def %s(*args):\n", name.c_str()); - fprintf(out, " result = %s(*args)\n", funcName.c_str()); - fprintf(out, " string = ctypes.cast(result, ctypes.c_char_p).value\n"); - fprintf(out, " BNFreeString(result)\n"); - fprintf(out, " return string\n"); + fprintf(out, "\tresult = %s(*args)\n", funcName.c_str()); + fprintf(out, "\tstring = ctypes.cast(result, ctypes.c_char_p).value\n"); + fprintf(out, "\tBNFreeString(result)\n"); + fprintf(out, "\treturn string\n"); } else if (pointerResult) { // Emit wrapper to return None on null pointer fprintf(out, "def %s(*args):\n", name.c_str()); - fprintf(out, " result = %s(*args)\n", funcName.c_str()); - fprintf(out, " if not result:\n"); - fprintf(out, " return None\n"); - fprintf(out, " return result\n"); + fprintf(out, "\tresult = %s(*args)\n", funcName.c_str()); + fprintf(out, "\tif not result:\n"); + fprintf(out, "\t\treturn None\n"); + fprintf(out, "\treturn result\n"); } } fprintf(out, "\n# Helper functions\n"); fprintf(out, "def handle_of_type(value, handle_type):\n"); - fprintf(out, " if isinstance(value, ctypes.POINTER(handle_type)) or isinstance(value, ctypes.c_void_p):\n"); - fprintf(out, " return ctypes.cast(value, ctypes.POINTER(handle_type))\n"); - fprintf(out, " raise ValueError, 'expected pointer to %%s' %% str(handle_type)\n"); + fprintf(out, "\tif isinstance(value, ctypes.POINTER(handle_type)) or isinstance(value, ctypes.c_void_p):\n"); + fprintf(out, "\t\treturn ctypes.cast(value, ctypes.POINTER(handle_type))\n"); + fprintf(out, "\traise ValueError, 'expected pointer to %%s' %% str(handle_type)\n"); fprintf(out, "\n# Set path for core plugins\n"); fprintf(out, "BNSetBundledPluginDirectory(os.path.join(_base_path, \"plugins\"))\n"); fclose(out); + fclose(enums); return 0; } diff --git a/python/highlight.py b/python/highlight.py new file mode 100644 index 00000000..6af1cf95 --- /dev/null +++ b/python/highlight.py @@ -0,0 +1,112 @@ +# 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. + + +# Binary Ninja components +import _binaryninjacore as core +from enums import HighlightColorStyle, HighlightStandardColor + + +class HighlightColor(object): + def __init__(self, color = None, mix_color = None, mix = None, red = None, green = None, blue = None, alpha = 255): + if (red is not None) and (green is not None) and (blue is not None): + self.style = HighlightColorStyle.CustomHighlightColor + self.red = red + self.green = green + self.blue = blue + elif (mix_color is not None) and (mix is not None): + self.style = HighlightColorStyle.MixedHighlightColor + if color is None: + self.color = HighlightStandardColor.NoHighlightColor + else: + self.color = color + self.mix_color = mix_color + self.mix = mix + else: + self.style = HighlightColorStyle.StandardHighlightColor + if color is None: + self.color = HighlightStandardColor.NoHighlightColor + else: + self.color = color + self.alpha = alpha + + def _standard_color_to_str(self, color): + if color == HighlightStandardColor.NoHighlightColor: + return "none" + if color == HighlightStandardColor.BlueHighlightColor: + return "blue" + if color == HighlightStandardColor.GreenHighlightColor: + return "green" + if color == HighlightStandardColor.CyanHighlightColor: + return "cyan" + if color == HighlightStandardColor.RedHighlightColor: + return "red" + if color == HighlightStandardColor.MagentaHighlightColor: + return "magenta" + if color == HighlightStandardColor.YellowHighlightColor: + return "yellow" + if color == HighlightStandardColor.OrangeHighlightColor: + return "orange" + if color == HighlightStandardColor.WhiteHighlightColor: + return "white" + if color == HighlightStandardColor.BlackHighlightColor: + return "black" + return "%d" % color + + def __repr__(self): + if self.style == HighlightColorStyle.StandardHighlightColor: + if self.alpha == 255: + return "<color: %s>" % self._standard_color_to_str(self.color) + return "<color: %s, alpha %d>" % (self._standard_color_to_str(self.color), self.alpha) + if self.style == HighlightColorStyle.MixedHighlightColor: + if self.alpha == 255: + return "<color: mix %s to %s factor %d>" % (self._standard_color_to_str(self.color), + self._standard_color_to_str(self.mix_color), self.mix) + return "<color: mix %s to %s factor %d, alpha %d>" % (self._standard_color_to_str(self.color), + self._standard_color_to_str(self.mix_color), self.mix, self.alpha) + if self.style == HighlightColorStyle.CustomHighlightColor: + if self.alpha == 255: + return "<color: #%.2x%.2x%.2x>" % (self.red, self.green, self.blue) + return "<color: #%.2x%.2x%.2x, alpha %d>" % (self.red, self.green, self.blue, self.alpha) + return "<color>" + + def _get_core_struct(self): + result = core.BNHighlightColor() + result.style = self.style + result.color = HighlightStandardColor.NoHighlightColor + result.mix_color = HighlightStandardColor.NoHighlightColor + result.mix = 0 + result.r = 0 + result.g = 0 + result.b = 0 + result.alpha = self.alpha + + if self.style == HighlightColorStyle.StandardHighlightColor: + result.color = self.color + elif self.style == HighlightColorStyle.MixedHighlightColor: + result.color = self.color + result.mixColor = self.mix_color + result.mix = self.mix + elif self.style == HighlightColorStyle.CustomHighlightColor: + result.r = self.red + result.g = self.green + result.b = self.blue + + return result diff --git a/python/interaction.py b/python/interaction.py new file mode 100644 index 00000000..6d640d17 --- /dev/null +++ b/python/interaction.py @@ -0,0 +1,521 @@ +# 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 ctypes +import traceback + +# Binary Ninja components +import _binaryninjacore as core +from enums import FormInputFieldType, MessageBoxIcon, MessageBoxButtonResult +import binaryview +import log + + +class LabelField(object): + def __init__(self, text): + self.text = text + + def _fill_core_struct(self, value): + value.type = FormInputFieldType.LabelFormField + value.prompt = self.text + + def _fill_core_result(self, value): + pass + + def _get_result(self, value): + pass + + +class SeparatorField(object): + def _fill_core_struct(self, value): + value.type = FormInputFieldType.SeparatorFormField + + def _fill_core_result(self, value): + pass + + def _get_result(self, value): + pass + + +class TextLineField(object): + def __init__(self, prompt): + self.prompt = prompt + self.result = None + + def _fill_core_struct(self, value): + value.type = FormInputFieldType.TextLineFormField + value.prompt = self.prompt + + def _fill_core_result(self, value): + value.stringResult = core.BNAllocString(str(self.result)) + + def _get_result(self, value): + self.result = value.stringResult + + +class MultilineTextField(object): + def __init__(self, prompt): + self.prompt = prompt + self.result = None + + def _fill_core_struct(self, value): + value.type = FormInputFieldType.MultilineTextFormField + value.prompt = self.prompt + + def _fill_core_result(self, value): + value.stringResult = core.BNAllocString(str(self.result)) + + def _get_result(self, value): + self.result = value.stringResult + + +class IntegerField(object): + def __init__(self, prompt): + self.prompt = prompt + self.result = None + + def _fill_core_struct(self, value): + value.type = FormInputFieldType.IntegerFormField + value.prompt = self.prompt + + def _fill_core_result(self, value): + value.intResult = self.result + + def _get_result(self, value): + self.result = value.intResult + + +class AddressField(object): + def __init__(self, prompt, view = None, current_address = 0): + self.prompt = prompt + self.view = view + self.current_address = current_address + self.result = None + + def _fill_core_struct(self, value): + value.type = FormInputFieldType.AddressFormField + value.prompt = self.prompt + value.view = None + if self.view is not None: + value.view = self.view.handle + value.currentAddress = self.current_address + + def _fill_core_result(self, value): + value.addressResult = self.result + + def _get_result(self, value): + self.result = value.addressResult + + +class ChoiceField(object): + def __init__(self, prompt, choices): + self.prompt = prompt + self.choices = choices + self.result = None + + def _fill_core_struct(self, value): + value.type = FormInputFieldType.ChoiceFormField + value.prompt = self.prompt + choice_buf = (ctypes.c_char_p * len(self.choices))() + for i in xrange(0, len(self.choices)): + choice_buf[i] = str(self.choices[i]) + value.choices = choice_buf + value.count = len(self.choices) + + def _fill_core_result(self, value): + value.indexResult = self.result + + def _get_result(self, value): + self.result = value.indexResult + + +class OpenFileNameField(object): + def __init__(self, prompt, ext = ""): + self.prompt = prompt + self.ext = ext + self.result = None + + def _fill_core_struct(self, value): + value.type = FormInputFieldType.OpenFileNameFormField + value.prompt = self.prompt + value.ext = self.ext + + def _fill_core_result(self, value): + value.stringResult = core.BNAllocString(str(self.result)) + + def _get_result(self, value): + self.result = value.stringResult + + +class SaveFileNameField(object): + def __init__(self, prompt, ext = "", default_name = ""): + self.prompt = prompt + self.ext = ext + self.default_name = default_name + self.result = None + + def _fill_core_struct(self, value): + value.type = FormInputFieldType.SaveFileNameFormField + value.prompt = self.prompt + value.ext = self.ext + value.defaultName = self.default_name + + def _fill_core_result(self, value): + value.stringResult = core.BNAllocString(str(self.result)) + + def _get_result(self, value): + self.result = value.stringResult + + +class DirectoryNameField(object): + def __init__(self, prompt, default_name = ""): + self.prompt = prompt + self.default_name = default_name + self.result = None + + def _fill_core_struct(self, value): + value.type = DirectoryNameField + value.prompt = self.prompt + value.defaultName = self.default_name + + def _fill_core_result(self, value): + value.stringResult = core.BNAllocString(str(self.result)) + + def _get_result(self, value): + self.result = value.stringResult + + +class InteractionHandler(object): + _interaction_handler = None + + def __init__(self): + self._cb = core.BNInteractionHandlerCallbacks() + self._cb.context = 0 + self._cb.showPlainTextReport = self._cb.showPlainTextReport.__class__(self._show_plain_text_report) + self._cb.showMarkdownReport = self._cb.showMarkdownReport.__class__(self._show_markdown_report) + self._cb.showHTMLReport = self._cb.showHTMLReport.__class__(self._show_html_report) + self._cb.getTextLineInput = self._cb.getTextLineInput.__class__(self._get_text_line_input) + self._cb.getIntegerInput = self._cb.getIntegerInput.__class__(self._get_int_input) + self._cb.getAddressInput = self._cb.getAddressInput.__class__(self._get_address_input) + self._cb.getChoiceInput = self._cb.getChoiceInput.__class__(self._get_choice_input) + self._cb.getOpenFileNameInput = self._cb.getOpenFileNameInput.__class__(self._get_open_filename_input) + self._cb.getSaveFileNameInput = self._cb.getSaveFileNameInput.__class__(self._get_save_filename_input) + self._cb.getDirectoryNameInput = self._cb.getDirectoryNameInput.__class__(self._get_directory_name_input) + self._cb.getFormInput = self._cb.getFormInput.__class__(self._get_form_input) + self._cb.showMessageBox = self._cb.showMessageBox.__class__(self._show_message_box) + + def register(self): + self.__class__._interaction_handler = self + core.BNRegisterInteractionHandler(self._cb) + + def _show_plain_text_report(self, ctxt, view, title, contents): + try: + if view: + view = binaryview.BinaryView(handle = core.BNNewViewReference(view)) + else: + view = None + self.show_plain_text_report(view, title, contents) + except: + log.log_error(traceback.format_exc()) + + def _show_markdown_report(self, ctxt, view, title, contents, plaintext): + try: + if view: + view = binaryview.BinaryView(handle = core.BNNewViewReference(view)) + else: + view = None + self.show_markdown_report(view, title, contents, plaintext) + except: + log.log_error(traceback.format_exc()) + + def _show_html_report(self, ctxt, view, title, contents, plaintext): + try: + if view: + view = binaryview.BinaryView(handle = core.BNNewViewReference(view)) + else: + view = None + self.show_html_report(view, title, contents, plaintext) + except: + log.log_error(traceback.format_exc()) + + def _get_text_line_input(self, ctxt, result, prompt, title): + try: + value = self.get_text_line_input(prompt, title) + if value is None: + return False + result[0] = core.BNAllocString(str(value)) + return True + except: + log.log_error(traceback.format_exc()) + + def _get_int_input(self, ctxt, result, prompt, title): + try: + value = self.get_int_input(prompt, title) + if value is None: + return False + result[0] = value + return True + except: + log.log_error(traceback.format_exc()) + + def _get_address_input(self, ctxt, result, prompt, title, view, current_address): + try: + if view: + view = binaryview.BinaryView(handle = core.BNNewViewReference(view)) + else: + view = None + value = self.get_address_input(prompt, title, view, current_address) + if value is None: + return False + result[0] = value + return True + except: + log.log_error(traceback.format_exc()) + + def _get_choice_input(self, ctxt, result, prompt, title, choice_buf, count): + try: + choices = [] + for i in xrange(0, count): + choices.append(choice_buf[i]) + value = self.get_choice_input(prompt, title, choices) + if value is None: + return False + result[0] = value + return True + except: + log.log_error(traceback.format_exc()) + + def _get_open_filename_input(self, ctxt, result, prompt, ext): + try: + value = self.get_open_filename_input(prompt, ext) + if value is None: + return False + result[0] = core.BNAllocString(str(value)) + return True + except: + log.log_error(traceback.format_exc()) + + def _get_save_filename_input(self, ctxt, result, prompt, ext, default_name): + try: + value = self.get_save_filename_input(prompt, ext, default_name) + if value is None: + return False + result[0] = core.BNAllocString(str(value)) + return True + except: + log.log_error(traceback.format_exc()) + + def _get_directory_name_input(self, ctxt, result, prompt, default_name): + try: + value = self.get_directory_name_input(prompt, default_name) + if value is None: + return False + result[0] = core.BNAllocString(str(value)) + return True + except: + log.log_error(traceback.format_exc()) + + def _get_form_input(self, ctxt, fields, count, title): + try: + field_objs = [] + for i in xrange(0, count): + if fields[i].type == FormInputFieldType.LabelFormField: + field_objs.append(LabelField(fields[i].prompt)) + elif fields[i].type == FormInputFieldType.SeparatorFormField: + field_objs.append(SeparatorField()) + elif fields[i].type == FormInputFieldType.TextLineFormField: + field_objs.append(TextLineField(fields[i].prompt)) + elif fields[i].type == FormInputFieldType.MultilineTextFormField: + field_objs.append(MultilineTextField(fields[i].prompt)) + elif fields[i].type == FormInputFieldType.IntegerFormField: + field_objs.append(IntegerField(fields[i].prompt)) + elif fields[i].type == FormInputFieldType.AddressFormField: + view = None + if fields[i].view: + view = binaryview.BinaryView(handle = core.BNNewViewReference(fields[i].view)) + field_objs.append(AddressField(fields[i].prompt, view, fields[i].currentAddress)) + elif fields[i].type == FormInputFieldType.ChoiceFormField: + choices = [] + for i in xrange(0, fields[i].count): + choices.append(fields[i].choices[i]) + field_objs.append(ChoiceField(fields[i].prompt, choices)) + elif fields[i].type == FormInputFieldType.OpenFileNameFormField: + field_objs.append(OpenFileNameField(fields[i].prompt, fields[i].ext)) + elif fields[i].type == FormInputFieldType.SaveFileNameFormField: + field_objs.append(SaveFileNameField(fields[i].prompt, fields[i].ext, fields[i].defaultName)) + elif fields[i].type == DirectoryNameField: + field_objs.append(DirectoryNameField(fields[i].prompt, fields[i].defaultName)) + else: + field_objs.append(LabelField(fields[i].prompt)) + if not self.get_form_input(field_objs, title): + return False + for i in xrange(0, count): + field_objs[i]._fill_core_result(fields[i]) + return True + except: + log.log_error(traceback.format_exc()) + + def _show_message_box(self, ctxt, title, text, buttons, icon): + try: + return self.show_message_box(title, text, buttons, icon) + except: + log.log_error(traceback.format_exc()) + + def show_plain_text_report(self, view, title, contents): + pass + + def show_markdown_report(self, view, title, contents, plaintext): + self.show_html_report(view, title, markdown_to_html(contents), plaintext) + + def show_html_report(self, view, title, contents, plaintext): + if len(plaintext) != 0: + self.show_plain_text_report(view, title, plaintext) + + def get_text_line_input(self, prompt, title): + return None + + def get_int_input(self, prompt, title): + while True: + text = self.get_text_line_input(prompt, title) + if len(text) == 0: + return False + try: + return int(text) + except: + continue + + def get_address_input(self, prompt, title, view, current_address): + return get_int_input(prompt, title) + + def get_choice_input(self, prompt, title, choices): + return None + + def get_open_filename_input(self, prompt, ext): + return get_text_line_input(prompt, "Open File") + + def get_save_filename_input(self, prompt, ext, default_name): + return get_text_line_input(prompt, "Save File") + + def get_directory_name_input(self, prompt, default_name): + return get_text_line_input(prompt, "Select Directory") + + def get_form_input(self, fields, title): + return False + + def show_message_box(self, title, text, buttons, icon): + return MessageBoxButtonResult.CancelButton + + +def markdown_to_html(contents): + return core.BNMarkdownToHTML(contents) + + +def show_plain_text_report(title, contents): + core.BNShowPlainTextReport(None, title, contents) + + +def show_markdown_report(title, contents, plaintext = ""): + core.BNShowMarkdownReport(None, title, contents, plaintext) + + +def show_html_report(title, contents, plaintext = ""): + core.BNShowHTMLReport(None, title, contents, plaintext) + + +def get_text_line_input(prompt, title): + value = ctypes.c_char_p() + if not core.BNGetTextLineInput(value, prompt, title): + return None + result = value.value + core.BNFreeString(ctypes.cast(value, ctypes.POINTER(ctypes.c_byte))) + return result + + +def get_int_input(prompt, title): + value = ctypes.c_longlong() + if not core.BNGetIntegerInput(value, prompt, title): + return None + return value.value + + +def get_address_input(prompt, title): + value = ctypes.c_ulonglong() + if not core.BNGetAddressInput(value, prompt, title, None, 0): + return None + return value.value + + +def get_choice_input(prompt, title, choices): + choice_buf = (ctypes.c_char_p * len(choices))() + for i in xrange(0, len(choices)): + choice_buf[i] = str(choices[i]) + value = ctypes.c_ulonglong() + if not core.BNGetChoiceInput(value, prompt, title, choice_buf, len(choices)): + return None + return value.value + + +def get_open_filename_input(prompt, ext = ""): + value = ctypes.c_char_p() + if not core.BNGetOpenFileNameInput(value, prompt, ext): + return None + result = value.value + core.BNFreeString(ctypes.cast(value, ctypes.POINTER(ctypes.c_byte))) + return result + + +def get_save_filename_input(prompt, ext = "", default_name = ""): + value = ctypes.c_char_p() + if not core.BNGetSaveFileNameInput(value, prompt, ext, default_name): + return None + result = value.value + core.BNFreeString(ctypes.cast(value, ctypes.POINTER(ctypes.c_byte))) + return result + + +def get_directory_name_input(prompt, default_name = ""): + value = ctypes.c_char_p() + if not core.BNGetDirectoryNameInput(value, prompt, default_name): + return None + result = value.value + core.BNFreeString(ctypes.cast(value, ctypes.POINTER(ctypes.c_byte))) + return result + + +def get_form_input(fields, title): + value = (core.BNFormInputField * len(fields))() + for i in xrange(0, len(fields)): + if isinstance(fields[i], str): + LabelField(fields[i])._fill_core_struct(value[i]) + elif fields[i] is None: + SeparatorField()._fill_core_struct(value[i]) + else: + fields[i]._fill_core_struct(value[i]) + if not core.BNGetFormInput(value, len(fields), title): + return False + for i in xrange(0, len(fields)): + if not (isinstance(fields[i], str) or (fields[i] is None)): + fields[i]._get_result(value[i]) + core.BNFreeFormInputResults(value, len(fields)) + return True + + +def show_message_box(title, text, buttons = MessageBoxButtonResult.OKButton, icon = MessageBoxIcon.InformationIcon): + return core.BNShowMessageBox(title, text, buttons, icon) diff --git a/python/lineardisassembly.py b/python/lineardisassembly.py new file mode 100644 index 00000000..9b88b78a --- /dev/null +++ b/python/lineardisassembly.py @@ -0,0 +1,47 @@ +# 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. + + +class LinearDisassemblyPosition(object): + """ + ``class LinearDisassemblyPosition`` is a helper object containing the position of the current Linear Disassembly. + + .. note:: This object should not be instantiated directly. Rather call \ + :py:method:`get_linear_disassembly_position_at` which instantiates this object. + """ + def __init__(self, func, block, addr): + self.function = func + self.block = block + self.address = addr + + +class LinearDisassemblyLine(object): + def __init__(self, line_type, func, block, line_offset, contents): + self.type = line_type + self.function = func + self.block = block + self.line_offset = line_offset + self.contents = contents + + def __str__(self): + return str(self.contents) + + def __repr__(self): + return repr(self.contents) diff --git a/python/log.py b/python/log.py new file mode 100644 index 00000000..45adb4aa --- /dev/null +++ b/python/log.py @@ -0,0 +1,175 @@ +# 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. + + +# Binary Ninja components +import _binaryninjacore as core + + +def redirect_output_to_log(): + global _output_to_log + _output_to_log = True + + +def log(level, text): + """ + ``log`` writes messages to the log console for the given log level. + + ============ ======== ======================================================================= + LogLevelName LogLevel Description + ============ ======== ======================================================================= + DebugLog 0 Logs debuging information messages to the console. + InfoLog 1 Logs general information messages to the console. + WarningLog 2 Logs message to console with **Warning** icon. + ErrorLog 3 Logs message to console with **Error** icon, focusing the error console. + AlertLog 4 Logs message to pop up window. + ============ ======== ======================================================================= + + :param LogLevel level: Log level to use + :param str text: message to print + :rtype: None + """ + core.BNLog(level, "%s", str(text)) + + +def log_debug(text): + """ + ``log_debug`` Logs debuging information messages to the console. + + :param str text: message to print + :rtype: None + :Example: + + >>> log_to_stdout(LogLevel.DebugLog) + >>> log_debug("Hotdogs!") + Hotdogs! + """ + core.BNLogDebug("%s", str(text)) + + +def log_info(text): + """ + ``log_info`` Logs general information messages to the console. + + :param str text: message to print + :rtype: None + :Example: + + >>> log_info("Saucisson!") + Saucisson! + >>> + """ + core.BNLogInfo("%s", str(text)) + + +def log_warn(text): + """ + ``log_warn`` Logs message to console, if run through the GUI it logs with **Warning** icon. + + :param str text: message to print + :rtype: None + :Example: + + >>> log_to_stdout(LogLevel.DebugLog) + >>> log_info("Chilidogs!") + Chilidogs! + >>> + """ + core.BNLogWarn("%s", str(text)) + + +def log_error(text): + """ + ``log_error`` Logs message to console, if run through the GUI it logs with **Error** icon, focusing the error console. + + :param str text: message to print + :rtype: None + :Example: + + >>> log_to_stdout(LogLevel.DebugLog) + >>> log_error("Spanferkel!") + Spanferkel! + >>> + """ + core.BNLogError("%s", str(text)) + + +def log_alert(text): + """ + ``log_alert`` Logs message console and to a pop up window if run through the GUI. + + :param str text: message to print + :rtype: None + :Example: + + >>> log_to_stdout(LogLevel.DebugLog) + >>> log_alert("Kielbasa!") + Kielbasa! + >>> + """ + core.BNLogAlert("%s", str(text)) + + +def log_to_stdout(min_level): + """ + ``log_to_stdout`` redirects minimum log level to standard out. + + :param int min_level: minimum level to log to + :rtype: None + :Example: + + >>> log_debug("Hotdogs!") + >>> log_to_stdout(LogLevel.DebugLog) + >>> log_debug("Hotdogs!") + Hotdogs! + >>> + """ + core.BNLogToStdout(min_level) + + +def log_to_stderr(min_level): + """ + ``log_to_stderr`` redirects minimum log level to standard error. + + :param int min_level: minimum level to log to + :rtype: None + """ + core.BNLogToStderr(min_level) + + +def log_to_file(min_level, path, append = False): + """ + ``log_to_file`` redirects minimum log level to a file named ``path``, optionally appending rather than overwritting. + + :param int min_level: minimum level to log to + :param str path: path to log to + :param bool append: optional flag for specifying appending. True = append, False = overwrite. + :rtype: None + """ + core.BNLogToFile(min_level, str(path), append) + + +def close_logs(): + """ + ``close_logs`` close all log files. + + :rtype: None + """ + core.BNCloseLogs() diff --git a/python/lowlevelil.py b/python/lowlevelil.py new file mode 100644 index 00000000..419e8513 --- /dev/null +++ b/python/lowlevelil.py @@ -0,0 +1,1282 @@ +# 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 ctypes + +# Binary Ninja components +import _binaryninjacore as core +from .enums import LowLevelILOperation, LowLevelILFlagCondition, InstructionTextTokenType +import function +import basicblock + + +class LowLevelILLabel(object): + def __init__(self, handle = None): + if handle is None: + self.handle = (core.BNLowLevelILLabel * 1)() + core.BNLowLevelILInitLabel(self.handle) + else: + self.handle = handle + + +class LowLevelILInstruction(object): + """ + ``class LowLevelILInstruction`` Low Level Intermediate Language Instructions are infinite length tree-based + instructions. Tree-based instructions use infix notation with the left hand operand being the destination operand. + Infix notation is thus more natural to read than other notations (e.g. x86 ``mov eax, 0`` vs. LLIL ``eax = 0``). + """ + + ILOperations = { + LowLevelILOperation.LLIL_NOP: [], + LowLevelILOperation.LLIL_SET_REG: [("dest", "reg"), ("src", "expr")], + LowLevelILOperation.LLIL_SET_REG_SPLIT: [("hi", "reg"), ("lo", "reg"), ("src", "expr")], + LowLevelILOperation.LLIL_SET_FLAG: [("dest", "flag"), ("src", "expr")], + LowLevelILOperation.LLIL_LOAD: [("src", "expr")], + LowLevelILOperation.LLIL_STORE: [("dest", "expr"), ("src", "expr")], + LowLevelILOperation.LLIL_PUSH: [("src", "expr")], + LowLevelILOperation.LLIL_POP: [], + LowLevelILOperation.LLIL_REG: [("src", "reg")], + LowLevelILOperation.LLIL_CONST: [("value", "int")], + LowLevelILOperation.LLIL_FLAG: [("src", "flag")], + LowLevelILOperation.LLIL_FLAG_BIT: [("src", "flag"), ("bit", "int")], + LowLevelILOperation.LLIL_ADD: [("left", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_ADC: [("left", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_SUB: [("left", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_SBB: [("left", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_AND: [("left", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_OR: [("left", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_XOR: [("left", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_LSL: [("left", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_LSR: [("left", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_ASR: [("left", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_ROL: [("left", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_RLC: [("left", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_ROR: [("left", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_RRC: [("left", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_MUL: [("left", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_MULU_DP: [("left", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_MULS_DP: [("left", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_DIVU: [("left", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_DIVU_DP: [("hi", "expr"), ("lo", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_DIVS: [("left", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_DIVS_DP: [("hi", "expr"), ("lo", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_MODU: [("left", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_MODU_DP: [("hi", "expr"), ("lo", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_MODS: [("left", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_MODS_DP: [("hi", "expr"), ("lo", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_NEG: [("src", "expr")], + LowLevelILOperation.LLIL_NOT: [("src", "expr")], + LowLevelILOperation.LLIL_SX: [("src", "expr")], + LowLevelILOperation.LLIL_ZX: [("src", "expr")], + LowLevelILOperation.LLIL_JUMP: [("dest", "expr")], + LowLevelILOperation.LLIL_JUMP_TO: [("dest", "expr"), ("targets", "int_list")], + LowLevelILOperation.LLIL_CALL: [("dest", "expr")], + LowLevelILOperation.LLIL_RET: [("dest", "expr")], + LowLevelILOperation.LLIL_NORET: [], + LowLevelILOperation.LLIL_IF: [("condition", "expr"), ("true", "int"), ("false", "int")], + LowLevelILOperation.LLIL_GOTO: [("dest", "int")], + LowLevelILOperation.LLIL_FLAG_COND: [("condition", "cond")], + LowLevelILOperation.LLIL_CMP_E: [("left", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_CMP_NE: [("left", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_CMP_SLT: [("left", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_CMP_ULT: [("left", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_CMP_SLE: [("left", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_CMP_ULE: [("left", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_CMP_SGE: [("left", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_CMP_UGE: [("left", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_CMP_SGT: [("left", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_CMP_UGT: [("left", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_TEST_BIT: [("left", "expr"), ("right", "expr")], + LowLevelILOperation.LLIL_BOOL_TO_INT: [("src", "expr")], + LowLevelILOperation.LLIL_SYSCALL: [], + LowLevelILOperation.LLIL_BP: [], + LowLevelILOperation.LLIL_TRAP: [("value", "int")], + LowLevelILOperation.LLIL_UNDEF: [], + LowLevelILOperation.LLIL_UNIMPL: [], + LowLevelILOperation.LLIL_UNIMPL_MEM: [("src", "expr")] + } + + def __init__(self, func, expr_index, instr_index=None): + instr = core.BNGetLowLevelILByIndex(func.handle, expr_index) + self.function = func + self.expr_index = expr_index + self.instr_index = instr_index + self.operation = LowLevelILOperation(instr.operation) + self.size = instr.size + self.address = instr.address + self.source_operand = instr.sourceOperand + if instr.flags == 0: + self.flags = None + else: + self.flags = func.arch.get_flag_write_type_name(instr.flags) + if self.source_operand == 0xffffffff: + self.source_operand = None + operands = LowLevelILInstruction.ILOperations[instr.operation] + self.operands = [] + for i in xrange(0, len(operands)): + name, operand_type = operands[i] + if operand_type == "int": + value = instr.operands[i] + elif operand_type == "expr": + value = LowLevelILInstruction(func, instr.operands[i]) + elif operand_type == "reg": + if (instr.operands[i] & 0x80000000) != 0: + value = instr.operands[i] + else: + value = func.arch.get_reg_name(instr.operands[i]) + elif operand_type == "flag": + value = func.arch.get_flag_name(instr.operands[i]) + elif operand_type == "cond": + value = LowLevelILFlagCondition(instr.operands[i]) + elif operand_type == "int_list": + count = ctypes.c_ulonglong() + operands = core.BNLowLevelILGetOperandList(func.handle, self.expr_index, i, count) + value = [] + for i in xrange(count.value): + value.append(operands[i]) + core.BNLowLevelILFreeOperandList(operands) + self.operands.append(value) + self.__dict__[name] = value + + def __str__(self): + tokens = self.tokens + if tokens is None: + return "invalid" + result = "" + for token in tokens: + result += token.text + return result + + def __repr__(self): + return "<il: %s>" % str(self) + + @property + def tokens(self): + """LLIL tokens (read-only)""" + count = ctypes.c_ulonglong() + tokens = ctypes.POINTER(core.BNInstructionTextToken)() + if (self.instr_index is not None) and (self.function.source_function is not None): + if not core.BNGetLowLevelILInstructionText(self.function.handle, self.function.source_function.handle, + self.function.arch.handle, self.instr_index, tokens, count): + return None + else: + if not core.BNGetLowLevelILExprText(self.function.handle, self.function.arch.handle, + self.expr_index, tokens, count): + return None + result = [] + for i in xrange(0, count.value): + token_type = InstructionTextTokenType(tokens[i].type) + text = tokens[i].text + value = tokens[i].value + size = tokens[i].size + operand = tokens[i].operand + result.append(function.InstructionTextToken(token_type, text, value, size, operand)) + core.BNFreeInstructionText(tokens, count.value) + return result + + def __setattr__(self, name, value): + try: + object.__setattr__(self, name, value) + except AttributeError: + raise AttributeError("attribute '%s' is read only" % name) + + +class LowLevelILExpr(object): + """ + ``class LowLevelILExpr`` hold the index of IL Expressions. + + .. note:: This class shouldn't be instantiated directly. Rather the helper members of LowLevelILFunction should be \ + used instead. + """ + def __init__(self, index): + self.index = index + + +class LowLevelILFunction(object): + """ + ``class LowLevelILFunction`` contains the list of LowLevelILExpr objects that make up a function. LowLevelILExpr + objects can be added to the LowLevelILFunction by calling ``append`` and passing the result of the various class + methods which return LowLevelILExpr objects. + + + LowLevelILFlagCondition values used as parameters in the ``flag_condition`` method. + + ======================= ========== =============================== + LowLevelILFlagCondition Operator Description + ======================= ========== =============================== + LLFC_E == Equal + LLFC_NE != Not equal + LLFC_SLT s< Signed less than + LLFC_ULT u< Unsigned less than + LLFC_SLE s<= Signed less than or equal + LLFC_ULE u<= Unsigned less than or equal + LLFC_SGE s>= Signed greater than or equal + LLFC_UGE u>= Unsigned greater than or equal + LLFC_SGT s> Signed greather than + LLFC_UGT u> Unsigned greater than + LLFC_NEG - Negative + LLFC_POS + Positive + LLFC_O overflow Overflow + LLFC_NO !overflow No overflow + ======================= ========== =============================== + """ + def __init__(self, arch, handle = None, source_func = None): + self.arch = arch + self.source_function = source_func + if handle is not None: + self.handle = core.handle_of_type(handle, core.BNLowLevelILFunction) + else: + func_handle = None + if self.source_function is not None: + func_handle = self.source_function.handle + self.handle = core.BNCreateLowLevelILFunction(arch.handle, func_handle) + + def __del__(self): + core.BNFreeLowLevelILFunction(self.handle) + + @property + def current_address(self): + """Current IL Address (read/write)""" + return core.BNLowLevelILGetCurrentAddress(self.handle) + + @current_address.setter + def current_address(self, value): + core.BNLowLevelILSetCurrentAddress(self.handle, value) + + @property + def temp_reg_count(self): + """Number of temporary registers (read-only)""" + return core.BNGetLowLevelILTemporaryRegisterCount(self.handle) + + @property + def temp_flag_count(self): + """Number of temporary flags (read-only)""" + return core.BNGetLowLevelILTemporaryFlagCount(self.handle) + + @property + def basic_blocks(self): + """list of LowLevelILBasicBlock objects (read-only)""" + count = ctypes.c_ulonglong() + blocks = core.BNGetLowLevelILBasicBlockList(self.handle, count) + result = [] + view = None + if self.source_function is not None: + view = self.source_function.view + for i in xrange(0, count.value): + result.append(LowLevelILBasicBlock(view, core.BNNewBasicBlockReference(blocks[i]), self)) + core.BNFreeBasicBlockList(blocks, count.value) + return result + + def __setattr__(self, name, value): + try: + object.__setattr__(self, name, value) + except AttributeError: + raise AttributeError("attribute '%s' is read only" % name) + + def __len__(self): + return int(core.BNGetLowLevelILInstructionCount(self.handle)) + + def __getitem__(self, i): + if isinstance(i, slice) or isinstance(i, tuple): + raise IndexError("expected integer instruction index") + if isinstance(i, LowLevelILExpr): + return LowLevelILInstruction(self, i.index) + if (i < 0) or (i >= len(self)): + raise IndexError("index out of range") + return LowLevelILInstruction(self, core.BNGetLowLevelILIndexForInstruction(self.handle, i), i) + + def __setitem__(self, i, j): + raise IndexError("instruction modification not implemented") + + def __iter__(self): + count = ctypes.c_ulonglong() + blocks = core.BNGetLowLevelILBasicBlockList(self.handle, count) + view = None + if self.source_function is not None: + view = self.source_function.view + try: + for i in xrange(0, count.value): + yield LowLevelILBasicBlock(view, core.BNNewBasicBlockReference(blocks[i]), self) + finally: + core.BNFreeBasicBlockList(blocks, count.value) + + def clear_indirect_branches(self): + core.BNLowLevelILClearIndirectBranches(self.handle) + + def set_indirect_branches(self, branches): + branch_list = (core.BNArchitectureAndAddress * len(branches))() + for i in xrange(len(branches)): + branch_list[i].arch = branches[i][0].handle + branch_list[i].address = branches[i][1] + core.BNLowLevelILSetIndirectBranches(self.handle, branch_list, len(branches)) + + def expr(self, operation, a = 0, b = 0, c = 0, d = 0, size = 0, flags = None): + if isinstance(operation, str): + operation = LowLevelILOperation[operation] + elif isinstance(operation, LowLevelILOperation): + operation = operation.value + if isinstance(flags, str): + flags = self.arch.get_flag_write_type_by_name(flags) + elif flags is None: + flags = 0 + return LowLevelILExpr(core.BNLowLevelILAddExpr(self.handle, operation, size, flags, a, b, c, d)) + + def append(self, expr): + """ + ``append`` adds the LowLevelILExpr ``expr`` to the current LowLevelILFunction. + + :param LowLevelILExpr expr: the LowLevelILExpr to add to the current LowLevelILFunction + :return: number of LowLevelILExpr in the current function + :rtype: int + """ + return core.BNLowLevelILAddInstruction(self.handle, expr.index) + + def nop(self): + """ + ``nop`` no operation, this instruction does nothing + + :return: The no operation expression + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_NOP) + + def set_reg(self, size, reg, value, flags = 0): + """ + ``set_reg`` sets the register ``reg`` of size ``size`` to the expression ``value`` + + :param int size: size of the register parameter in bytes + :param str reg: the register name + :param LowLevelILExpr value: an expression to set the register to + :param str flags: which flags are set by this operation + :return: The expression ``reg = value`` + :rtype: LowLevelILExpr + """ + if isinstance(reg, str): + reg = self.arch.regs[reg].index + return self.expr(LowLevelILOperation.LLIL_SET_REG, reg, value.index, size = size, flags = flags) + + def set_reg_split(self, size, hi, lo, value, flags = 0): + """ + ``set_reg_split`` uses ``hi`` and ``lo`` as a single extended register setting ``hi:lo`` to the expression + ``value``. + + :param int size: size of the register parameter in bytes + :param str hi: the high register name + :param str lo: the low register name + :param LowLevelILExpr value: an expression to set the split regiters to + :param str flags: which flags are set by this operation + :return: The expression ``hi:lo = value`` + :rtype: LowLevelILExpr + """ + if isinstance(hi, str): + hi = self.arch.regs[hi].index + if isinstance(lo, str): + lo = self.arch.regs[lo].index + return self.expr(LowLevelILOperation.LLIL_SET_REG_SPLIT, hi, lo, value.index, size = size, flags = flags) + + def set_flag(self, flag, value): + """ + ``set_flag`` sets the flag ``flag`` to the LowLevelILExpr ``value`` + + :param str flag: the low register name + :param LowLevelILExpr value: an expression to set the flag to + :return: The expression FLAG.flag = value + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_SET_FLAG, self.arch.get_flag_by_name(flag), value.index) + + def load(self, size, addr): + """ + ``laod`` Reads ``size`` bytes from the expression ``addr`` + + :param int size: number of bytes to read + :param LowLevelILExpr addr: the expression to read memory from + :return: The expression ``[addr].size`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_LOAD, addr.index, size=size) + + def store(self, size, addr, value): + """ + ``store`` Writes ``size`` bytes to expression ``addr`` read from expression ``value`` + + :param int size: number of bytes to write + :param LowLevelILExpr addr: the expression to write to + :param LowLevelILExpr value: the expression to be written + :return: The expression ``[addr].size = value`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_STORE, addr.index, value.index, size=size) + + def push(self, size, value): + """ + ``push`` writes ``size`` bytes from expression ``value`` to the stack, adjusting the stack by ``size``. + + :param int size: number of bytes to write and adjust the stack by + :param LowLevelILExpr value: the expression to write + :return: The expression push(value) + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_PUSH, value.index, size=size) + + def pop(self, size): + """ + ``pop`` reads ``size`` bytes from the stack, adjusting the stack by ``size``. + + :param int size: number of bytes to read from the stack + :return: The expression ``pop`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_POP, size=size) + + def reg(self, size, reg): + """ + ``reg`` returns a register of size ``size`` with name ``name`` + + :param int size: the size of the register in bytes + :param str reg: the name of the register + :return: A register expression for the given string + :rtype: LowLevelILExpr + """ + if isinstance(reg, str): + reg = self.arch.regs[reg].index + return self.expr(LowLevelILOperation.LLIL_REG, reg, size=size) + + def const(self, size, value): + """ + ``const`` returns an expression for the constant integer ``value`` with size ``size`` + + :param int size: the size of the constant in bytes + :param int value: integer value of the constant + :return: A constant expression of given value and size + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_CONST, value, size=size) + + def flag(self, reg): + """ + ``flag`` returns a flag expression for the given flag name. + + :param str reg: name of the flag expression to retrieve + :return: A flag expression of given flag name + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_FLAG, self.arch.get_flag_by_name(reg)) + + def flag_bit(self, size, reg, bit): + """ + ``flag_bit`` sets the flag named ``reg`` and size ``size`` to the constant integer value ``bit`` + + :param int size: the size of the flag + :param str reg: flag value + :param int bit: integer value to set the bit to + :return: A constant expression of given value and size ``FLAG.reg = bit`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_FLAG_BIT, self.arch.get_flag_by_name(reg), bit, size=size) + + def add(self, size, a, b, flags=None): + """ + ``add`` adds expression ``a`` to expression ``b`` potentially setting flags ``flags`` and returning + an expression of ``size`` bytes. + + :param int size: the size of the result in bytes + :param LowLevelILExpr a: LHS expression + :param LowLevelILExpr b: RHS expression + :param str flags: flags to set + :return: The expression ``add.<size>{<flags>}(a, b)`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_ADD, a.index, b.index, size=size, flags=flags) + + def add_carry(self, size, a, b, flags=None): + """ + ``add_carry`` adds with carry expression ``a`` to expression ``b`` potentially setting flags ``flags`` and + returning an expression of ``size`` bytes. + + :param int size: the size of the result in bytes + :param LowLevelILExpr a: LHS expression + :param LowLevelILExpr b: RHS expression + :param str flags: flags to set + :return: The expression ``adc.<size>{<flags>}(a, b)`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_ADC, a.index, b.index, size=size, flags=flags) + + def sub(self, size, a, b, flags=None): + """ + ``sub`` subtracts expression ``b`` from expression ``a`` potentially setting flags ``flags`` and returning + an expression of ``size`` bytes. + + :param int size: the size of the result in bytes + :param LowLevelILExpr a: LHS expression + :param LowLevelILExpr b: RHS expression + :param str flags: flags to set + :return: The expression ``sub.<size>{<flags>}(a, b)`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_SUB, a.index, b.index, size=size, flags=flags) + + def sub_borrow(self, size, a, b, flags=None): + """ + ``sub_borrow`` subtracts with borrow expression ``b`` from expression ``a`` potentially setting flags ``flags`` + and returning an expression of ``size`` bytes. + + :param int size: the size of the result in bytes + :param LowLevelILExpr a: LHS expression + :param LowLevelILExpr b: RHS expression + :param str flags: flags to set + :return: The expression ``sbc.<size>{<flags>}(a, b)`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_SBB, a.index, b.index, size=size, flags=flags) + + def and_expr(self, size, a, b, flags=None): + """ + ``and_expr`` bitwise and's expression ``a`` and expression ``b`` potentially setting flags ``flags`` + and returning an expression of ``size`` bytes. + + :param int size: the size of the result in bytes + :param LowLevelILExpr a: LHS expression + :param LowLevelILExpr b: RHS expression + :param str flags: optional, flags to set + :return: The expression ``and.<size>{<flags>}(a, b)`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_AND, a.index, b.index, size=size, flags=flags) + + def or_expr(self, size, a, b, flags=None): + """ + ``or_expr`` bitwise or's expression ``a`` and expression ``b`` potentially setting flags ``flags`` + and returning an expression of ``size`` bytes. + + :param int size: the size of the result in bytes + :param LowLevelILExpr a: LHS expression + :param LowLevelILExpr b: RHS expression + :param str flags: optional, flags to set + :return: The expression ``or.<size>{<flags>}(a, b)`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_OR, a.index, b.index, size=size, flags=flags) + + def xor_expr(self, size, a, b, flags=None): + """ + ``xor_expr`` xor's expression ``a`` with expression ``b`` potentially setting flags ``flags`` + and returning an expression of ``size`` bytes. + + :param int size: the size of the result in bytes + :param LowLevelILExpr a: LHS expression + :param LowLevelILExpr b: RHS expression + :param str flags: optional, flags to set + :return: The expression ``xor.<size>{<flags>}(a, b)`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_XOR, a.index, b.index, size=size, flags=flags) + + def shift_left(self, size, a, b, flags=None): + """ + ``shift_left`` subtracts with borrow expression ``b`` from expression ``a`` potentially setting flags ``flags`` + and returning an expression of ``size`` bytes. + + :param int size: the size of the result in bytes + :param LowLevelILExpr a: LHS expression + :param LowLevelILExpr b: RHS expression + :param str flags: optional, flags to set + :return: The expression ``lsl.<size>{<flags>}(a, b)`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_LSL, a.index, b.index, size=size, flags=flags) + + def logical_shift_right(self, size, a, b, flags=None): + """ + ``logical_shift_right`` shifts logically right expression ``a`` by expression ``b`` potentially setting flags + ``flags``and returning an expression of ``size`` bytes. + + :param int size: the size of the result in bytes + :param LowLevelILExpr a: LHS expression + :param LowLevelILExpr b: RHS expression + :param str flags: optional, flags to set + :return: The expression ``lsr.<size>{<flags>}(a, b)`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_LSR, a.index, b.index, size=size, flags=flags) + + def arith_shift_right(self, size, a, b, flags=None): + """ + ``arith_shift_right`` shifts arithmatic right expression ``a`` by expression ``b`` potentially setting flags + ``flags`` and returning an expression of ``size`` bytes. + + :param int size: the size of the result in bytes + :param LowLevelILExpr a: LHS expression + :param LowLevelILExpr b: RHS expression + :param str flags: optional, flags to set + :return: The expression ``asr.<size>{<flags>}(a, b)`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_ASR, a.index, b.index, size=size, flags=flags) + + def rotate_left(self, size, a, b, flags=None): + """ + ``rotate_left`` bitwise rotates left expression ``a`` by expression ``b`` potentially setting flags ``flags`` + and returning an expression of ``size`` bytes. + + :param int size: the size of the result in bytes + :param LowLevelILExpr a: LHS expression + :param LowLevelILExpr b: RHS expression + :param str flags: optional, flags to set + :return: The expression ``rol.<size>{<flags>}(a, b)`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_ROL, a.index, b.index, size=size, flags=flags) + + def rotate_left_carry(self, size, a, b, flags=None): + """ + ``rotate_left_carry`` bitwise rotates left with carry expression ``a`` by expression ``b`` potentially setting + flags ``flags`` and returning an expression of ``size`` bytes. + + :param int size: the size of the result in bytes + :param LowLevelILExpr a: LHS expression + :param LowLevelILExpr b: RHS expression + :param str flags: optional, flags to set + :return: The expression ``rcl.<size>{<flags>}(a, b)`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_RLC, a.index, b.index, size=size, flags=flags) + + def rotate_right(self, size, a, b, flags=None): + """ + ``rotate_right`` bitwise rotates right expression ``a`` by expression ``b`` potentially setting flags ``flags`` + and returning an expression of ``size`` bytes. + + :param int size: the size of the result in bytes + :param LowLevelILExpr a: LHS expression + :param LowLevelILExpr b: RHS expression + :param str flags: optional, flags to set + :return: The expression ``ror.<size>{<flags>}(a, b)`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_ROR, a.index, b.index, size=size, flags=flags) + + def rotate_right_carry(self, size, a, b, flags=None): + """ + ``rotate_right_carry`` bitwise rotates right with carry expression ``a`` by expression ``b`` potentially setting + flags ``flags`` and returning an expression of ``size`` bytes. + + :param int size: the size of the result in bytes + :param LowLevelILExpr a: LHS expression + :param LowLevelILExpr b: RHS expression + :param str flags: optional, flags to set + :return: The expression ``rcr.<size>{<flags>}(a, b)`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_RRC, a.index, b.index, size=size, flags=flags) + + def mult(self, size, a, b, flags=None): + """ + ``mult`` multiplies expression ``a`` by expression ``b`` potentially setting flags ``flags`` and returning an + expression of ``size`` bytes. + + :param int size: the size of the result in bytes + :param LowLevelILExpr a: LHS expression + :param LowLevelILExpr b: RHS expression + :param str flags: optional, flags to set + :return: The expression ``sbc.<size>{<flags>}(a, b)`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_MUL, a.index, b.index, size=size, flags=flags) + + def mult_double_prec_signed(self, size, a, b, flags=None): + """ + ``mult_double_prec_signed`` multiplies signed with double precision expression ``a`` by expression ``b`` + potentially setting flags ``flags`` and returning an expression of ``size`` bytes. + + :param int size: the size of the result in bytes + :param LowLevelILExpr a: LHS expression + :param LowLevelILExpr b: RHS expression + :param str flags: optional, flags to set + :return: The expression ``muls.dp.<size>{<flags>}(a, b)`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_MULS_DP, a.index, b.index, size=size, flags=flags) + + def mult_double_prec_unsigned(self, size, a, b, flags=None): + """ + ``mult_double_prec_unsigned`` multiplies unsigned with double precision expression ``a`` by expression ``b`` + potentially setting flags ``flags`` and returning an expression of ``size`` bytes. + + :param int size: the size of the result in bytes + :param LowLevelILExpr a: LHS expression + :param LowLevelILExpr b: RHS expression + :param str flags: optional, flags to set + :return: The expression ``muls.dp.<size>{<flags>}(a, b)`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_MULU_DP, a.index, b.index, size=size, flags=flags) + + def div_signed(self, size, a, b, flags=None): + """ + ``div_signed`` signed divide expression ``a`` by expression ``b`` potentially setting flags ``flags`` + and returning an expression of ``size`` bytes. + + :param int size: the size of the result in bytes + :param LowLevelILExpr a: LHS expression + :param LowLevelILExpr b: RHS expression + :param str flags: optional, flags to set + :return: The expression ``divs.<size>{<flags>}(a, b)`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_DIVS, a.index, b.index, size=size, flags=flags) + + def div_double_prec_signed(self, size, hi, lo, b, flags=None): + """ + ``div_double_prec_signed`` signed double precision divide using expression ``hi`` and expression ``lo`` as a + single double precision register by expression ``b`` potentially setting flags ``flags`` and returning an + expression of ``size`` bytes. + + :param int size: the size of the result in bytes + :param LowLevelILExpr hi: high LHS expression + :param LowLevelILExpr lo: low LHS expression + :param LowLevelILExpr b: RHS expression + :param str flags: optional, flags to set + :return: The expression ``divs.dp.<size>{<flags>}(hi:lo, b)`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_DIVS_DP, hi.index, lo.index, b.index, size=size, flags=flags) + + def div_unsigned(self, size, a, b, flags=None): + """ + ``div_unsigned`` unsigned divide expression ``a`` by expression ``b`` potentially setting flags ``flags`` + and returning an expression of ``size`` bytes. + + :param int size: the size of the result in bytes + :param LowLevelILExpr a: LHS expression + :param LowLevelILExpr b: RHS expression + :param str flags: optional, flags to set + :return: The expression ``divs.<size>{<flags>}(a, b)`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_DIVS, a.index, b.index, size=size, flags=flags) + + def div_double_prec_unsigned(self, size, hi, lo, b, flags=None): + """ + ``div_double_prec_unsigned`` unsigned double precision divide using expression ``hi`` and expression ``lo`` as + a single double precision register by expression ``b`` potentially setting flags ``flags`` and returning an + expression of ``size`` bytes. + + :param int size: the size of the result in bytes + :param LowLevelILExpr hi: high LHS expression + :param LowLevelILExpr lo: low LHS expression + :param LowLevelILExpr b: RHS expression + :param str flags: optional, flags to set + :return: The expression ``divs.dp.<size>{<flags>}(hi:lo, b)`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_DIVS_DP, hi.index, lo.index, b.index, size=size, flags=flags) + + def mod_signed(self, size, a, b, flags=None): + """ + ``mod_signed`` signed modulus expression ``a`` by expression ``b`` potentially setting flags ``flags`` + and returning an expression of ``size`` bytes. + + :param int size: the size of the result in bytes + :param LowLevelILExpr a: LHS expression + :param LowLevelILExpr b: RHS expression + :param str flags: optional, flags to set + :return: The expression ``mods.<size>{<flags>}(a, b)`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_MODS, a.index, b.index, size=size, flags=flags) + + def mod_double_prec_signed(self, size, hi, lo, b, flags=None): + """ + ``mod_double_prec_signed`` signed double precision modulus using expression ``hi`` and expression ``lo`` as a single + double precision register by expression ``b`` potentially setting flags ``flags`` and returning an expression + of ``size`` bytes. + + :param int size: the size of the result in bytes + :param LowLevelILExpr hi: high LHS expression + :param LowLevelILExpr lo: low LHS expression + :param LowLevelILExpr b: RHS expression + :param str flags: optional, flags to set + :return: The expression ``mods.dp.<size>{<flags>}(hi:lo, b)`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_MODS_DP, hi.index, lo.index, b.index, size=size, flags=flags) + + def mod_unsigned(self, size, a, b, flags=None): + """ + ``mod_unsigned`` unsigned modulus expression ``a`` by expression ``b`` potentially setting flags ``flags`` + and returning an expression of ``size`` bytes. + + :param int size: the size of the result in bytes + :param LowLevelILExpr a: LHS expression + :param LowLevelILExpr b: RHS expression + :param str flags: optional, flags to set + :return: The expression ``modu.<size>{<flags>}(a, b)`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_MODS, a.index, b.index, size=size, flags=flags) + + def mod_double_prec_unsigned(self, size, hi, lo, b, flags=None): + """ + ``mod_double_prec_unsigned`` unsigned double precision modulus using expression ``hi`` and expression ``lo`` as + a single double precision register by expression ``b`` potentially setting flags ``flags`` and returning an + expression of ``size`` bytes. + + :param int size: the size of the result in bytes + :param LowLevelILExpr hi: high LHS expression + :param LowLevelILExpr lo: low LHS expression + :param LowLevelILExpr b: RHS expression + :param str flags: optional, flags to set + :return: The expression ``modu.dp.<size>{<flags>}(hi:lo, b)`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_MODS_DP, hi.index, lo.index, b.index, size=size, flags=flags) + + def neg_expr(self, size, value, flags=None): + """ + ``neg_expr`` two's complement sign negation of expression ``value`` of size ``size`` potentially setting flags + + :param int size: the size of the result in bytes + :param LowLevelILExpr value: the expression to negate + :param str flags: optional, flags to set + :return: The expression ``neg.<size>{<flags>}(value)`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_NEG, value.index, size=size, flags=flags) + + def not_expr(self, size, value, flags=None): + """ + ``not_expr`` bitwise inverse of expression ``value`` of size ``size`` potentially setting flags + + :param int size: the size of the result in bytes + :param LowLevelILExpr value: the expression to bitwise invert + :param str flags: optional, flags to set + :return: The expression ``not.<size>{<flags>}(value)`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_NOT, value.index, size=size, flags=flags) + + def sign_extend(self, size, value, flags=None): + """ + ``sign_extend`` two's complement sign-extends the expression in ``value`` to ``size`` bytes + + :param int size: the size of the result in bytes + :param LowLevelILExpr value: the expression to sign extn + :param str flags: optional, flags to set + :return: The expression ``sx.<size>(value)`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_SX, value.index, size=size, flags=flags) + + def zero_extend(self, size, value): + """ + ``zero_extend`` zero-extends the expression in ``value`` to ``size`` bytes + + :param int size: the size of the result in bytes + :param LowLevelILExpr value: the expression to zero extend + :return: The expression ``sx.<size>(value)`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_ZX, value.index, size=size) + + def jump(self, dest): + """ + ``jump`` returns an expression which jumps (branches) to the expression ``dest`` + + :param LowLevelILExpr dest: the expression to jump to + :return: The expression ``jump(dest)`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_JUMP, dest.index) + + def call(self, dest): + """ + ``call`` returns an expression which first pushes the address of the next instruction onto the stack then jumps + (branches) to the expression ``dest`` + + :param LowLevelILExpr dest: the expression to call + :return: The expression ``call(dest)`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_CALL, dest.index) + + def ret(self, dest): + """ + ``ret`` returns an expression which jumps (branches) to the expression ``dest``. ``ret`` is a special alias for + jump that makes the disassembler top disassembling. + + :param LowLevelILExpr dest: the expression to jump to + :return: The expression ``jump(dest)`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_RET, dest.index) + + def no_ret(self): + """ + ``no_ret`` returns an expression halts disassembly + + :return: The expression ``noreturn`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_NORET) + + def flag_condition(self, cond): + """ + ``flag_condition`` returns a flag_condition expression for the given LowLevelILFlagCondition + + :param LowLevelILFlagCondition cond: Flag condition expression to retrieve + :return: A flag_condition expression + :rtype: LowLevelILExpr + """ + if isinstance(cond, str): + cond = LowLevelILFlagCondition[cond] + elif isinstance(cond, LowLevelILFlagCondition): + cond = cond.value + return self.expr(LowLevelILOperation.LLIL_FLAG_COND, cond) + + def compare_equal(self, size, a, b): + """ + ``compare_equal`` returns comparison expression of size ``size`` checking if expression ``a`` is equal to + expression ``b`` + + :param int size: size in bytes + :param LowLevelILExpr a: LHS of comparison + :param LowLevelILExpr b: RHS of comparison + :return: a comparison expression. + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_CMP_E, a.index, b.index, size = size) + + def compare_not_equal(self, size, a, b): + """ + ``compare_not_equal`` returns comparison expression of size ``size`` checking if expression ``a`` is not equal to + expression ``b`` + + :param int size: size in bytes + :param LowLevelILExpr a: LHS of comparison + :param LowLevelILExpr b: RHS of comparison + :return: a comparison expression. + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_CMP_NE, a.index, b.index, size = size) + + def compare_signed_less_than(self, size, a, b): + """ + ``compare_signed_less_than`` returns comparison expression of size ``size`` checking if expression ``a`` is + signed less than expression ``b`` + + :param int size: size in bytes + :param LowLevelILExpr a: LHS of comparison + :param LowLevelILExpr b: RHS of comparison + :return: a comparison expression. + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_CMP_SLT, a.index, b.index, size = size) + + def compare_unsigned_less_than(self, size, a, b): + """ + ``compare_unsigned_less_than`` returns comparison expression of size ``size`` checking if expression ``a`` is + unsigned less than expression ``b`` + + :param int size: size in bytes + :param LowLevelILExpr a: LHS of comparison + :param LowLevelILExpr b: RHS of comparison + :return: a comparison expression. + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_CMP_ULT, a.index, b.index, size = size) + + def compare_signed_less_equal(self, size, a, b): + """ + ``compare_signed_less_equal`` returns comparison expression of size ``size`` checking if expression ``a`` is + signed less than or equal to expression ``b`` + + :param int size: size in bytes + :param LowLevelILExpr a: LHS of comparison + :param LowLevelILExpr b: RHS of comparison + :return: a comparison expression. + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_CMP_SLE, a.index, b.index, size = size) + + def compare_unsigned_less_equal(self, size, a, b): + """ + ``compare_unsigned_less_equal`` returns comparison expression of size ``size`` checking if expression ``a`` is + unsigned less than or equal to expression ``b`` + + :param int size: size in bytes + :param LowLevelILExpr a: LHS of comparison + :param LowLevelILExpr b: RHS of comparison + :return: a comparison expression. + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_CMP_ULE, a.index, b.index, size = size) + + def compare_signed_greater_equal(self, size, a, b): + """ + ``compare_signed_greater_equal`` returns comparison expression of size ``size`` checking if expression ``a`` is + signed greater than or equal toexpression ``b`` + + :param int size: size in bytes + :param LowLevelILExpr a: LHS of comparison + :param LowLevelILExpr b: RHS of comparison + :return: a comparison expression. + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_CMP_SGE, a.index, b.index, size = size) + + def compare_unsigned_greater_equal(self, size, a, b): + """ + ``compare_unsigned_greater_equal`` returns comparison expression of size ``size`` checking if expression ``a`` + is unsigned greater than or equal to expression ``b`` + + :param int size: size in bytes + :param LowLevelILExpr a: LHS of comparison + :param LowLevelILExpr b: RHS of comparison + :return: a comparison expression. + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_CMP_UGE, a.index, b.index, size = size) + + def compare_signed_greater_than(self, size, a, b): + """ + ``compare_signed_greater_than`` returns comparison expression of size ``size`` checking if expression ``a`` is + signed greater than or equal to expression ``b`` + + :param int size: size in bytes + :param LowLevelILExpr a: LHS of comparison + :param LowLevelILExpr b: RHS of comparison + :return: a comparison expression. + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_CMP_SGT, a.index, b.index, size = size) + + def compare_unsigned_greater_than(self, size, a, b): + """ + ``compare_unsigned_greater_than`` returns comparison expression of size ``size`` checking if expression ``a`` is + unsigned greater than or equal to expression ``b`` + + :param int size: size in bytes + :param LowLevelILExpr a: LHS of comparison + :param LowLevelILExpr b: RHS of comparison + :return: a comparison expression. + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_CMP_UGT, a.index, b.index, size = size) + + def test_bit(self, size, a, b): + return self.expr(LowLevelILOperation.LLIL_TEST_BIT, a.index, b.index, size = size) + + def system_call(self): + """ + ``system_call`` return a system call expression. + + :return: a system call expression. + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_SYSCALL) + + def breakpoint(self): + """ + ``breakpoint`` returns a processor breakpoint expression. + + :return: a breakpoint expression. + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_BP) + + def trap(self, value): + """ + ``trap`` returns a processor trap (interrupt) expression of the given integer ``value``. + + :param int value: trap (interrupt) number + :return: a trap expression. + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_TRAP, value) + + def undefined(self): + """ + ``undefined`` returns the undefined expression. This should be used for instructions which perform functions but + aren't important for dataflow or partial emulation purposes. + + :return: the unimplemented expression. + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_UNDEF) + + def unimplemented(self): + """ + ``unimplemented`` returns the unimplemented expression. This should be used for all instructions which aren't + implemented. + + :return: the unimplemented expression. + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_UNIMPL) + + def unimplemented_memory_ref(self, size, addr): + """ + ``unimplemented_memory_ref`` a memory reference to expression ``addr`` of size ``size`` with unimplemented operation. + + :param int size: size in bytes of the memory reference + :param LowLevelILExpr addr: expression to reference memory + :return: the unimplemented memory reference expression. + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_UNIMPL_MEM, addr.index, size = size) + + def goto(self, label): + """ + ``goto`` returns a goto expression which jumps to the provided LowLevelILLabel. + + :param LowLevelILLabel label: Label to jump to + :return: the LowLevelILExpr that jumps to the provided label + :rtype: LowLevelILExpr + """ + return LowLevelILExpr(core.BNLowLevelILGoto(self.handle, label.handle)) + + def if_expr(self, operand, t, f): + """ + ``if_expr`` returns the ``if`` expression which depending on condition ``operand`` jumps to the LowLevelILLabel + ``t`` when the condition expression ``operand`` is non-zero and ``f`` when it's zero. + + :param LowLevelILExpr operand: comparison expression to evaluate. + :param LowLevelILLabel t: Label for the true branch + :param LowLevelILLabel f: Label for the false branch + :return: the LowLevelILExpr for the if expression + :rtype: LowLevelILExpr + """ + return LowLevelILExpr(core.BNLowLevelILIf(self.handle, operand.index, t.handle, f.handle)) + + def mark_label(self, label): + """ + ``mark_label`` assigns a LowLevelILLabel to the current IL address. + + :param LowLevelILLabel label: + :rtype: None + """ + core.BNLowLevelILMarkLabel(self.handle, label.handle) + + def add_label_list(self, labels): + """ + ``add_label_list`` returns a label list expression for the given list of LowLevelILLabel objects. + + :param list(LowLevelILLabel) lables: the list of LowLevelILLabel to get a label list expression from + :return: the label list expression + :rtype: LowLevelILExpr + """ + label_list = (ctypes.POINTER(core.BNLowLevelILLabel) * len(labels))() + for i in xrange(len(labels)): + label_list[i] = labels[i].handle + return LowLevelILExpr(core.BNLowLevelILAddLabelList(self.handle, label_list, len(labels))) + + def add_operand_list(self, operands): + """ + ``add_operand_list`` returns an operand list expression for the given list of integer operands. + + :param list(int) operands: list of operand numbers + :return: an operand list expression + :rtype: LowLevelILExpr + """ + operand_list = (ctypes.c_ulonglong * len(operands))() + for i in xrange(len(operands)): + operand_list[i] = operands[i] + return LowLevelILExpr(core.BNLowLevelILAddOperandList(self.handle, operand_list, len(operands))) + + def operand(self, n, expr): + """ + ``operand`` sets the operand number of the expression ``expr`` and passes back ``expr`` without modification. + + :param int n: + :param LowLevelILExpr expr: + :return: returns the expression ``expr`` unmodified + :rtype: LowLevelILExpr + """ + core.BNLowLevelILSetExprSourceOperand(self.handle, expr.index, n) + return expr + + def finalize(self): + """ + ``finalize`` ends the function and computes the list of basic blocks. + + :rtype: None + """ + core.BNFinalizeLowLevelILFunction(self.handle) + + def add_label_for_address(self, arch, addr): + """ + ``add_label_for_address`` adds a low-level IL label for the given architecture ``arch`` at the given virtual + address ``addr`` + + :param Architecture arch: Architecture to add labels for + :param int addr: the IL address to add a label at + """ + if arch is not None: + arch = arch.handle + core.BNAddLowLevelILLabelForAddress(self.handle, arch, addr) + + def get_label_for_address(self, arch, addr): + """ + ``get_label_for_address`` returns the LowLevelILLabel for the given Architecture ``arch`` and IL address ``addr``. + + :param Architecture arch: + :param int addr: IL Address label to retrieve + :return: the LowLevelILLabel for the given IL address + :rtype: LowLevelILLabel + """ + if arch is not None: + arch = arch.handle + label = core.BNGetLowLevelILLabelForAddress(self.handle, arch, addr) + if label is None: + return None + return LowLevelILLabel(label) + + +class LowLevelILBasicBlock(basicblock.BasicBlock): + def __init__(self, view, handle, owner): + super(LowLevelILBasicBlock, self).__init__(view, handle) + self.il_function = owner + + def __iter__(self): + for idx in xrange(self.start, self.end): + yield self.il_function[idx] + + def __getitem__(self, idx): + size = self.end - self.start + if idx > size or idx < -size: + raise IndexError("list index is out of range") + if idx >= 0: + return self.il_function[idx + self.start] + else: + return self.il_function[self.end + idx] + + +def LLIL_TEMP(n): + return n | 0x80000000 + + +def LLIL_REG_IS_TEMP(n): + return (n & 0x80000000) != 0 + + +def LLIL_GET_TEMP_REG_INDEX(n): + return n & 0x7fffffff diff --git a/python/mainthread.py b/python/mainthread.py new file mode 100644 index 00000000..220f0b64 --- /dev/null +++ b/python/mainthread.py @@ -0,0 +1,59 @@ +# 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. + +# Binary Ninja components +import _binaryninjacore as core +import scriptingprovider + + +def execute_on_main_thread(func): + action = scriptingprovider._ThreadActionContext(func) + obj = core.BNExecuteOnMainThread(0, action.callback) + if obj: + return scriptingprovider.MainThreadAction(obj) + return None + + +def execute_on_main_thread_and_wait(func): + action = scriptingprovider._ThreadActionContext(func) + core.BNExecuteOnMainThreadAndWait(0, action.callback) + + +def worker_enqueue(func): + action = scriptingprovider._ThreadActionContext(func) + core.BNWorkerEnqueue(0, action.callback) + + +def worker_priority_enqueue(func): + action = scriptingprovider._ThreadActionContext(func) + core.BNWorkerPriorityEnqueue(0, action.callback) + + +def worker_interactive_enqueue(func): + action = scriptingprovider._ThreadActionContext(func) + core.BNWorkerInteractiveEnqueue(0, action.callback) + + +def get_worker_thread_count(): + return core.BNGetWorkerThreadCount() + + +def set_worker_thread_count(count): + core.BNSetWorkerThreadCount(count) diff --git a/python/platform.py b/python/platform.py new file mode 100644 index 00000000..04dce587 --- /dev/null +++ b/python/platform.py @@ -0,0 +1,261 @@ +# 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 ctypes + +# Binary Ninja components +import _binaryninjacore as core +import startup +import architecture +import callingconvention + + +class _PlatformMetaClass(type): + @property + def list(self): + startup._init_plugins() + count = ctypes.c_ulonglong() + platforms = core.BNGetPlatformList(count) + result = [] + for i in xrange(0, count.value): + result.append(Platform(None, core.BNNewPlatformReference(platforms[i]))) + core.BNFreePlatformList(platforms, count.value) + return result + + @property + def os_list(self): + startup._init_plugins() + count = ctypes.c_ulonglong() + platforms = core.BNGetPlatformOSList(count) + result = [] + for i in xrange(0, count.value): + result.append(str(platforms[i])) + core.BNFreePlatformOSList(platforms, count.value) + return result + + def __iter__(self): + startup._init_plugins() + count = ctypes.c_ulonglong() + platforms = core.BNGetPlatformList(count) + try: + for i in xrange(0, count.value): + yield Platform(None, core.BNNewPlatformReference(platforms[i])) + finally: + core.BNFreePlatformList(platforms, count.value) + + def __setattr__(self, name, value): + try: + type.__setattr__(self, name, value) + except AttributeError: + raise AttributeError("attribute '%s' is read only" % name) + + def __getitem__(cls, value): + startup._init_plugins() + platform = core.BNGetPlatformByName(str(value)) + if platform is None: + raise KeyError("'%s' is not a valid platform" % str(value)) + return Platform(None, platform) + + def get_list(cls, os = None, arch = None): + startup._init_plugins() + count = ctypes.c_ulonglong() + if os is None: + platforms = core.BNGetPlatformList(count) + elif arch is None: + platforms = core.BNGetPlatformListByOS(os) + else: + platforms = core.BNGetPlatformListByArchitecture(os, arch.handle) + result = [] + for i in xrange(0, count.value): + result.append(Platform(None, core.BNNewPlatformReference(platforms[i]))) + core.BNFreePlatformList(platforms, count.value) + return result + + +class Platform(object): + """ + ``class Platform`` contains all information releated to the execution environment of the binary, mainly the + calling conventions used. + """ + __metaclass__ = _PlatformMetaClass + name = None + + def __init__(self, arch, handle = None): + if handle is None: + self.arch = arch + self.handle = core.BNCreatePlatform(arch.handle, self.__class__.name) + else: + self.handle = handle + self.__dict__["name"] = core.BNGetPlatformName(self.handle) + self.arch = architecture.Architecture(core.BNGetPlatformArchitecture(self.handle)) + + def __del__(self): + core.BNFreePlatform(self.handle) + + @property + def default_calling_convention(self): + """ + Default calling convention. + + :getter: returns a CallingConvention object for the default calling convention. + :setter: sets the default calling convention + :type: CallingConvention + """ + result = core.BNGetPlatformDefaultCallingConvention(self.handle) + if result is None: + return None + return callingconvention.CallingConvention(None, result) + + @default_calling_convention.setter + def default_calling_convention(self, value): + core.BNRegisterPlatformDefaultCallingConvention(self.handle, value.handle) + + @property + def cdecl_calling_convention(self): + """ + Cdecl calling convention. + + :getter: returns a CallingConvention object for the cdecl calling convention. + :setter sets the cdecl calling convention + :type: CallingConvention + """ + result = core.BNGetPlatformCdeclCallingConvention(self.handle) + if result is None: + return None + return callingconvention.CallingConvention(None, result) + + @cdecl_calling_convention.setter + def cdecl_calling_convention(self, value): + core.BNRegisterPlatformCdeclCallingConvention(self.handle, value.handle) + + @property + def stdcall_calling_convention(self): + """ + Stdcall calling convention. + + :getter: returns a CallingConvention object for the stdcall calling convention. + :setter sets the stdcall calling convention + :type: CallingConvention + """ + result = core.BNGetPlatformStdcallCallingConvention(self.handle) + if result is None: + return None + return callingconvention.CallingConvention(None, result) + + @stdcall_calling_convention.setter + def stdcall_calling_convention(self, value): + core.BNRegisterPlatformStdcallCallingConvention(self.handle, value.handle) + + @property + def fastcall_calling_convention(self): + """ + Fastcall calling convention. + + :getter: returns a CallingConvention object for the fastcall calling convention. + :setter sets the fastcall calling convention + :type: CallingConvention + """ + result = core.BNGetPlatformFastcallCallingConvention(self.handle) + if result is None: + return None + return callingconvention.CallingConvention(None, result) + + @fastcall_calling_convention.setter + def fastcall_calling_convention(self, value): + core.BNRegisterPlatformFastcallCallingConvention(self.handle, value.handle) + + @property + def system_call_convention(self): + """ + System call convention. + + :getter: returns a CallingConvention object for the system call convention. + :setter sets the system call convention + :type: CallingConvention + """ + result = core.BNGetPlatformSystemCallConvention(self.handle) + if result is None: + return None + return callingconvention.CallingConvention(None, result) + + @system_call_convention.setter + def system_call_convention(self, value): + core.BNSetPlatformSystemCallConvention(self.handle, value.handle) + + @property + def calling_conventions(self): + """ + List of platform CallingConvention objects (read-only) + + :getter: returns the list of supported CallingConvention objects + :type: list(CallingConvention) + """ + count = ctypes.c_ulonglong() + cc = core.BNGetPlatformCallingConventions(self.handle, count) + result = [] + for i in xrange(0, count.value): + result.append(callingconvention.CallingConvention(None, core.BNNewCallingConventionReference(cc[i]))) + core.BNFreeCallingConventionList(cc, count.value) + return result + + def __setattr__(self, name, value): + try: + object.__setattr__(self, name, value) + except AttributeError: + raise AttributeError("attribute '%s' is read only" % name) + + def __repr__(self): + return "<platform: %s>" % self.name + + def __str__(self): + return self.name + + def register(self, os): + """ + ``register`` registers the platform for given OS name. + + :param str os: OS name to register + :rtype: None + """ + core.BNRegisterPlatform(os, self.handle) + + def register_calling_convention(self, cc): + """ + ``register_calling_convention`` register a new calling convention. + + :param CallingConvention cc: a CallingConvention object to register + :rtype: None + """ + core.BNRegisterPlatformCallingConvention(self.handle, cc.handle) + + def get_related_platform(self, arch): + result = core.BNGetRelatedPlatform(self.handle, arch.handle) + if not result: + return None + return Platform(None, handle = result) + + def add_related_platform(self, arch, platform): + core.BNAddRelatedPlatform(self.handle, arch.handle, platform.handle) + + def get_associated_platform_by_address(self, addr): + new_addr = ctypes.c_ulonglong() + new_addr.value = addr + result = core.BNGetAssociatedPlatformByAddress(self.handle, new_addr) + return Platform(None, handle = result), new_addr.value diff --git a/python/plugin.py b/python/plugin.py new file mode 100644 index 00000000..2632b5a9 --- /dev/null +++ b/python/plugin.py @@ -0,0 +1,389 @@ +# 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 traceback +import ctypes +import threading + +# Binary Ninja components +import _binaryninjacore as core +from enums import PluginCommandType +import startup +import filemetadata +import binaryview +import function +import log + + +class PluginCommandContext(object): + def __init__(self, view): + self.view = view + self.address = 0 + self.length = 0 + self.function = None + + +class _PluginCommandMetaClass(type): + @property + def list(self): + startup._init_plugins() + count = ctypes.c_ulonglong() + commands = core.BNGetAllPluginCommands(count) + result = [] + for i in xrange(0, count.value): + result.append(PluginCommand(commands[i])) + core.BNFreePluginCommandList(commands) + return result + + def __iter__(self): + startup._init_plugins() + count = ctypes.c_ulonglong() + commands = core.BNGetAllPluginCommands(count) + try: + for i in xrange(0, count.value): + yield PluginCommand(commands[i]) + finally: + core.BNFreePluginCommandList(commands) + + def __setattr__(self, name, value): + try: + type.__setattr__(self, name, value) + except AttributeError: + raise AttributeError("attribute '%s' is read only" % name) + + +class PluginCommand(object): + _registered_commands = [] + __metaclass__ = _PluginCommandMetaClass + + def __init__(self, cmd): + self.command = core.BNPluginCommand() + ctypes.memmove(ctypes.byref(self.command), ctypes.byref(cmd), ctypes.sizeof(core.BNPluginCommand)) + self.name = str(cmd.name) + self.description = str(cmd.description) + self.type = PluginCommandType(cmd.type) + + @classmethod + def _default_action(cls, view, action): + try: + file_metadata = filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) + view_obj = binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) + action(view_obj) + except: + log.log_error(traceback.format_exc()) + + @classmethod + def _address_action(cls, view, addr, action): + try: + file_metadata = filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) + view_obj = binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) + action(view_obj, addr) + except: + log.log_error(traceback.format_exc()) + + @classmethod + def _range_action(cls, view, addr, length, action): + try: + file_metadata = filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) + view_obj = binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) + action(view_obj, addr, length) + except: + log.log_error(traceback.format_exc()) + + @classmethod + def _function_action(cls, view, func, action): + try: + file_metadata = filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) + view_obj = binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) + func_obj = function.Function(view_obj, core.BNNewFunctionReference(func)) + action(view_obj, func_obj) + except: + log.log_error(traceback.format_exc()) + + @classmethod + def _default_is_valid(cls, view, is_valid): + try: + if is_valid is None: + return True + file_metadata = filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) + view_obj = binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) + return is_valid(view_obj) + except: + log.log_error(traceback.format_exc()) + return False + + @classmethod + def _address_is_valid(cls, view, addr, is_valid): + try: + if is_valid is None: + return True + file_metadata = filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) + view_obj = binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) + return is_valid(view_obj, addr) + except: + log.log_error(traceback.format_exc()) + return False + + @classmethod + def _range_is_valid(cls, view, addr, length, is_valid): + try: + if is_valid is None: + return True + file_metadata = filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) + view_obj = binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) + return is_valid(view_obj, addr, length) + except: + log.log_error(traceback.format_exc()) + return False + + @classmethod + def _function_is_valid(cls, view, func, is_valid): + try: + if is_valid is None: + return True + file_metadata = filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) + view_obj = binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) + func_obj = function.Function(view_obj, core.BNNewFunctionReference(func)) + return is_valid(view_obj, func_obj) + except: + log.log_error(traceback.format_exc()) + return False + + @classmethod + def register(cls, name, description, action, is_valid = None): + startup._init_plugins() + action_obj = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView))(lambda ctxt, view: cls._default_action(view, action)) + is_valid_obj = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView))(lambda ctxt, view: cls._default_is_valid(view, is_valid)) + cls._registered_commands.append((action_obj, is_valid_obj)) + core.BNRegisterPluginCommand(name, description, action_obj, is_valid_obj, None) + + @classmethod + def register_for_address(cls, name, description, action, is_valid = None): + startup._init_plugins() + action_obj = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView), ctypes.c_ulonglong)(lambda ctxt, view, addr: cls._address_action(view, addr, action)) + is_valid_obj = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView), ctypes.c_ulonglong)(lambda ctxt, view, addr: cls._address_is_valid(view, addr, is_valid)) + cls._registered_commands.append((action_obj, is_valid_obj)) + core.BNRegisterPluginCommandForAddress(name, description, action_obj, is_valid_obj, None) + + @classmethod + def register_for_range(cls, name, description, action, is_valid = None): + startup._init_plugins() + action_obj = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView), ctypes.c_ulonglong, ctypes.c_ulonglong)(lambda ctxt, view, addr, length: cls._range_action(view, addr, length, action)) + is_valid_obj = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView), ctypes.c_ulonglong, ctypes.c_ulonglong)(lambda ctxt, view, addr, length: cls._range_is_valid(view, addr, length, is_valid)) + cls._registered_commands.append((action_obj, is_valid_obj)) + core.BNRegisterPluginCommandForRange(name, description, action_obj, is_valid_obj, None) + + @classmethod + def register_for_function(cls, name, description, action, is_valid = None): + startup._init_plugins() + action_obj = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView), ctypes.POINTER(core.BNFunction))(lambda ctxt, view, func: cls._function_action(view, func, action)) + is_valid_obj = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView), ctypes.POINTER(core.BNFunction))(lambda ctxt, view, func: cls._function_is_valid(view, func, is_valid)) + cls._registered_commands.append((action_obj, is_valid_obj)) + core.BNRegisterPluginCommandForFunction(name, description, action_obj, is_valid_obj, None) + + @classmethod + def get_valid_list(cls, context): + commands = cls.list + result = [] + for cmd in commands: + if cmd.is_valid(context): + result.append(cmd) + return result + + def is_valid(self, context): + if context.view is None: + return False + if self.command.type == PluginCommandType.DefaultPluginCommand: + if not self.command.defaultIsValid: + return True + return self.command.defaultIsValid(self.command.context, context.view.handle) + elif self.command.type == PluginCommandType.AddressPluginCommand: + if not self.command.addressIsValid: + return True + return self.command.addressIsValid(self.command.context, context.view.handle, context.address) + elif self.command.type == PluginCommandType.RangePluginCommand: + if context.length == 0: + return False + if not self.command.rangeIsValid: + return True + return self.command.rangeIsValid(self.command.context, context.view.handle, context.address, context.length) + elif self.command.type == PluginCommandType.FunctionPluginCommand: + if context.function is None: + return False + if not self.command.functionIsValid: + return True + return self.command.functionIsValid(self.command.context, context.view.handle, context.function.handle) + return False + + def execute(self, context): + if not self.is_valid(context): + return + if self.command.type == PluginCommandType.DefaultPluginCommand: + self.command.defaultCommand(self.command.context, context.view.handle) + elif self.command.type == PluginCommandType.AddressPluginCommand: + self.command.addressCommand(self.command.context, context.view.handle, context.address) + elif self.command.type == PluginCommandType.RangePluginCommand: + self.command.rangeCommand(self.command.context, context.view.handle, context.address, context.length) + elif self.command.type == PluginCommandType.FunctionPluginCommand: + self.command.functionCommand(self.command.context, context.view.handle, context.function.handle) + + def __repr__(self): + return "<PluginCommand: %s>" % self.name + + +class MainThreadAction(object): + def __init__(self, handle): + self.handle = handle + + def __del__(self): + core.BNFreeMainThreadAction(self.handle) + + def execute(self): + core.BNExecuteMainThreadAction(self.handle) + + @property + def done(self): + return core.BNIsMainThreadActionDone(self.handle) + + def wait(self): + core.BNWaitForMainThreadAction(self.handle) + + +class MainThreadActionHandler(object): + _main_thread = None + + def __init__(self): + self._cb = core.BNMainThreadCallbacks() + self._cb.context = 0 + self._cb.addAction = self._cb.addAction.__class__(self._add_action) + + def register(self): + self.__class__._main_thread = self + core.BNRegisterMainThread(self._cb) + + def _add_action(self, ctxt, action): + try: + self.add_action(MainThreadAction(action)) + except: + log.log_error(traceback.format_exc()) + + def add_action(self, action): + pass + + +class _BackgroundTaskMetaclass(type): + @property + def list(self): + """List all running background tasks (read-only)""" + count = ctypes.c_ulonglong() + tasks = core.BNGetRunningBackgroundTasks(count) + result = [] + for i in xrange(0, count.value): + result.append(BackgroundTask(core.BNNewBackgroundTaskReference(tasks[i]))) + core.BNFreeBackgroundTaskList(tasks) + return result + + def __iter__(self): + startup._init_plugins() + count = ctypes.c_ulonglong() + tasks = core.BNGetRunningBackgroundTasks(count) + try: + for i in xrange(0, count.value): + yield BackgroundTask(core.BNNewBackgroundTaskReference(tasks[i])) + finally: + core.BNFreeBackgroundTaskList(tasks) + + +class BackgroundTask(object): + __metaclass__ = _BackgroundTaskMetaclass + + def __init__(self, initial_progress_text = "", can_cancel = False, handle = None): + if handle is None: + self.handle = core.BNBeginBackgroundTask(initial_progress_text, can_cancel) + else: + self.handle = handle + + def __del__(self): + core.BNFreeBackgroundTask(self.handle) + + @property + def progress(self): + """Text description of the progress of the background task (displayed in status bar of the UI)""" + return core.BNGetBackgroundTaskProgressText(self.handle) + + @progress.setter + def progress(self, value): + core.BNSetBackgroundTaskProgressText(self.handle, str(value)) + + @property + def can_cancel(self): + """Whether the task can be cancelled (read-only)""" + return core.BNCanCancelBackgroundTask(self.handle) + + @property + def finished(self): + """Whether the task has finished""" + return core.BNIsBackgroundTaskFinished(self.handle) + + @finished.setter + def finished(self, value): + if value: + self.finish() + + def finish(self): + core.BNFinishBackgroundTask(self.handle) + + @property + def cancelled(self): + """Whether the task has been cancelled""" + return core.BNIsBackgroundTaskCancelled(self.handle) + + @cancelled.setter + def cancelled(self, value): + if value: + self.cancel() + + def cancel(self): + core.BNCancelBackgroundTask(self.handle) + + +class BackgroundTaskThread(BackgroundTask): + def __init__(self, initial_progress_text = "", can_cancel = False): + class _Thread(threading.Thread): + def __init__(self, task): + threading.Thread.__init__(self) + self.task = task + + def run(self): + self.task.run() + self.task.finish() + self.task = None + + BackgroundTask.__init__(self, initial_progress_text, can_cancel) + self.thread = _Thread(self) + + def run(self): + pass + + def start(self): + self.thread.start() + + def join(self): + self.thread.join() diff --git a/python/scriptingprovider.py b/python/scriptingprovider.py new file mode 100644 index 00000000..71402b1b --- /dev/null +++ b/python/scriptingprovider.py @@ -0,0 +1,645 @@ +# 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 code +import traceback +import ctypes +import threading +import abc +import sys + +# Binary Ninja Components +import _binaryninjacore as core +from enums import ScriptingProviderExecuteResult, ScriptingProviderInputReadyState +import binaryview +import function +import basicblock +import startup +import log + +_output_to_log = False + + +class _ThreadActionContext(object): + _actions = [] + + def __init__(self, func): + self.func = func + self.interpreter = None + if "value" in dir(PythonScriptingInstance._interpreter): + self.interpreter = PythonScriptingInstance._interpreter.value + self.__class__._actions.append(self) + self.callback = ctypes.CFUNCTYPE(None, ctypes.c_void_p)(lambda ctxt: self.execute()) + + def execute(self): + old_interpreter = None + if "value" in dir(PythonScriptingInstance._interpreter): + old_interpreter = PythonScriptingInstance._interpreter.value + PythonScriptingInstance._interpreter.value = self.interpreter + try: + self.func() + finally: + PythonScriptingInstance._interpreter.value = old_interpreter + self.__class__._actions.remove(self) + + +class ScriptingOutputListener(object): + def _register(self, handle): + self._cb = core.BNScriptingOutputListener() + self._cb.context = 0 + self._cb.output = self._cb.output.__class__(self._output) + self._cb.error = self._cb.error.__class__(self._error) + self._cb.inputReadyStateChanged = self._cb.inputReadyStateChanged.__class__(self._input_ready_state_changed) + core.BNRegisterScriptingInstanceOutputListener(handle, self._cb) + + def _unregister(self, handle): + core.BNUnregisterScriptingInstanceOutputListener(handle, self._cb) + + def _output(self, ctxt, text): + try: + self.notify_output(text) + except: + log.log_error(traceback.format_exc()) + + def _error(self, ctxt, text): + try: + self.notify_error(text) + except: + log.log_error(traceback.format_exc()) + + def _input_ready_state_changed(self, ctxt, state): + try: + self.notify_input_ready_state_changed(state) + except: + log.log_error(traceback.format_exc()) + + def notify_output(self, text): + pass + + def notify_error(self, text): + pass + + def notify_input_ready_state_changed(self, state): + pass + + +class ScriptingInstance(object): + def __init__(self, provider, handle = None): + if handle is None: + self._cb = core.BNScriptingInstanceCallbacks() + self._cb.context = 0 + self._cb.destroyInstance = self._cb.destroyInstance.__class__(self._destroy_instance) + self._cb.executeScriptInput = self._cb.executeScriptInput.__class__(self._execute_script_input) + self._cb.setCurrentBinaryView = self._cb.setCurrentBinaryView.__class__(self._set_current_binary_view) + self._cb.setCurrentFunction = self._cb.setCurrentFunction.__class__(self._set_current_function) + self._cb.setCurrentBasicBlock = self._cb.setCurrentBasicBlock.__class__(self._set_current_basic_block) + self._cb.setCurrentAddress = self._cb.setCurrentAddress.__class__(self._set_current_address) + self._cb.setCurrentSelection = self._cb.setCurrentSelection.__class__(self._set_current_selection) + self.handle = core.BNInitScriptingInstance(provider.handle, self._cb) + else: + self.handle = core.handle_of_type(handle, core.BNScriptingInstance) + self.listeners = [] + + def __del__(self): + core.BNFreeScriptingInstance(self.handle) + + def _destroy_instance(self, ctxt): + try: + self.perform_destroy_instance() + except: + log.log_error(traceback.format_exc()) + + def _execute_script_input(self, ctxt, text): + try: + return self.perform_execute_script_input(text) + except: + log.log_error(traceback.format_exc()) + return ScriptingProviderExecuteResult.InvalidScriptInput + + def _set_current_binary_view(self, ctxt, view): + try: + if view: + view = binaryview.BinaryView(handle = core.BNNewViewReference(view)) + else: + view = None + self.perform_set_current_binary_view(view) + except: + log.log_error(traceback.format_exc()) + + def _set_current_function(self, ctxt, func): + try: + if func: + func = function.Function(binaryview.BinaryView(handle = core.BNGetFunctionData(func)), core.BNNewFunctionReference(func)) + else: + func = None + self.perform_set_current_function(func) + except: + log.log.log_error(traceback.format_exc()) + + def _set_current_basic_block(self, ctxt, block): + try: + if block: + func = core.BNGetBasicBlockFunction(block) + if func is None: + block = None + else: + block = basicblock.BasicBlock(binaryview.BinaryView(handle = core.BNGetFunctionData(func)), core.BNNewBasicBlockReference(block)) + core.BNFreeFunction(func) + else: + block = None + self.perform_set_current_basic_block(block) + except: + log.log_error(traceback.format_exc()) + + def _set_current_address(self, ctxt, addr): + try: + self.perform_set_current_address(addr) + except: + log.log_error(traceback.format_exc()) + + def _set_current_selection(self, ctxt, begin, end): + try: + self.perform_set_current_selection(begin, end) + except: + log.log_error(traceback.format_exc()) + + @abc.abstractmethod + def perform_destroy_instance(self): + raise NotImplementedError + + @abc.abstractmethod + def perform_execute_script_input(self, text): + return ScriptingProviderExecuteResult.InvalidScriptInput + + @abc.abstractmethod + def perform_set_current_binary_view(self, view): + raise NotImplementedError + + @abc.abstractmethod + def perform_set_current_function(self, func): + raise NotImplementedError + + @abc.abstractmethod + def perform_set_current_basic_block(self, block): + raise NotImplementedError + + @abc.abstractmethod + def perform_set_current_address(self, addr): + raise NotImplementedError + + @abc.abstractmethod + def perform_set_current_selection(self, begin, end): + raise NotImplementedError + + @property + def input_ready_state(self): + return core.BNGetScriptingInstanceInputReadyState(self.handle) + + @input_ready_state.setter + def input_ready_state(self, value): + core.BNNotifyInputReadyStateForScriptingInstance(self.handle, value.value) + + def output(self, text): + core.BNNotifyOutputForScriptingInstance(self.handle, text) + + def error(self, text): + core.BNNotifyErrorForScriptingInstance(self.handle, text) + + def execute_script_input(self, text): + return core.BNExecuteScriptInput(self.handle, text) + + def set_current_binary_view(self, view): + if view is not None: + view = view.handle + core.BNSetScriptingInstanceCurrentBinaryView(self.handle, view) + + def set_current_function(self, func): + if func is not None: + func = func.handle + core.BNSetScriptingInstanceCurrentFunction(self.handle, func) + + def set_current_basic_block(self, block): + if block is not None: + block = block.handle + core.BNSetScriptingInstanceCurrentBasicBlock(self.handle, block) + + def set_current_address(self, addr): + core.BNSetScriptingInstanceCurrentAddress(self.handle, addr) + + def set_current_selection(self, begin, end): + core.BNSetScriptingInstanceCurrentSelection(self.handle, begin, end) + + def register_output_listener(self, listener): + listener._register(self.handle) + self.listeners.append(listener) + + def unregister_output_listener(self, listener): + if listener in self.listeners: + listener._unregister(self.handle) + self.listeners.remove(listener) + + +class _ScriptingProviderMetaclass(type): + @property + def list(self): + """List all ScriptingProvider types (read-only)""" + startup._init_plugins() + count = ctypes.c_ulonglong() + types = core.BNGetScriptingProviderList(count) + result = [] + for i in xrange(0, count.value): + result.append(ScriptingProvider(types[i])) + core.BNFreeScriptingProviderList(types) + return result + + def __iter__(self): + startup._init_plugins() + count = ctypes.c_ulonglong() + types = core.BNGetScriptingProviderList(count) + try: + for i in xrange(0, count.value): + yield ScriptingProvider(types[i]) + finally: + core.BNFreeScriptingProviderList(types) + + def __getitem__(self, value): + startup._init_plugins() + provider = core.BNGetScriptingProviderByName(str(value)) + if provider is None: + raise KeyError("'%s' is not a valid scripting provider" % str(value)) + return ScriptingProvider(provider) + + def __setattr__(self, name, value): + try: + type.__setattr__(self, name, value) + except AttributeError: + raise AttributeError("attribute '%s' is read only" % name) + + +class ScriptingProvider(object): + __metaclass__ = _ScriptingProviderMetaclass + + name = None + instance_class = None + _registered_providers = [] + + def __init__(self, handle = None): + if handle is not None: + self.handle = core.handle_of_type(handle, core.BNScriptingProvider) + self.__dict__["name"] = core.BNGetScriptingProviderName(handle) + + def register(self): + self._cb = core.BNScriptingProviderCallbacks() + self._cb.context = 0 + self._cb.createInstance = self._cb.createInstance.__class__(self._create_instance) + self.handle = core.BNRegisterScriptingProvider(self.__class__.name, self._cb) + self.__class__._registered_providers.append(self) + + def _create_instance(self, ctxt): + try: + result = self.__class__.instance_class(self) + if result is None: + return None + return ctypes.cast(core.BNNewScriptingInstanceReference(result.handle), ctypes.c_void_p).value + except: + log.log_error(traceback.format_exc()) + return None + + def create_instance(self): + result = core.BNCreateScriptingProviderInstance(self.handle) + if result is None: + return None + return ScriptingInstance(self, handle = result) + + +class _PythonScriptingInstanceOutput(object): + def __init__(self, orig, is_error): + self.orig = orig + self.is_error = is_error + self.buffer = "" + self.encoding = 'UTF-8' + self.errors = None + self.isatty = False + self.mode = 'w' + self.name = 'PythonScriptingInstanceOutput' + self.newlines = None + + def close(self): + pass + + def closed(self): + return False + + def flush(self): + pass + + def next(self): + raise IOError("File not open for reading") + + def read(self): + raise IOError("File not open for reading") + + def readinto(self): + raise IOError("File not open for reading") + + def readlines(self): + raise IOError("File not open for reading") + + def seek(self): + pass + + def sofspace(self): + return 0 + + def truncate(self): + pass + + def tell(self): + return self.orig.tell() + + def writelines(self, lines): + return self.write('\n'.join(lines)) + + def write(self, data): + global _output_to_log + + interpreter = None + if "value" in dir(PythonScriptingInstance._interpreter): + interpreter = PythonScriptingInstance._interpreter.value + + if interpreter is None: + if _output_to_log: + self.buffer += data + while True: + i = self.buffer.find('\n') + if i == -1: + break + line = self.buffer[:i] + self.buffer = self.buffer[i + 1:] + + if self.is_error: + log.log_error(line) + else: + log.log_info(line) + else: + self.orig.write(data) + else: + PythonScriptingInstance._interpreter.value = None + try: + if self.is_error: + interpreter.instance.error(data) + else: + interpreter.instance.output(data) + finally: + PythonScriptingInstance._interpreter.value = interpreter + + +class _PythonScriptingInstanceInput(object): + def __init__(self, orig): + self.orig = orig + + def read(self, size): + interpreter = None + if "value" in dir(PythonScriptingInstance._interpreter): + interpreter = PythonScriptingInstance._interpreter.value + + if interpreter is None: + return self.orig.read(size) + else: + PythonScriptingInstance._interpreter.value = None + try: + result = interpreter.read(size) + finally: + PythonScriptingInstance._interpreter.value = interpreter + return result + + def readline(self): + interpreter = None + if "value" in dir(PythonScriptingInstance._interpreter): + interpreter = PythonScriptingInstance._interpreter.value + + if interpreter is None: + return self.orig.readline() + else: + result = "" + while True: + data = interpreter.read(1) + result += data + if (len(data) == 0) or (data == "\n"): + break + return result + + +class PythonScriptingInstance(ScriptingInstance): + _interpreter = threading.local() + + class InterpreterThread(threading.Thread): + def __init__(self, instance): + super(PythonScriptingInstance.InterpreterThread, self).__init__() + self.instance = instance + self.locals = {"__name__": "__console__", "__doc__": None, "binaryninja": sys.modules[__name__]} + self.interpreter = code.InteractiveInterpreter(self.locals) + self.event = threading.Event() + self.daemon = True + + # Latest selections from UI + self.current_view = None + self.current_func = None + self.current_block = None + self.current_addr = 0 + self.current_selection_begin = 0 + self.current_selection_end = 0 + + # Selections that were current as of last issued command + self.active_view = None + self.active_func = None + self.active_block = None + self.active_addr = 0 + self.active_selection_begin = 0 + self.active_selection_end = 0 + + self.locals["get_selected_data"] = self.get_selected_data + self.locals["write_at_cursor"] = self.write_at_cursor + + self.exit = False + self.code = None + self.input = "" + + self.interpreter.runsource("from binaryninja import *\n") + + def execute(self, code): + self.code = code + self.event.set() + + def add_input(self, data): + self.input += data + self.event.set() + + def end(self): + self.exit = True + self.event.set() + + def read(self, size): + while not self.exit: + if len(self.input) > size: + result = self.input[:size] + self.input = self.input[size:] + return result + elif len(self.input) > 0: + result = self.input + self.input = "" + return result + self.instance.input_ready_state = ScriptingProviderInputReadyState.ReadyForScriptProgramInput + self.event.wait() + self.event.clear() + return "" + + def run(self): + while not self.exit: + self.event.wait() + self.event.clear() + if self.exit: + break + if self.code is not None: + self.instance.input_ready_state = ScriptingProviderInputReadyState.NotReadyForInput + code = self.code + self.code = None + + PythonScriptingInstance._interpreter.value = self + try: + self.active_view = self.current_view + self.active_func = self.current_func + self.active_block = self.current_block + self.active_addr = self.current_addr + self.active_selection_begin = self.current_selection_begin + self.active_selection_end = self.current_selection_end + + self.locals["current_view"] = self.active_view + self.locals["bv"] = self.active_view + self.locals["current_function"] = self.active_func + self.locals["current_basic_block"] = self.active_block + self.locals["current_address"] = self.active_addr + self.locals["here"] = self.active_addr + self.locals["current_selection"] = (self.active_selection_begin, self.active_selection_end) + + self.interpreter.runsource(code) + + if self.locals["here"] != self.active_addr: + if not self.active_view.file.navigate(self.active_view.file.view, self.locals["here"]): + sys.stderr.write("Address 0x%x is not valid for the current view\n" % self.locals["here"]) + elif self.locals["current_address"] != self.active_addr: + if not self.active_view.file.navigate(self.active_view.file.view, self.locals["current_address"]): + sys.stderr.write("Address 0x%x is not valid for the current view\n" % self.locals["current_address"]) + except: + traceback.print_exc() + finally: + PythonScriptingInstance._interpreter.value = None + self.instance.input_ready_state = ScriptingProviderInputReadyState.ReadyForScriptExecution + + def get_selected_data(self): + if self.active_view is None: + return None + length = self.active_selection_end - self.active_selection_begin + return self.active_view.read(self.active_selection_begin, length) + + def write_at_cursor(self, data): + if self.active_view is None: + return 0 + selected_length = self.active_selection_end - self.active_selection_begin + data = str(data) + if (len(data) == selected_length) or (selected_length == 0): + return self.active_view.write(self.active_selection_begin, data) + else: + self.active_view.remove(self.active_selection_begin, selected_length) + return self.active_view.insert(self.active_selection_begin, data) + + def __init__(self, provider): + super(PythonScriptingInstance, self).__init__(provider) + self.interpreter = PythonScriptingInstance.InterpreterThread(self) + self.interpreter.start() + self.queued_input = "" + self.input_ready_state = ScriptingProviderInputReadyState.ReadyForScriptExecution + + @abc.abstractmethod + def perform_destroy_instance(self): + self.interpreter.end() + + @abc.abstractmethod + def perform_execute_script_input(self, text): + if self.input_ready_state == ScriptingProviderInputReadyState.NotReadyForInput: + return ScriptingProviderExecuteResult.InvalidScriptInput + + if self.input_ready_state == ScriptingProviderInputReadyState.ReadyForScriptProgramInput: + if len(text) == 0: + return ScriptingProviderExecuteResult.SuccessfulScriptExecution + self.input_ready_state = ScriptingProviderInputReadyState.NotReadyForInput + self.interpreter.add_input(text) + return ScriptingProviderExecuteResult.SuccessfulScriptExecution + + try: + result = code.compile_command(text) + except: + result = False + + if result is None: + # Command is not complete, ask for more input + return ScriptingProviderExecuteResult.IncompleteScriptInput + + self.input_ready_state = ScriptingProviderInputReadyState.NotReadyForInput + self.interpreter.execute(text) + return ScriptingProviderExecuteResult.SuccessfulScriptExecution + + @abc.abstractmethod + def perform_set_current_binary_view(self, view): + self.interpreter.current_view = view + + @abc.abstractmethod + def perform_set_current_function(self, func): + self.interpreter.current_func = func + + @abc.abstractmethod + def perform_set_current_basic_block(self, block): + self.interpreter.current_block = block + + @abc.abstractmethod + def perform_set_current_address(self, addr): + self.interpreter.current_addr = addr + + @abc.abstractmethod + def perform_set_current_selection(self, begin, end): + self.interpreter.current_selection_begin = begin + self.interpreter.current_selection_end = end + + +class PythonScriptingProvider(ScriptingProvider): + name = "Python" + instance_class = PythonScriptingInstance + + +PythonScriptingProvider().register() +# Wrap stdin/stdout/stderr for Python scripting provider implementation +original_stdin = sys.stdin +original_stdout = sys.stdout +original_stderr = sys.stderr + +sys.stdin = _PythonScriptingInstanceInput(sys.stdin) +sys.stdout = _PythonScriptingInstanceOutput(sys.stdout, False) +sys.stderr = _PythonScriptingInstanceOutput(sys.stderr, True) diff --git a/python/startup.py b/python/startup.py new file mode 100644 index 00000000..809f185b --- /dev/null +++ b/python/startup.py @@ -0,0 +1,34 @@ +# 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 _binaryninjacore as core + + +_plugin_init = False + + +def _init_plugins(): + global _plugin_init + if not _plugin_init: + _plugin_init = True + core.BNInitCorePlugins() + core.BNInitUserPlugins() + if not core.BNIsLicenseValidated(): + raise RuntimeError("License is not valid. Please supply a valid license.") diff --git a/python/transform.py b/python/transform.py new file mode 100644 index 00000000..0c003738 --- /dev/null +++ b/python/transform.py @@ -0,0 +1,227 @@ +# 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 traceback +import ctypes +import abc + +# Binary Ninja components +import _binaryninjacore as core +from enums import TransformType +import startup +import log +import databuffer + + +class _TransformMetaClass(type): + @property + def list(self): + startup._init_plugins() + count = ctypes.c_ulonglong() + xforms = core.BNGetTransformTypeList(count) + result = [] + for i in xrange(0, count.value): + result.append(Transform(xforms[i])) + core.BNFreeTransformTypeList(xforms) + return result + + def __iter__(self): + startup._init_plugins() + count = ctypes.c_ulonglong() + xforms = core.BNGetTransformTypeList(count) + try: + for i in xrange(0, count.value): + yield Transform(xforms[i]) + finally: + core.BNFreeTransformTypeList(xforms) + + def __setattr__(self, name, value): + try: + type.__setattr__(self, name, value) + except AttributeError: + raise AttributeError("attribute '%s' is read only" % name) + + def __getitem__(cls, name): + startup._init_plugins() + xform = core.BNGetTransformByName(name) + if xform is None: + raise KeyError("'%s' is not a valid transform" % str(name)) + return Transform(xform) + + def register(cls): + startup._init_plugins() + if cls.name is None: + raise ValueError("transform 'name' is not defined") + if cls.long_name is None: + cls.long_name = cls.name + if cls.transform_type is None: + raise ValueError("transform 'transform_type' is not defined") + if cls.group is None: + cls.group = "" + xform = cls(None) + cls._registered_cb = xform._cb + xform.handle = core.BNRegisterTransformType(cls.transform_type, cls.name, cls.long_name, cls.group, xform._cb) + + +class TransformParameter(object): + def __init__(self, name, long_name = None, fixed_length = 0): + self.name = name + if long_name is None: + self.long_name = name + else: + self.long_name = long_name + self.fixed_length = fixed_length + + +class Transform(object): + transform_type = None + name = None + long_name = None + group = None + parameters = [] + _registered_cb = None + __metaclass__ = _TransformMetaClass + + def __init__(self, handle): + if handle is None: + self._cb = core.BNCustomTransform() + self._cb.context = 0 + self._cb.getParameters = self._cb.getParameters.__class__(self._get_parameters) + self._cb.freeParameters = self._cb.freeParameters.__class__(self._free_parameters) + self._cb.decode = self._cb.decode.__class__(self._decode) + self._cb.encode = self._cb.encode.__class__(self._encode) + self._pending_param_lists = {} + self.type = self.__class__.transform_type + if not isinstance(self.type, str): + self.type = TransformType(self.type) + self.name = self.__class__.name + self.long_name = self.__class__.long_name + self.group = self.__class__.group + self.parameters = self.__class__.parameters + else: + self.handle = handle + self.type = TransformType(core.BNGetTransformType(self.handle)) + self.name = core.BNGetTransformName(self.handle) + self.long_name = core.BNGetTransformLongName(self.handle) + self.group = core.BNGetTransformGroup(self.handle) + count = ctypes.c_ulonglong() + params = core.BNGetTransformParameterList(self.handle, count) + self.parameters = [] + for i in xrange(0, count.value): + self.parameters.append(TransformParameter(params[i].name, params[i].longName, params[i].fixedLength)) + core.BNFreeTransformParameterList(params, count.value) + + def __repr__(self): + return "<transform: %s>" % self.name + + def _get_parameters(self, ctxt, count): + try: + count[0] = len(self.parameters) + param_buf = (core.BNTransformParameterInfo * len(self.parameters))() + for i in xrange(0, len(self.parameters)): + param_buf[i].name = self.parameters[i].name + param_buf[i].longName = self.parameters[i].long_name + param_buf[i].fixedLength = self.parameters[i].fixed_length + result = ctypes.cast(param_buf, ctypes.c_void_p) + self._pending_param_lists[result.value] = (result, param_buf) + return result.value + except: + log.log_error(traceback.format_exc()) + count[0] = 0 + return None + + def _free_parameters(self, params, count): + try: + buf = ctypes.cast(params, ctypes.c_void_p) + if buf.value not in self._pending_param_lists: + raise ValueError("freeing parameter list that wasn't allocated") + del self._pending_param_lists[buf.value] + except: + log.log_error(traceback.format_exc()) + + def _decode(self, ctxt, input_buf, output_buf, params, count): + try: + input_obj = databuffer.DataBuffer(handle = core.BNDuplicateDataBuffer(input_buf)) + param_map = {} + for i in xrange(0, count): + data = databuffer.DataBuffer(handle = core.BNDuplicateDataBuffer(params[i].value)) + param_map[params[i].name] = str(data) + result = self.perform_decode(str(input_obj), param_map) + if result is None: + return False + result = str(result) + core.BNSetDataBufferContents(output_buf, result, len(result)) + return True + except: + log.log_error(traceback.format_exc()) + return False + + def _encode(self, ctxt, input_buf, output_buf, params, count): + try: + input_obj = databuffer.DataBuffer(handle = core.BNDuplicateDataBuffer(input_buf)) + param_map = {} + for i in xrange(0, count): + data = databuffer.DataBuffer(handle = core.BNDuplicateDataBuffer(params[i].value)) + param_map[params[i].name] = str(data) + result = self.perform_encode(str(input_obj), param_map) + if result is None: + return False + result = str(result) + core.BNSetDataBufferContents(output_buf, result, len(result)) + return True + except: + log.log_error(traceback.format_exc()) + return False + + @abc.abstractmethod + def perform_decode(self, data, params): + if self.type == TransformType.InvertingTransform: + return self.perform_encode(data, params) + return None + + @abc.abstractmethod + def perform_encode(self, data, params): + return None + + def decode(self, input_buf, params = {}): + input_buf = databuffer.DataBuffer(input_buf) + output_buf = databuffer.DataBuffer() + keys = params.keys() + param_buf = (core.BNTransformParameter * len(keys))() + for i in xrange(0, len(keys)): + data = databuffer.DataBuffer(params[keys[i]]) + param_buf[i].name = keys[i] + param_buf[i].value = data.handle + if not core.BNDecode(self.handle, input_buf.handle, output_buf.handle, param_buf, len(keys)): + return None + return str(output_buf) + + def encode(self, input_buf, params = {}): + input_buf = databuffer.DataBuffer(input_buf) + output_buf = databuffer.DataBuffer() + keys = params.keys() + param_buf = (core.BNTransformParameter * len(keys))() + for i in xrange(0, len(keys)): + data = databuffer.DataBuffer(params[keys[i]]) + param_buf[i].name = keys[i] + param_buf[i].value = data.handle + if not core.BNEncode(self.handle, input_buf.handle, output_buf.handle, param_buf, len(keys)): + return None + return str(output_buf) diff --git a/python/types.py b/python/types.py new file mode 100644 index 00000000..757f88aa --- /dev/null +++ b/python/types.py @@ -0,0 +1,610 @@ +# 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 ctypes + +# Binary Ninja components +import _binaryninjacore as core +from enums import SymbolType, TypeClass, NamedTypeReferenceClass +import callingconvention +import demangle + + +class QualifiedName(object): + def __init__(self, name = []): + if isinstance(name, str): + self.name = [name] + elif isinstance(name, QualifiedName): + self.name = name.name + else: + self.name = name + + def __str__(self): + return "::".join(self.name) + + def __repr__(self): + return repr(str(self)) + + def __len__(self): + return len(self.name) + + def __hash__(self): + return hash(str(self)) + + def __eq__(self, other): + if isinstance(other, str): + return str(self) == other + elif isinstance(other, list): + return self.name == other + elif isinstance(other, QualifiedName): + return self.name == other.name + return False + + def __ne__(self, other): + return not (self == other) + + def __lt__(self, other): + if isinstance(other, QualifiedName): + return self.name < other.name + return False + + def __le__(self, other): + if isinstance(other, QualifiedName): + return self.name <= other.name + return False + + def __gt__(self, other): + if isinstance(other, QualifiedName): + return self.name > other.name + return False + + def __ge__(self, other): + if isinstance(other, QualifiedName): + return self.name >= other.name + return False + + def __cmp__(self, other): + if self == other: + return 0 + if self < other: + return -1 + return 1 + + def __getitem__(self, key): + return self.name[key] + + def __iter__(self): + return iter(self.name) + + def _get_core_struct(self): + result = core.BNQualifiedName() + name_list = (ctypes.c_char_p * len(self.name))() + for i in xrange(0, len(self.name)): + name_list[i] = self.name[i] + result.name = name_list + result.nameCount = len(self.name) + return result + + @classmethod + def _from_core_struct(cls, name): + result = [] + for i in xrange(0, name.nameCount): + result.append(name.name[i]) + return QualifiedName(result) + + +class Symbol(object): + """ + Symbols are defined as one of the following types: + + =========================== ============================================================== + SymbolType Description + =========================== ============================================================== + FunctionSymbol Symbol for Function that exists in the current binary + ImportAddressSymbol Symbol defined in the Import Address Table + ImportedFunctionSymbol Symbol for Function that is not defined in the current binary + DataSymbol Symbol for Data in the current binary + ImportedDataSymbol Symbol for Data that is not defined in the current binary + =========================== ============================================================== + """ + def __init__(self, sym_type, addr, short_name, full_name = None, raw_name = None, handle = None): + if handle is not None: + self.handle = core.handle_of_type(handle, core.BNSymbol) + else: + if isinstance(sym_type, str): + sym_type = SymbolType[sym_type] + if full_name is None: + full_name = short_name + if raw_name is None: + raw_name = full_name + self.handle = core.BNCreateSymbol(sym_type, short_name, full_name, raw_name, addr) + + def __del__(self): + core.BNFreeSymbol(self.handle) + + @property + def type(self): + """Symbol type (read-only)""" + return SymbolType(core.BNGetSymbolType(self.handle)) + + @property + def name(self): + """Symbol name (read-only)""" + return core.BNGetSymbolRawName(self.handle) + + @property + def short_name(self): + """Symbol short name (read-only)""" + return core.BNGetSymbolShortName(self.handle) + + @property + def full_name(self): + """Symbol full name (read-only)""" + return core.BNGetSymbolFullName(self.handle) + + @property + def raw_name(self): + """Symbol raw name (read-only)""" + return core.BNGetSymbolRawName(self.handle) + + @property + def address(self): + """Symbol address (read-only)""" + return core.BNGetSymbolAddress(self.handle) + + @property + def auto(self): + return core.BNIsSymbolAutoDefined(self.handle) + + @auto.setter + def auto(self, value): + core.BNSetSymbolAutoDefined(self.handle, value) + + def __repr__(self): + return "<%s: \"%s\" @ %#x>" % (self.type, self.full_name, self.address) + + def __setattr__(self, name, value): + try: + object.__setattr__(self, name, value) + except AttributeError: + raise AttributeError("attribute '%s' is read only" % name) + + +class Type(object): + def __init__(self, handle): + self.handle = handle + + def __del__(self): + core.BNFreeType(self.handle) + + @property + def type_class(self): + """Type class (read-only)""" + return TypeClass(core.BNGetTypeClass(self.handle)) + + @property + def width(self): + """Type width (read-only)""" + return core.BNGetTypeWidth(self.handle) + + @property + def alignment(self): + """Type alignment (read-only)""" + return core.BNGetTypeAlignment(self.handle) + + @property + def signed(self): + """Wether type is signed (read-only)""" + return core.BNIsTypeSigned(self.handle) + + @property + def const(self): + """Whether type is const (read-only)""" + return core.BNIsTypeConst(self.handle) + + @property + def modified(self): + """Whether type is modified (read-only)""" + return core.BNIsTypeFloatingPoint(self.handle) + + @property + def target(self): + """Target (read-only)""" + result = core.BNGetChildType(self.handle) + if result is None: + return None + return Type(result) + + @property + def element_type(self): + """Target (read-only)""" + result = core.BNGetChildType(self.handle) + if result is None: + return None + return Type(result) + + @property + def return_value(self): + """Return value (read-only)""" + result = core.BNGetChildType(self.handle) + if result is None: + return None + return Type(result) + + @property + def calling_convention(self): + """Calling convention (read-only)""" + result = core.BNGetTypeCallingConvention(self.handle) + if result is None: + return None + return callingconvention.CallingConvention(None, result) + + @property + def parameters(self): + """Type parameters list (read-only)""" + count = ctypes.c_ulonglong() + params = core.BNGetTypeParameters(self.handle, count) + result = [] + for i in xrange(0, count.value): + result.append((Type(core.BNNewTypeReference(params[i].type)), params[i].name)) + core.BNFreeTypeParameterList(params, count.value) + return result + + @property + def has_variable_arguments(self): + """Whether type has variable arguments (read-only)""" + return core.BNTypeHasVariableArguments(self.handle) + + @property + def can_return(self): + """Whether type can return (read-only)""" + return core.BNFunctionTypeCanReturn(self.handle) + + @property + def structure(self): + """Structure of the type (read-only)""" + result = core.BNGetTypeStructure(self.handle) + if result is None: + return None + return Structure(result) + + @property + def enumeration(self): + """Type enumeration (read-only)""" + result = core.BNGetTypeEnumeration(self.handle) + if result is None: + return None + return Enumeration(result) + + @property + def named_type_reference(self): + """Reference to a named type (read-only)""" + result = core.BNGetTypeNamedTypeReference(self.handle) + if result is None: + return None + return NamedTypeReference(result) + + @property + def count(self): + """Type count (read-only)""" + return core.BNGetTypeElementCount(self.handle) + + def __str__(self): + return core.BNGetTypeString(self.handle) + + def __repr__(self): + return "<type: %s>" % str(self) + + def get_string_before_name(self): + return core.BNGetTypeStringBeforeName(self.handle) + + def get_string_after_name(self): + return core.BNGetTypeStringAfterName(self.handle) + + @classmethod + def void(cls): + return Type(core.BNCreateVoidType()) + + @classmethod + def bool(self): + return Type(core.BNCreateBoolType()) + + @classmethod + def int(self, width, sign = True, altname=""): + return Type(core.BNCreateIntegerType(width, sign, altname)) + + @classmethod + def float(self, width): + return Type(core.BNCreateFloatType(width)) + + @classmethod + def structure_type(self, structure_type): + return Type(core.BNCreateStructureType(structure_type.handle)) + + @classmethod + def named_type(self, named_type, width = 0, align = 1): + return Type(core.BNCreateNamedTypeReference(named_type.handle, width, align)) + + @classmethod + def named_type_from_type(self, name, t): + name = QualifiedName(name)._get_core_struct() + if t is not None: + t = t.handle + return Type(core.BNCreateNamedTypeReferenceFromType(name, t)) + + @classmethod + def enumeration_type(self, arch, e, width=None): + if width is None: + width = arch.default_int_size + return Type(core.BNCreateEnumerationType(e.handle, width)) + + @classmethod + def pointer(self, arch, t, const=False): + return Type(core.BNCreatePointerType(arch.handle, t.handle, const)) + + @classmethod + def array(self, t, count): + return Type(core.BNCreateArrayType(t.handle, count)) + + @classmethod + def function(self, ret, params, calling_convention=None, variable_arguments=False): + param_buf = (core.BNNameAndType * len(params))() + for i in xrange(0, len(params)): + if isinstance(params[i], Type): + param_buf[i].name = "" + param_buf[i].type = params[i].handle + else: + param_buf[i].name = params[i][1] + param_buf[i].type = params[i][0] + if calling_convention is not None: + calling_convention = calling_convention.handle + return Type(core.BNCreateFunctionType(ret.handle, calling_convention, param_buf, len(params), + variable_arguments)) + + def __setattr__(self, name, value): + try: + object.__setattr__(self, name, value) + except AttributeError: + raise AttributeError("attribute '%s' is read only" % name) + + +class NamedTypeReference(object): + def __init__(self, type_class = NamedTypeReferenceClass.UnknownNamedTypeClass, name = None, handle = None): + if handle is None: + self.handle = core.BNCreateNamedType() + core.BNSetTypeReferenceClass(self.handle, type_class) + if name is not None: + name = QualifiedName(name)._get_core_struct() + core.BNSetTypeReferenceName(self.handle, name) + else: + self.handle = handle + + def __del__(self): + core.BNFreeNamedTypeReference(self.handle) + + @property + def type_class(self): + return core.BNGetTypeReferenceClass(self.handle) + + @type_class.setter + def type_class(self, value): + core.BNSetTypeReferenceClass(self.handle, value) + + @property + def name(self): + count = ctypes.c_ulonglong() + name = core.BNGetTypeReferenceName(self.handle, count) + result = QualifiedName._from_core_struct(name) + core.BNFreeQualifiedName(name) + return result + + @name.setter + def name(self, value): + value = QualifiedName(value)._get_core_struct() + core.BNSetTypeReferenceName(self.handle, value) + + def __repr__(self): + if self.type_class == NamedTypeReferenceClass.TypedefNamedTypeClass: + return "<named type: typedef %s>" % str(self.name) + if self.type_class == NamedTypeReferenceClass.StructNamedTypeClass: + return "<named type: struct %s>" % str(self.name) + if self.type_class == NamedTypeReferenceClass.UnionNamedTypeClass: + return "<named type: union %s>" % str(self.name) + if self.type_class == NamedTypeReferenceClass.EnumNamedTypeClass: + return "<named type: enum %s>" % str(self.name) + return "<named type: unknown %s>" % str(self.name) + + +class StructureMember(object): + def __init__(self, t, name, offset): + self.type = t + self.name = name + self.offset = offset + + def __repr__(self): + if len(self.name) == 0: + return "<member: %s, offset %#x>" % (str(self.type), self.offset) + return "<%s %s%s, offset %#x>" % (self.type.get_string_before_name(), self.name, + self.type.get_string_after_name(), self.offset) + + +class Structure(object): + def __init__(self, handle=None): + if handle is None: + self.handle = core.BNCreateStructure() + else: + self.handle = handle + + def __del__(self): + core.BNFreeStructure(self.handle) + + @property + def members(self): + """Structure member list (read-only)""" + count = ctypes.c_ulonglong() + members = core.BNGetStructureMembers(self.handle, count) + result = [] + for i in xrange(0, count.value): + result.append(StructureMember(Type(core.BNNewTypeReference(members[i].type)), + members[i].name, members[i].offset)) + core.BNFreeStructureMemberList(members, count.value) + return result + + @property + def width(self): + """Structure width""" + return core.BNGetStructureWidth(self.handle) + + @width.setter + def width(self, new_width): + core.BNSetStructureWidth(self.handle, new_width) + + @property + def alignment(self): + """Structure alignment""" + return core.BNGetStructureAlignment(self.handle) + + @alignment.setter + def alignment(self, align): + core.BNSetStructureAlignment(self.handle, align) + + @property + def packed(self): + return core.BNIsStructurePacked(self.handle) + + @packed.setter + def packed(self, value): + core.BNSetStructurePacked(self.handle, value) + + @property + def union(self): + return core.BNIsStructureUnion(self.handle) + + @union.setter + def union(self, value): + core.BNSetStructureUnion(self.handle, value) + + def __setattr__(self, name, value): + try: + object.__setattr__(self, name, value) + except AttributeError: + raise AttributeError("attribute '%s' is read only" % name) + + def __repr__(self): + return "<struct: size %#x>" % self.width + + def append(self, t, name = ""): + core.BNAddStructureMember(self.handle, t.handle, name) + + def insert(self, offset, t, name = ""): + core.BNAddStructureMemberAtOffset(self.handle, t.handle, name, offset) + + def remove(self, i): + core.BNRemoveStructureMember(self.handle, i) + + +class EnumerationMember(object): + def __init__(self, name, value, default): + self.name = name + self.value = value + self.default = default + + def __repr__(self): + return "<%s = %#x>" % (self.name, self.value) + + +class Enumeration(object): + def __init__(self, handle=None): + if handle is None: + self.handle = core.BNCreateEnumeration() + else: + self.handle = handle + + def __del__(self): + core.BNFreeEnumeration(self.handle) + + @property + def members(self): + """Enumeration member list (read-only)""" + count = ctypes.c_ulonglong() + members = core.BNGetEnumerationMembers(self.handle, count) + result = [] + for i in xrange(0, count.value): + result.append(EnumerationMember(members[i].name, members[i].value, members[i].isDefault)) + core.BNFreeEnumerationMemberList(members, count.value) + return result + + def __setattr__(self, name, value): + try: + object.__setattr__(self, name, value) + except AttributeError: + raise AttributeError("attribute '%s' is read only" % name) + + def __repr__(self): + return "<enum: %s>" % repr(self.members) + + def append(self, name, value = None): + if value is None: + core.BNAddEnumerationMember(self.handle, name) + else: + core.BNAddEnumerationMemberWithValue(self.handle, name, value) + + +class TypeParserResult(object): + def __init__(self, types, variables, functions): + self.types = types + self.variables = variables + self.functions = functions + + def __repr__(self): + return "{types: %s, variables: %s, functions: %s}" % (self.types, self.variables, self.functions) + + +def preprocess_source(source, filename=None, include_dirs=[]): + """ + ``preprocess_source`` run the C preprocessor on the given source or source filename. + + :param str source: source to preprocess + :param str filename: optional filename to preprocess + :param list(str) include_dirs: list of string directorires to use as include directories. + :return: returns a tuple of (preprocessed_source, error_string) + :rtype: tuple(str,str) + :Example: + + >>> source = "#define TEN 10\\nint x[TEN];\\n" + >>> preprocess_source(source) + ('#line 1 "input"\\n\\n#line 2 "input"\\n int x [ 10 ] ;\\n', '') + >>> + """ + if filename is None: + filename = "input" + dir_buf = (ctypes.c_char_p * len(include_dirs))() + for i in xrange(0, len(include_dirs)): + dir_buf[i] = str(include_dirs[i]) + output = ctypes.c_char_p() + errors = ctypes.c_char_p() + result = core.BNPreprocessSource(source, filename, output, errors, dir_buf, len(include_dirs)) + output_str = output.value + error_str = errors.value + core.BNFreeString(ctypes.cast(output, ctypes.POINTER(ctypes.c_byte))) + core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) + if result: + return (output_str, error_str) + return (None, error_str) diff --git a/python/undoaction.py b/python/undoaction.py new file mode 100644 index 00000000..9f742e00 --- /dev/null +++ b/python/undoaction.py @@ -0,0 +1,99 @@ +# 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 traceback +import json +import ctypes + +# Binary Ninja components +import _binaryninjacore as core +from enums import ActionType +import startup +import log + + +class UndoAction(object): + name = None + action_type = None + _registered = False + _registered_cb = None + + def __init__(self, view): + self._cb = core.BNUndoAction() + if not self.__class__._registered: + raise TypeError("undo action type not registered") + action_type = self.__class__.action_type + if isinstance(action_type, str): + self._cb.type = ActionType[action_type] + else: + self._cb.type = action_type + self._cb.context = 0 + self._cb.undo = self._cb.undo.__class__(self._undo) + self._cb.redo = self._cb.redo.__class__(self._redo) + self._cb.serialize = self._cb.serialize.__class__(self._serialize) + self.view = view + + @classmethod + def register(cls): + startup._init_plugins() + if cls.name is None: + raise ValueError("undo action 'name' not defined") + if cls.action_type is None: + raise ValueError("undo action 'action_type' not defined") + cb_type = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.c_char_p, ctypes.POINTER(core.BNUndoAction)) + cls._registered_cb = cb_type(cls._deserialize) + core.BNRegisterUndoActionType(cls.name, 0, cls._registered_cb) + cls._registered = True + + @classmethod + def _deserialize(cls, ctxt, data, result): + try: + action = cls.deserialize(json.loads(data)) + if action is None: + return False + result.context = action._cb.context + result.undo = action._cb.undo + result.redo = action._cb.redo + result.serialize = action._cb.serialize + return True + except: + log.log_error(traceback.format_exc()) + return False + + def _undo(self, ctxt, view): + try: + self.undo() + except: + log.log_error(traceback.format_exc()) + return False + + def _redo(self, ctxt, view): + try: + self.redo() + except: + log.log_error(traceback.format_exc()) + return False + + def _serialize(self, ctxt): + try: + return json.dumps(self.serialize()) + except: + log.log_error(traceback.format_exc()) + return "null" diff --git a/python/update.py b/python/update.py new file mode 100644 index 00000000..7e2bd4ef --- /dev/null +++ b/python/update.py @@ -0,0 +1,242 @@ +# 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 traceback +import ctypes + +# Binary Ninja components +import _binaryninjacore as core +from enums import UpdateResult +import startup +import log + + +class _UpdateChannelMetaClass(type): + @property + def list(self): + startup._init_plugins() + count = ctypes.c_ulonglong() + errors = ctypes.c_char_p() + channels = core.BNGetUpdateChannels(count, errors) + if errors: + error_str = errors.value + core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) + raise IOError(error_str) + result = [] + for i in xrange(0, count.value): + result.append(UpdateChannel(channels[i].name, channels[i].description, channels[i].latestVersion)) + core.BNFreeUpdateChannelList(channels, count.value) + return result + + @property + def active(self): + return core.BNGetActiveUpdateChannel() + + @active.setter + def active(self, value): + return core.BNSetActiveUpdateChannel(value) + + def __iter__(self): + startup._init_plugins() + count = ctypes.c_ulonglong() + errors = ctypes.c_char_p() + channels = core.BNGetUpdateChannels(count, errors) + if errors: + error_str = errors.value + core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) + raise IOError(error_str) + try: + for i in xrange(0, count.value): + yield UpdateChannel(channels[i].name, channels[i].description, channels[i].latestVersion) + finally: + core.BNFreeUpdateChannelList(channels, count.value) + + def __setattr__(self, name, value): + try: + type.__setattr__(self, name, value) + except AttributeError: + raise AttributeError("attribute '%s' is read only" % name) + + def __getitem__(cls, name): + startup._init_plugins() + count = ctypes.c_ulonglong() + errors = ctypes.c_char_p() + channels = core.BNGetUpdateChannels(count, errors) + if errors: + error_str = errors.value + core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) + raise IOError(error_str) + result = None + for i in xrange(0, count.value): + if channels[i].name == str(name): + result = UpdateChannel(channels[i].name, channels[i].description, channels[i].latestVersion) + break + core.BNFreeUpdateChannelList(channels, count.value) + if result is None: + raise KeyError("'%s' is not a valid channel" % str(name)) + return result + + +class UpdateProgressCallback(object): + def __init__(self, func): + self.cb = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.c_ulonglong, ctypes.c_ulonglong)(self.callback) + self.func = func + + def callback(self, ctxt, progress, total): + try: + if self.func is not None: + return self.func(progress, total) + return True + except: + log.log_error(traceback.format_exc()) + + +class UpdateChannel(object): + __metaclass__ = _UpdateChannelMetaClass + + def __init__(self, name, desc, ver): + self.name = name + self.description = desc + self.latest_version_num = ver + + @property + def versions(self): + """List of versions (read-only)""" + count = ctypes.c_ulonglong() + errors = ctypes.c_char_p() + versions = core.BNGetUpdateChannelVersions(self.name, count, errors) + if errors: + error_str = errors.value + core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) + raise IOError(error_str) + result = [] + for i in xrange(0, count.value): + result.append(UpdateVersion(self, versions[i].version, versions[i].notes, versions[i].time)) + core.BNFreeUpdateChannelVersionList(versions, count.value) + return result + + @property + def latest_version(self): + """Latest version (read-only)""" + count = ctypes.c_ulonglong() + errors = ctypes.c_char_p() + versions = core.BNGetUpdateChannelVersions(self.name, count, errors) + if errors: + error_str = errors.value + core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) + raise IOError(error_str) + result = None + for i in xrange(0, count.value): + if versions[i].version == self.latest_version_num: + result = UpdateVersion(self, versions[i].version, versions[i].notes, versions[i].time) + break + core.BNFreeUpdateChannelVersionList(versions, count.value) + return result + + @property + def updates_available(self): + """Whether updates are available (read-only)""" + errors = ctypes.c_char_p() + result = core.BNAreUpdatesAvailable(self.name, errors) + if errors: + error_str = errors.value + core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) + raise IOError(error_str) + return result + + def __setattr__(self, name, value): + try: + object.__setattr__(self, name, value) + except AttributeError: + raise AttributeError("attribute '%s' is read only" % name) + + def __repr__(self): + return "<channel: %s>" % self.name + + def __str__(self): + return self.name + + def update_to_latest(self, progress = None): + cb = UpdateProgressCallback(progress) + errors = ctypes.c_char_p() + result = core.BNUpdateToLatestVersion(self.name, errors, cb.cb, None) + if errors: + error_str = errors.value + core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) + raise IOError(error_str) + return UpdateResult(result) + + +class UpdateVersion(object): + def __init__(self, channel, ver, notes, t): + self.channel = channel + self.version = ver + self.notes = notes + self.time = t + + def __repr__(self): + return "<version: %s>" % self.version + + def __str__(self): + return self.version + + def update(self, progress = None): + cb = UpdateProgressCallback(progress) + errors = ctypes.c_char_p() + result = core.BNUpdateToVersion(self.channel.name, self.version, errors, cb.cb, None) + if errors: + error_str = errors.value + core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) + raise IOError(error_str) + return UpdateResult(result) + + +def are_auto_updates_enabled(): + """ + ``are_auto_updates_enabled`` queries if auto updates are enabled. + + :return: boolean True if auto updates are enabled. False if they are disabled. + :rtype: bool + """ + return core.BNAreAutoUpdatesEnabled() + + +def set_auto_updates_enabled(enabled): + """ + ``set_auto_updates_enabled`` sets auto update enabled status. + + :param bool enabled: True to enable update, Flase to disable updates. + :rtype: None + """ + core.BNSetAutoUpdatesEnabled(enabled) + + +def get_time_since_last_update_check(): + """ + ``get_time_since_last_update_check`` returns the time stamp for the last time updates were checked. + + :return: time stacmp for last update check + :rtype: int + """ + return core.BNGetTimeSinceLastUpdateCheck() + + +def updates_checked(): + core.BNUpdatesChecked() |
