From 5e4cca1f1796bec109adacdb049d9e34c17656eb Mon Sep 17 00:00:00 2001 From: plafosse Date: Fri, 28 Oct 2016 20:16:37 -0400 Subject: Refactor python api into separate files and add Enumeration support. Also fixed bugs found with pyflakes --- python/filemetadata.py | 340 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 340 insertions(+) create mode 100644 python/filemetadata.py (limited to 'python/filemetadata.py') diff --git a/python/filemetadata.py b/python/filemetadata.py new file mode 100644 index 00000000..846c9f94 --- /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(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.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) -- cgit v1.3.1 From 842aba557f24ca9ab63f05cec6aa0c0efebd51b4 Mon Sep 17 00:00:00 2001 From: Peter LaFosse Date: Mon, 2 Jan 2017 15:19:47 -0500 Subject: Making platform and architecture optional parameters where possible --- python/architecture.py | 6 ++ python/basicblock.py | 47 +++++++++-- python/binaryview.py | 153 +++++++++++++++++++++-------------- python/examples/jump_table.py | 2 +- python/examples/print_syscalls.py | 2 +- python/filemetadata.py | 8 +- python/function.py | 162 ++++++++++++++++++++++++++++++++------ python/scriptingprovider.py | 43 ++++++++++ 8 files changed, 330 insertions(+), 93 deletions(-) (limited to 'python/filemetadata.py') diff --git a/python/architecture.py b/python/architecture.py index 0cdba6e5..5162e58a 100644 --- a/python/architecture.py +++ b/python/architecture.py @@ -1152,6 +1152,12 @@ class Architecture(object): 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 diff --git a/python/basicblock.py b/python/basicblock.py index 7abfdc94..d728cb8d 100644 --- a/python/basicblock.py +++ b/python/basicblock.py @@ -111,11 +111,25 @@ class BasicBlock(object): @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): - """Highlight color for basic block""" + """Gets or sets the highlight color for basic block + + :Example: + + >>> current_basic_block.highlight = core.BNHighlightStandardColor.BlueHighlightColor + >>> current_basic_block.highlight + + """ color = core.BNGetBasicBlockHighlight(self.handle) if color.style == core.BNHighlightColorStyle.StandardHighlightColor: return highlight.HighlightColor(color=color.color, alpha=color.alpha) @@ -162,6 +176,13 @@ class BasicBlock(object): 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 @@ -184,11 +205,27 @@ class BasicBlock(object): return result def set_auto_highlight(self, color): - if not isinstance(color, highlight.HighlightColor): - color = highlight.HighlightColor(color=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 core.BNHighlightStandardColor or highlight.HighlightColor color: Color value to use for highlighting + """ + if not isinstance(color, core.BNHighlightStandardColor) and not isinstance(color, highlight.HighlightColor): + raise ValueError("Specified color is not one of core.BNHighlightStandardColor, highlight.HighlightColor") core.BNSetAutoBasicBlockHighlight(self.handle, color._get_core_struct()) def set_user_highlight(self, color): - if not isinstance(color, highlight.HighlightColor): - color = highlight.HighlightColor(color=color) + """ + ``set_user_highlight`` highlights the current BasicBlock with the supplied color + + :param core.BNHighlightStandardColor 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(core.BNHighlightStandardColor.BlueHighlightColor) + """ + if not isinstance(color, core.BNHighlightStandardColor) and not isinstance(color, highlight.HighlightColor): + raise ValueError("Specified color is not one of core.BNHighlightStandardColor, highlight.HighlightColor") core.BNSetUserBasicBlockHighlight(self.handle, color._get_core_struct()) diff --git a/python/binaryview.py b/python/binaryview.py index e6e0ff61..7c85cd52 100644 --- a/python/binaryview.py +++ b/python/binaryview.py @@ -275,7 +275,7 @@ class BinaryViewType(object): @property def name(self): - """Binary View name (read-only)""" + """BinaryView name (read-only)""" return core.BNGetBinaryViewTypeName(self.handle) @property @@ -303,12 +303,21 @@ class BinaryViewType(object): """ ``get_view_of_file`` returns the first available, non-Raw `BinaryView` available. - :param str filename: Path to filename + :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 """ - view = BinaryView.open(filename) + 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: @@ -1381,7 +1390,7 @@ class BinaryView(object): >>> bv.get_disassembly(0x100012f1) 'xor eax, eax' >>> bv.begin_undo_actions() - >>> bv.convert_to_nop(bv.arch, 0x100012f1) + >>> bv.convert_to_nop(0x100012f1) True >>> bv.commit_undo_actions() >>> bv.get_disassembly(0x100012f1) @@ -1406,7 +1415,7 @@ class BinaryView(object): >>> bv.get_disassembly(0x100012f1) 'xor eax, eax' >>> bv.begin_undo_actions() - >>> bv.convert_to_nop(bv.arch, 0x100012f1) + >>> bv.convert_to_nop(0x100012f1) True >>> bv.commit_undo_actions() >>> bv.get_disassembly(0x100012f1) @@ -1428,7 +1437,7 @@ class BinaryView(object): >>> bv.get_disassembly(0x100012f1) 'xor eax, eax' >>> bv.begin_undo_actions() - >>> bv.convert_to_nop(bv.arch, 0x100012f1) + >>> bv.convert_to_nop(0x100012f1) True >>> bv.commit_undo_actions() >>> bv.get_disassembly(0x100012f1) @@ -1453,7 +1462,7 @@ class BinaryView(object): >>> bv.get_disassembly(0x100012f1) 'xor eax, eax' >>> bv.begin_undo_actions() - >>> bv.convert_to_nop(bv.arch, 0x100012f1) + >>> bv.convert_to_nop(0x100012f1) True >>> bv.commit_undo_actions() >>> bv.get_disassembly(0x100012f1) @@ -1624,33 +1633,37 @@ class BinaryView(object): self.notifications[notify]._unregister() del self.notifications[notify] - def add_function(self, plat, addr): + def add_function(self, addr, plat=None): """ ``add_function`` add a new function of the given ``plat`` at the virtual address ``addr`` - :param Platform plat: Platform for the function to be added :param int addr: virtual address of the function to be added + :param Platform plat: Platform for the function to be added :rtype: None :Example: - >>> bv.add_function(bv.plat, 1) + >>> bv.add_function(1) >>> bv.functions [] """ + if plat is None: + plat = self.platform core.BNAddFunctionForAnalysis(self.handle, plat.handle, addr) - def add_entry_point(self, plat, 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 Platform plat: Platform for the entry point analysis :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(bv.plat, 0xdeadbeef) + >>> bv.add_entry_point(0xdeadbeef) >>> """ + if plat is None: + plat = self.platform core.BNAddEntryPointForAnalysis(self.handle, plat.handle, addr) def remove_function(self, func): @@ -1669,20 +1682,22 @@ class BinaryView(object): """ core.BNRemoveAnalysisFunction(self.handle, func.handle) - def create_user_function(self, plat, addr): + 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 Platform plat: Platform for the function to be added :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(bv.plat, 1) + >>> bv.create_user_function(1) >>> bv.functions [] """ + if plat is None: + plat = self.platform core.BNCreateUserFunction(self.handle, plat.handle, addr) def remove_user_function(self, func): @@ -1832,20 +1847,22 @@ class BinaryView(object): return None return DataVariable(var.address, type.Type(var.type), var.autoDiscovered) - def get_function_at(self, plat, addr): + 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 binaryninja.Platform plat: plat of the desired function :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.plat, bv.entry_point) + >>> bv.get_function_at(bv.entry_point) >>> """ + if plat is None: + plat = self.platform func = core.BNGetAnalysisFunction(self.handle, plat.handle, addr) if func is None: return None @@ -2092,144 +2109,154 @@ class BinaryView(object): """ core.BNDefineImportedFunction(self.handle, import_addr_sym.handle, func.handle) - def is_never_branch_patch_available(self, arch, addr): + 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 Architecture arch: the architecture for the current view :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(bv.arch, 0x100012ed) + >>> bv.is_never_branch_patch_available(0x100012ed) False >>> bv.get_disassembly(0x100012ef) 'jg 0x100012f5' - >>> bv.is_never_branch_patch_available(bv.arch, 0x100012ef) + >>> 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, arch, 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 Architecture arch: the architecture for the current view :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(bv.arch, 0x100012ed) + >>> bv.is_always_branch_patch_available(0x100012ed) False >>> bv.get_disassembly(0x100012ef) 'jg 0x100012f5' - >>> bv.is_always_branch_patch_available(bv.arch, 0x100012ef) + >>> 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, arch, 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 Architecture arch: the architecture for the current view :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(bv.arch, 0x100012ed) + >>> bv.is_invert_branch_patch_available(0x100012ed) False >>> bv.get_disassembly(0x100012ef) 'jg 0x100012f5' - >>> bv.is_invert_branch_patch_available(bv.arch, 0x100012ef) + >>> 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, arch, 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 Architecture arch: the architecture for the current view :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(bv.arch, 0x100012f6) + >>> bv.is_skip_and_return_zero_patch_available(0x100012f6) False >>> bv.get_disassembly(0x100012fb) 'call 0x10001629' - >>> bv.is_skip_and_return_zero_patch_available(bv.arch, 0x100012fb) + >>> 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, arch, 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 Architecture arch: the architecture for the current view :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(bv.arch, 0x100012f6) + >>> bv.is_skip_and_return_value_patch_available(0x100012f6) False >>> bv.get_disassembly(0x100012fb) 'call 0x10001629' - >>> bv.is_skip_and_return_value_patch_available(bv.arch, 0x100012fb) + >>> 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, arch, 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 Architecture arch: architecture of the current BinaryView :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(bv.arch, 0x100012fb) + >>> 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: @@ -2246,9 +2273,11 @@ class BinaryView(object): >>> 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, arch, 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. @@ -2256,23 +2285,25 @@ class BinaryView(object): .. 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 + :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(bv.arch, 0x100012ef) + >>> 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, arch, 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. @@ -2280,23 +2311,25 @@ class BinaryView(object): .. 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 + :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(bv.arch, 0x1000130e) + >>> 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, arch, 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. @@ -2304,63 +2337,69 @@ class BinaryView(object): .. 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 + :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(bv.arch, 0x1000130e) + >>> 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, arch, addr, value): + 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 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* + :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(bv.arch, 0x1000132a, 42) + >>> 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, arch, addr): + 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 Architecture arch: architecture of the current binary view :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(bv.arch, 0x100012f1) + >>> 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): diff --git a/python/examples/jump_table.py b/python/examples/jump_table.py index 23531de0..0fd0dbab 100644 --- a/python/examples/jump_table.py +++ b/python/examples/jump_table.py @@ -77,7 +77,7 @@ 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/print_syscalls.py b/python/examples/print_syscalls.py index c3b47a8d..003b388e 100644 --- a/python/examples/print_syscalls.py +++ b/python/examples/print_syscalls.py @@ -43,7 +43,7 @@ def print_syscalls(bv): syscalls = (il for il in chain.from_iterable(func.low_level_il) if il.operation == core.BNLowLevelILOperation.LLIL_SYSCALL) for il in syscalls: - value = func.get_reg_value_at(bv.arch, il.address, register).value + value = func.get_reg_value_at(il.address, register).value print("System call address: {:#x} - {:d}".format(il.address, value)) diff --git a/python/filemetadata.py b/python/filemetadata.py index 846c9f94..f6593405 100644 --- a/python/filemetadata.py +++ b/python/filemetadata.py @@ -208,7 +208,7 @@ class FileMetadata(object): >>> bv.get_disassembly(0x100012f1) 'xor eax, eax' >>> bv.begin_undo_actions() - >>> bv.convert_to_nop(bv.arch, 0x100012f1) + >>> bv.convert_to_nop(0x100012f1) True >>> bv.commit_undo_actions() >>> bv.get_disassembly(0x100012f1) @@ -230,7 +230,7 @@ class FileMetadata(object): >>> bv.get_disassembly(0x100012f1) 'xor eax, eax' >>> bv.begin_undo_actions() - >>> bv.convert_to_nop(bv.arch, 0x100012f1) + >>> bv.convert_to_nop(0x100012f1) True >>> bv.commit_undo_actions() >>> bv.get_disassembly(0x100012f1) @@ -252,7 +252,7 @@ class FileMetadata(object): >>> bv.get_disassembly(0x100012f1) 'xor eax, eax' >>> bv.begin_undo_actions() - >>> bv.convert_to_nop(bv.arch, 0x100012f1) + >>> bv.convert_to_nop(0x100012f1) True >>> bv.commit_undo_actions() >>> bv.get_disassembly(0x100012f1) @@ -277,7 +277,7 @@ class FileMetadata(object): >>> bv.get_disassembly(0x100012f1) 'xor eax, eax' >>> bv.begin_undo_actions() - >>> bv.convert_to_nop(bv.arch, 0x100012f1) + >>> bv.convert_to_nop(0x100012f1) True >>> bv.commit_undo_actions() >>> bv.get_disassembly(0x100012f1) diff --git a/python/function.py b/python/function.py index 2910d283..7253f264 100644 --- a/python/function.py +++ b/python/function.py @@ -286,7 +286,7 @@ class Function(object): @property def function_type(self): - """Function type""" + """Function type object""" return bntype.Type(core.BNGetFunctionType(self.handle)) @function_type.setter @@ -358,10 +358,26 @@ class Function(object): def set_comment(self, addr, comment): core.BNSetCommentForAddress(self.handle, addr, comment) - def get_low_level_il_at(self, arch, addr): + 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, arch, 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 = [] @@ -370,7 +386,21 @@ class Function(object): core.BNFreeLowLevelILInstructionList(exits) return result - def get_reg_value_at(self, arch, addr, reg): + 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') + + """ + 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) @@ -378,7 +408,21 @@ class Function(object): core.BNFreeRegisterValue(value) return result - def get_reg_value_after(self, arch, addr, reg): + 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') + + """ + 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) @@ -386,11 +430,25 @@ class Function(object): core.BNFreeRegisterValue(value) return result - def get_reg_value_at_low_level_il_instruction(self, i, reg): + 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') + + """ + 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(self.arch, value) + result = RegisterValue(arch, value) core.BNFreeRegisterValue(value) return result @@ -402,13 +460,35 @@ class Function(object): core.BNFreeRegisterValue(value) return result - def get_stack_contents_at(self, arch, addr, offset, size): + 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) + + """ + 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, arch, addr, offset, size): + 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) @@ -426,7 +506,9 @@ class Function(object): core.BNFreeRegisterValue(value) return result - def get_parameter_at(self, arch, addr, func_type, i): + 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) @@ -442,7 +524,9 @@ class Function(object): core.BNFreeRegisterValue(value) return result - def get_regs_read_by(self, arch, addr): + 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 = [] @@ -451,7 +535,9 @@ class Function(object): core.BNFreeRegisterList(regs) return result - def get_regs_written_by(self, arch, addr): + 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 = [] @@ -460,7 +546,9 @@ class Function(object): core.BNFreeRegisterList(regs) return result - def get_stack_vars_referenced_by(self, arch, addr): + 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 = [] @@ -470,7 +558,9 @@ class Function(object): core.BNFreeStackVariableReferenceList(refs, count.value) return result - def get_constants_referenced_by(self, arch, addr): + 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 = [] @@ -479,7 +569,9 @@ class Function(object): core.BNFreeConstantReferenceList(refs) return result - def get_lifted_il_at(self, arch, addr): + 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): @@ -531,21 +623,27 @@ class Function(object): 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): + 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_arch, source, 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, arch, addr): + 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 = [] @@ -554,7 +652,9 @@ class Function(object): core.BNFreeIndirectBranchList(branches) return result - def get_block_annotations(self, arch, addr): + 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 = [] @@ -577,10 +677,14 @@ class Function(object): def set_user_type(self, value): core.BNSetFunctionUserType(self.handle, value.handle) - def get_int_display_type(self, arch, instr_addr, value, operand): + def get_int_display_type(self, instr_addr, value, operand, arch=None): + if arch is None: + arch = self.arch return core.BNGetIntegerConstantDisplayType(self.handle, arch.handle, instr_addr, value, operand) - def set_int_display_type(self, arch, instr_addr, value, operand, display_type): + def set_int_display_type(self, instr_addr, value, operand, display_type, arch=None): + if arch is None: + arch = self.arch if isinstance(display_type, str): display_type = core.BNIntegerDisplayType[display_type] core.BNSetIntegerConstantDisplayType(self.handle, arch.handle, instr_addr, value, operand, display_type) @@ -601,13 +705,17 @@ class Function(object): core.BNReleaseAdvancedFunctionAnalysisData(self.handle) self._advanced_analysis_requests -= 1 - def get_basic_block_at(self, arch, addr): + def get_basic_block_at(self, addr, arch=None): + 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, arch, addr): + def get_instr_highlight(self, addr, arch=None): + if arch is None: + arch = self.arch color = core.BNGetInstructionHighlight(self.handle, arch.handle, addr) if color.style == core.BNHighlightColorStyle.StandardHighlightColor: return highlight.HighlightColor(color = color.color, alpha = color.alpha) @@ -617,12 +725,16 @@ class Function(object): return highlight.HighlightColor(red = color.r, green = color.g, blue = color.b, alpha = color.alpha) return highlight.HighlightColor(color = core.BNHighlightStandardColor.NoHighlightColor) - def set_auto_instr_highlight(self, arch, addr, color): + def set_auto_instr_highlight(self, addr, color, arch=None): + 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, arch, addr, color): + def set_user_instr_highlight(self, addr, color, arch=None): + if arch is None: + arch = self.arch if not isinstance(color, highlight.HighlightColor): color = highlight.HighlightColor(color = color) core.BNSetUserInstructionHighlight(self.handle, arch.handle, addr, color._get_core_struct()) diff --git a/python/scriptingprovider.py b/python/scriptingprovider.py index 3ccf9560..cbd9696d 100644 --- a/python/scriptingprovider.py +++ b/python/scriptingprovider.py @@ -334,6 +334,48 @@ class _PythonScriptingInstanceOutput(object): 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 @@ -590,6 +632,7 @@ class PythonScriptingProvider(ScriptingProvider): name = "Python" instance_class = PythonScriptingInstance + PythonScriptingProvider().register() # Wrap stdin/stdout/stderr for Python scripting provider implementation -- cgit v1.3.1 From 57c08987ee10af0b52d5c3fd44739a3935f9efa7 Mon Sep 17 00:00:00 2001 From: Rusty Wagner Date: Thu, 16 Feb 2017 19:12:43 -0500 Subject: Basic blocks have incoming and outgoing edges with basic block references, use core object identity for equality --- basicblock.cpp | 30 +++++++++++++++++++++--- binaryninjaapi.h | 42 ++++++++++++++++++++++++++++++---- binaryninjacore.h | 7 +++--- python/architecture.py | 10 ++++++++ python/basicblock.py | 56 +++++++++++++++++++++++++++++++++------------ python/binaryview.py | 40 ++++++++++++++++++++++++++++++++ python/callingconvention.py | 10 ++++++++ python/filemetadata.py | 10 ++++++++ python/function.py | 30 ++++++++++++++++++++++++ python/lowlevelil.py | 10 ++++++++ python/platform.py | 10 ++++++++ python/transform.py | 10 ++++++++ python/types.py | 50 ++++++++++++++++++++++++++++++++++++++++ 13 files changed, 291 insertions(+), 24 deletions(-) (limited to 'python/filemetadata.py') diff --git a/basicblock.cpp b/basicblock.cpp index 4edcc568..153a7768 100644 --- a/basicblock.cpp +++ b/basicblock.cpp @@ -108,6 +108,12 @@ uint64_t BasicBlock::GetLength() const } +size_t BasicBlock::GetIndex() const +{ + return BNGetBasicBlockIndex(m_object); +} + + vector BasicBlock::GetOutgoingEdges() const { size_t count; @@ -118,12 +124,30 @@ vector BasicBlock::GetOutgoingEdges() const { BasicBlockEdge edge; edge.type = array[i].type; - edge.target = array[i].target; - edge.arch = array[i].arch ? new CoreArchitecture(array[i].arch) : nullptr; + edge.target = array[i].target ? new BasicBlock(BNNewBasicBlockReference(array[i].target)) : nullptr; + result.push_back(edge); + } + + BNFreeBasicBlockEdgeList(array, count); + return result; +} + + +vector BasicBlock::GetIncomingEdges() const +{ + size_t count; + BNBasicBlockEdge* array = BNGetBasicBlockIncomingEdges(m_object, &count); + + vector result; + for (size_t i = 0; i < count; i++) + { + BasicBlockEdge edge; + edge.type = array[i].type; + edge.target = array[i].target ? new BasicBlock(BNNewBasicBlockReference(array[i].target)) : nullptr; result.push_back(edge); } - BNFreeBasicBlockOutgoingEdgeList(array); + BNFreeBasicBlockEdgeList(array, count); return result; } diff --git a/binaryninjaapi.h b/binaryninjaapi.h index 370a9ae2..7004501c 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -51,6 +51,8 @@ namespace BinaryNinja RefCountObject(): m_refs(0) {} virtual ~RefCountObject() {} + RefCountObject* GetObject() { return this; } + void AddRef() { #ifdef WIN32 @@ -101,7 +103,7 @@ namespace BinaryNinja CoreRefCountObject(): m_refs(0), m_object(nullptr) {} virtual ~CoreRefCountObject() {} - T* GetObject() { return m_object; } + T* GetObject() const { return m_object; } void AddRef() { @@ -158,7 +160,7 @@ namespace BinaryNinja StaticCoreRefCountObject(): m_refs(0), m_object(nullptr) {} virtual ~StaticCoreRefCountObject() {} - T* GetObject() { return m_object; } + T* GetObject() const { return m_object; } void AddRef() { @@ -246,6 +248,36 @@ namespace BinaryNinja return m_obj == NULL; } + bool operator==(const T* obj) const + { + return m_obj->GetObject() == obj->GetObject(); + } + + bool operator==(const Ref& obj) const + { + return m_obj->GetObject() == obj.m_obj->GetObject(); + } + + bool operator!=(const T* obj) const + { + return m_obj->GetObject() != obj->GetObject(); + } + + bool operator!=(const Ref& obj) const + { + return m_obj->GetObject() != obj.m_obj->GetObject(); + } + + bool operator<(const T* obj) const + { + return m_obj->GetObject() < obj->GetObject(); + } + + bool operator<(const Ref& obj) const + { + return m_obj->GetObject() < obj.m_obj->GetObject(); + } + T* GetPtr() const { return m_obj; @@ -1728,8 +1760,7 @@ namespace BinaryNinja struct BasicBlockEdge { BNBranchType type; - uint64_t target; - Ref arch; + Ref target; }; class BasicBlock: public CoreRefCountObject @@ -1744,7 +1775,10 @@ namespace BinaryNinja uint64_t GetEnd() const; uint64_t GetLength() const; + size_t GetIndex() const; + std::vector GetOutgoingEdges() const; + std::vector GetIncomingEdges() const; bool HasUndeterminedOutgoingEdges() const; void MarkRecentUse(); diff --git a/binaryninjacore.h b/binaryninjacore.h index b8531742..2d22bb3f 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -793,8 +793,7 @@ extern "C" struct BNBasicBlockEdge { BNBranchType type; - uint64_t target; - BNArchitecture* arch; + BNBasicBlock* target; }; struct BNPoint @@ -1726,8 +1725,10 @@ extern "C" BINARYNINJACOREAPI uint64_t BNGetBasicBlockEnd(BNBasicBlock* block); BINARYNINJACOREAPI uint64_t BNGetBasicBlockLength(BNBasicBlock* block); BINARYNINJACOREAPI BNBasicBlockEdge* BNGetBasicBlockOutgoingEdges(BNBasicBlock* block, size_t* count); - BINARYNINJACOREAPI void BNFreeBasicBlockOutgoingEdgeList(BNBasicBlockEdge* edges); + BINARYNINJACOREAPI BNBasicBlockEdge* BNGetBasicBlockIncomingEdges(BNBasicBlock* block, size_t* count); + BINARYNINJACOREAPI void BNFreeBasicBlockEdgeList(BNBasicBlockEdge* edges, size_t count); BINARYNINJACOREAPI bool BNBasicBlockHasUndeterminedOutgoingEdges(BNBasicBlock* block); + BINARYNINJACOREAPI size_t BNGetBasicBlockIndex(BNBasicBlock* block); BINARYNINJACOREAPI BNDisassemblyTextLine* BNGetBasicBlockDisassemblyText(BNBasicBlock* block, BNDisassemblySettings* settings, size_t* count); diff --git a/python/architecture.py b/python/architecture.py index 5bafe949..39e5e72d 100644 --- a/python/architecture.py +++ b/python/architecture.py @@ -333,6 +333,16 @@ class Architecture(object): self._pending_reg_lists = {} self._pending_token_lists = {} + def __eq__(self, value): + if not isinstance(value, Architecture): + return False + return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents) + + def __ne__(self, value): + if not isinstance(value, Architecture): + return True + return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) + @property def full_width_regs(self): """List of full width register strings (read-only)""" diff --git a/python/basicblock.py b/python/basicblock.py index f6dbd60d..ebacb311 100644 --- a/python/basicblock.py +++ b/python/basicblock.py @@ -29,19 +29,17 @@ import function class BasicBlockEdge(object): - def __init__(self, branch_type, target, arch): + def __init__(self, branch_type, target): self.type = branch_type - if self.type != BranchType.UnresolvedBranch: - self.target = target - self.arch = arch + self.target = target 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) + elif self.target.arch: + return "<%s: %s@%#x>" % (BranchType(self.type).name, self.target.arch.name, self.target.start) else: - return "<%s: %#x>" % (self.type, self.target) + return "<%s: %#x>" % (BranchType(self.type).name, self.target.start) class BasicBlock(object): @@ -52,6 +50,16 @@ class BasicBlock(object): def __del__(self): core.BNFreeBasicBlock(self.handle) + def __eq__(self, value): + if not isinstance(value, BasicBlock): + return False + return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents) + + def __ne__(self, value): + if not isinstance(value, BasicBlock): + return True + return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) + @property def function(self): """Basic block function (read-only)""" @@ -83,6 +91,11 @@ class BasicBlock(object): """Basic block length (read-only)""" return core.BNGetBasicBlockLength(self.handle) + @property + def index(self): + """Basic block index in list of blocks for the function (read-only)""" + return core.BNGetBasicBlockIndex(self.handle) + @property def outgoing_edges(self): """List of basic block outgoing edges (read-only)""" @@ -90,14 +103,29 @@ class BasicBlock(object): 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) + branch_type = BranchType(edges[i].type) + if edges[i].target: + target = BasicBlock(self.view, core.BNNewBasicBlockReference(edges[i].target)) + else: + target = None + result.append(BasicBlockEdge(branch_type, target)) + core.BNFreeBasicBlockEdgeList(edges, count.value) + return result + + @property + def incoming_edges(self): + """List of basic block incoming edges (read-only)""" + count = ctypes.c_ulonglong(0) + edges = core.BNGetBasicBlockIncomingEdges(self.handle, count) + result = [] + for i in xrange(0, count.value): + branch_type = BranchType(edges[i].type) + if edges[i].target: + target = BasicBlock(self.view, core.BNNewBasicBlockReference(edges[i].target)) else: - arch = None - result.append(BasicBlockEdge(branch_type, target, arch)) - core.BNFreeBasicBlockOutgoingEdgeList(edges) + target = None + result.append(BasicBlockEdge(branch_type, target)) + core.BNFreeBasicBlockEdgeList(edges, count.value) return result @property diff --git a/python/binaryview.py b/python/binaryview.py index 9753b653..dd37be69 100644 --- a/python/binaryview.py +++ b/python/binaryview.py @@ -299,6 +299,16 @@ class BinaryViewType(object): def __init__(self, handle): self.handle = core.handle_of_type(handle, core.BNBinaryViewType) + def __eq__(self, value): + if not isinstance(value, BinaryViewType): + return False + return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents) + + def __ne__(self, value): + if not isinstance(value, BinaryViewType): + return True + return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) + @property def name(self): """BinaryView name (read-only)""" @@ -547,6 +557,16 @@ class BinaryView(object): self.notifications = {} self.next_address = None # Do NOT try to access view before init() is called, use placeholder + def __eq__(self, value): + if not isinstance(value, BinaryView): + return False + return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents) + + def __ne__(self, value): + if not isinstance(value, BinaryView): + return True + return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) + @classmethod def register(cls): startup._init_plugins() @@ -3255,6 +3275,16 @@ class BinaryReader(object): def __del__(self): core.BNFreeBinaryReader(self.handle) + def __eq__(self, value): + if not isinstance(value, BinaryReader): + return False + return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents) + + def __ne__(self, value): + if not isinstance(value, BinaryReader): + return True + return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) + @property def endianness(self): """ @@ -3562,6 +3592,16 @@ class BinaryWriter(object): def __del__(self): core.BNFreeBinaryWriter(self.handle) + def __eq__(self, value): + if not isinstance(value, BinaryWriter): + return False + return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents) + + def __ne__(self, value): + if not isinstance(value, BinaryWriter): + return True + return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) + @property def endianness(self): """ diff --git a/python/callingconvention.py b/python/callingconvention.py index 5f4adeab..04ba711f 100644 --- a/python/callingconvention.py +++ b/python/callingconvention.py @@ -112,6 +112,16 @@ class CallingConvention(object): def __del__(self): core.BNFreeCallingConvention(self.handle) + def __eq__(self, value): + if not isinstance(value, CallingConvention): + return False + return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents) + + def __ne__(self, value): + if not isinstance(value, CallingConvention): + return True + return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) + def _get_caller_saved_regs(self, ctxt, count): try: regs = self.__class__.caller_saved_regs diff --git a/python/filemetadata.py b/python/filemetadata.py index f6593405..b489d5bb 100644 --- a/python/filemetadata.py +++ b/python/filemetadata.py @@ -93,6 +93,16 @@ class FileMetadata(object): core.BNSetFileMetadataNavigationHandler(self.handle, None) core.BNFreeFileMetadata(self.handle) + def __eq__(self, value): + if not isinstance(value, FileMetadata): + return False + return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents) + + def __ne__(self, value): + if not isinstance(value, FileMetadata): + return True + return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) + @classmethod def _unregister(cls, f): handle = ctypes.cast(f, ctypes.c_void_p) diff --git a/python/function.py b/python/function.py index 3aeb8e22..4cdd6cc9 100644 --- a/python/function.py +++ b/python/function.py @@ -177,6 +177,16 @@ class Function(object): core.BNReleaseAdvancedFunctionAnalysisDataMultiple(self.handle, self._advanced_analysis_requests) core.BNFreeFunction(self.handle) + def __eq__(self, value): + if not isinstance(value, Function): + return False + return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents) + + def __ne__(self, value): + if not isinstance(value, Function): + return True + return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) + @classmethod def _unregister(cls, func): handle = ctypes.cast(func, ctypes.c_void_p) @@ -856,6 +866,16 @@ class FunctionGraphBlock(object): def __del__(self): core.BNFreeFunctionGraphBlock(self.handle) + def __eq__(self, value): + if not isinstance(value, FunctionGraphBlock): + return False + return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents) + + def __ne__(self, value): + if not isinstance(value, FunctionGraphBlock): + return True + return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) + @property def basic_block(self): """Basic block associated with this part of the function graph (read-only)""" @@ -1030,6 +1050,16 @@ class FunctionGraph(object): self.abort() core.BNFreeFunctionGraph(self.handle) + def __eq__(self, value): + if not isinstance(value, FunctionGraph): + return False + return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents) + + def __ne__(self, value): + if not isinstance(value, FunctionGraph): + return True + return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) + @property def function(self): """Function for a function graph (read-only)""" diff --git a/python/lowlevelil.py b/python/lowlevelil.py index 29b46b65..c80fcd0d 100644 --- a/python/lowlevelil.py +++ b/python/lowlevelil.py @@ -253,6 +253,16 @@ class LowLevelILFunction(object): def __del__(self): core.BNFreeLowLevelILFunction(self.handle) + def __eq__(self, value): + if not isinstance(value, LowLevelILFunction): + return False + return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents) + + def __ne__(self, value): + if not isinstance(value, LowLevelILFunction): + return True + return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) + @property def current_address(self): """Current IL Address (read/write)""" diff --git a/python/platform.py b/python/platform.py index ccc80476..139b7d5e 100644 --- a/python/platform.py +++ b/python/platform.py @@ -110,6 +110,16 @@ class Platform(object): def __del__(self): core.BNFreePlatform(self.handle) + def __eq__(self, value): + if not isinstance(value, Platform): + return False + return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents) + + def __ne__(self, value): + if not isinstance(value, Platform): + return True + return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) + @property def default_calling_convention(self): """ diff --git a/python/transform.py b/python/transform.py index 0c003738..40382c69 100644 --- a/python/transform.py +++ b/python/transform.py @@ -131,6 +131,16 @@ class Transform(object): def __repr__(self): return "" % self.name + def __eq__(self, value): + if not isinstance(value, Transform): + return False + return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents) + + def __ne__(self, value): + if not isinstance(value, Transform): + return True + return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) + def _get_parameters(self, ctxt, count): try: count[0] = len(self.parameters) diff --git a/python/types.py b/python/types.py index 43aaa66f..ebaa36fc 100644 --- a/python/types.py +++ b/python/types.py @@ -139,6 +139,16 @@ class Symbol(object): def __del__(self): core.BNFreeSymbol(self.handle) + def __eq__(self, value): + if not isinstance(value, Symbol): + return False + return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents) + + def __ne__(self, value): + if not isinstance(value, Symbol): + return True + return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) + @property def type(self): """Symbol type (read-only)""" @@ -194,6 +204,16 @@ class Type(object): def __del__(self): core.BNFreeType(self.handle) + def __eq__(self, value): + if not isinstance(value, Type): + return False + return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents) + + def __ne__(self, value): + if not isinstance(value, Type): + return True + return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) + @property def type_class(self): """Type class (read-only)""" @@ -491,6 +511,16 @@ class NamedTypeReference(object): def __del__(self): core.BNFreeNamedTypeReference(self.handle) + def __eq__(self, value): + if not isinstance(value, NamedTypeReference): + return False + return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents) + + def __ne__(self, value): + if not isinstance(value, NamedTypeReference): + return True + return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) + @property def type_class(self): return NamedTypeReferenceClass(core.BNGetTypeReferenceClass(self.handle)) @@ -564,6 +594,16 @@ class Structure(object): def __del__(self): core.BNFreeStructure(self.handle) + def __eq__(self, value): + if not isinstance(value, Structure): + return False + return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents) + + def __ne__(self, value): + if not isinstance(value, Structure): + return True + return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) + @property def members(self): """Structure member list (read-only)""" @@ -652,6 +692,16 @@ class Enumeration(object): def __del__(self): core.BNFreeEnumeration(self.handle) + def __eq__(self, value): + if not isinstance(value, Enumeration): + return False + return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents) + + def __ne__(self, value): + if not isinstance(value, Enumeration): + return True + return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) + @property def members(self): """Enumeration member list (read-only)""" -- cgit v1.3.1