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