diff options
35 files changed, 620 insertions, 555 deletions
@@ -31,3 +31,17 @@ CTestTestfile.cmake api-docs/source/python.rst api-docs/source/index.rst .coverage +# Make +generator +libcrypto.so.1.0.2 +libcurl.so.4 +libssl.so.1.0.2 +plugins/ +python/examples/binaryninja/ +python/libcrypto.so.1.0.2 +python/libcurl.so.4 +python/libssl.so.1.0.2 +python/plugins/ +python/types/ +suite/binaryninja/ +types/ @@ -43,7 +43,8 @@ json.o: ./json/jsoncpp.cpp ./json/json.h ./json/json-forwards.h install: generate $(TARGET).a @echo "Installing binaryninja API."; cp -r python/* $(INSTALLPATH)/python/binaryninja - cp $(TARGETDIR)/$(TARGET).a $(INSTALLPATH) + cp $(TARGET).a $(INSTALLPATH) + @echo "Done."; generator: python/generator.cpp $(TARGET).a @echo "Building generator..."; @@ -65,43 +66,36 @@ python_test: environment python/_binaryninjacore.py python/enums.py oracle: environment python/_binaryninjacore.py python/enums.py python3 suite/generator.py -environment: python/_binaryninjacore.py python/enums.py - @echo "Copying libs to needed locations..." - @cp $(INSTALLPATH)/libbinaryninjacore.so.1 . - @cp $(INSTALLPATH)/libcurl.so.4 . - @cp $(INSTALLPATH)/libcrypto.so.1.0.2 . - @cp $(INSTALLPATH)/libssl.so.1.0.2 . - - @mkdir -p api/python/examples - @cp python/examples/bin_info.py api/python/examples/ - @cp $(INSTALLPATH)/libbinaryninjacore.so.1 api/python/ - @cp $(INSTALLPATH)/libcurl.so.4 api/python/ - @cp $(INSTALLPATH)/libcrypto.so.1.0.2 api/python/ - @cp $(INSTALLPATH)/libssl.so.1.0.2 api/python/ +environment: environment_clean python/_binaryninjacore.py python/enums.py + @echo "Copying over libs..." + cp $(INSTALLPATH)/libbinaryninjacore.so.1 . + cp $(INSTALLPATH)/libbinaryninjacore.so.1 python/ @echo "Building 'binaryninja' Packages..." - @mkdir -p suite/binaryninja/ - @cp -r python/* suite/binaryninja/ - @mkdir -p api/python/examples/binaryninja/ - @cp -r python/* api/python/examples/binaryninja/ + mkdir -p suite/binaryninja/ + cp -r python/* suite/binaryninja/ + cp -r suite/binaryninja/ python/examples/ @echo "Copying Architectures Over..." - @cp -r $(INSTALLPATH)/types/ . - @cp -r $(INSTALLPATH)/plugins/ . - @cp -r $(INSTALLPATH)/plugins/ api/python/ + cp -r $(INSTALLPATH)/types/ . + cp -r $(INSTALLPATH)/plugins/ . + cp -r $(INSTALLPATH)/types/ python/ + cp -r $(INSTALLPATH)/plugins/ python/ environment_clean: @echo "Removing 'binaryninja' Packages..." - @rm -r suite/binaryninja/ - @rm -r api/ - -@rm suite/*.pyc - + rm -rf suite/binaryninja/ + rm -rf python/examples/binaryninja/ + @echo "Removing libs..." - @rm lib* + rm -f libbinaryninjacore.so.1 + rm -f python/libbinaryninjacore.so.1 @echo "Removing Architectures..." - @rm -r types/ - @rm -r plugins/ + rm -rf types/ + rm -rf plugins/ + rm -rf python/types/ + rm -rf python/plugins/ clean: @echo " Cleaning..."; diff --git a/python/__init__.py b/python/__init__.py index 2f7cc5a0..e1288b0a 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -25,6 +25,32 @@ import sys import ctypes from time import gmtime + +# 2-3 compatibility +try: + import builtins # __builtins__ for python2 +except ImportError: + pass +def range(*args): + """ A Python2 and Python3 Compatible Range Generator """ + try: + return xrange(*args) + except NameError: + return builtins.range(*args) + + +def with_metaclass(meta, *bases): + """Create a base class with a metaclass.""" + class metaclass(type): + def __new__(cls, name, this_bases, d): + return meta(name, bases, d) + + @classmethod + def __prepare__(cls, name, this_bases): + return meta.__prepare__(name, bases) + return type.__new__(metaclass, 'temporary_class', (), {}) + + # Binary Ninja components import binaryninja._binaryninjacore as core # __all__ = [ diff --git a/python/architecture.py b/python/architecture.py index 521c750a..aee7c301 100644 --- a/python/architecture.py +++ b/python/architecture.py @@ -23,13 +23,12 @@ import traceback import ctypes import abc -# Binary Ninja components -- additional imports belong in the appropriate class +# Binary Ninja components from binaryninja import _binaryninjacore as core from binaryninja.enums import (Endianness, ImplicitRegisterExtend, BranchType, InstructionTextTokenType, LowLevelILFlagCondition, FlagRole) import binaryninja from binaryninja import log - from binaryninja import lowlevelil from binaryninja import types from binaryninja import databuffer @@ -37,8 +36,9 @@ from binaryninja import platform from binaryninja import callingconvention # 2-3 compatibility -from six import with_metaclass -from six.moves import range +from binaryninja import range +from binaryninja import with_metaclass + class _ArchitectureMetaClass(type): @@ -624,7 +624,7 @@ class Architecture(with_metaclass(_ArchitectureMetaClass, object)): def _get_full_width_registers(self, ctxt, count): try: - regs = self._full_width_regs.values() + regs = list(self._full_width_regs.values()) count[0] = len(regs) reg_buf = (ctypes.c_uint * len(regs))() for i in range(0, len(regs)): @@ -639,7 +639,7 @@ class Architecture(with_metaclass(_ArchitectureMetaClass, object)): def _get_all_registers(self, ctxt, count): try: - regs = self._regs_by_index.keys() + regs = list(self._regs_by_index.keys()) count[0] = len(regs) reg_buf = (ctypes.c_uint * len(regs))() for i in range(0, len(regs)): @@ -654,7 +654,7 @@ class Architecture(with_metaclass(_ArchitectureMetaClass, object)): def _get_all_flags(self, ctxt, count): try: - flags = self._flags_by_index.keys() + flags = list(self._flags_by_index.keys()) count[0] = len(flags) flag_buf = (ctypes.c_uint * len(flags))() for i in range(0, len(flags)): @@ -669,7 +669,7 @@ class Architecture(with_metaclass(_ArchitectureMetaClass, object)): def _get_all_flag_write_types(self, ctxt, count): try: - write_types = self._flag_write_types_by_index.keys() + write_types = list(self._flag_write_types_by_index.keys()) count[0] = len(write_types) type_buf = (ctypes.c_uint * len(write_types))() for i in range(0, len(write_types)): @@ -684,7 +684,7 @@ class Architecture(with_metaclass(_ArchitectureMetaClass, object)): def _get_all_semantic_flag_classes(self, ctxt, count): try: - sem_classes = self._semantic_flag_classes_by_index.keys() + sem_classes = list(self._semantic_flag_classes_by_index.keys()) count[0] = len(sem_classes) class_buf = (ctypes.c_uint * len(sem_classes))() for i in range(0, len(sem_classes)): @@ -699,7 +699,7 @@ class Architecture(with_metaclass(_ArchitectureMetaClass, object)): def _get_all_semantic_flag_groups(self, ctxt, count): try: - sem_groups = self._semantic_flag_groups_by_index.keys() + sem_groups = list(self._semantic_flag_groups_by_index.keys()) count[0] = len(sem_groups) group_buf = (ctypes.c_uint * len(sem_groups))() for i in range(0, len(sem_groups)): @@ -938,7 +938,7 @@ class Architecture(with_metaclass(_ArchitectureMetaClass, object)): def _get_all_register_stacks(self, ctxt, count): try: - regs = self._reg_stacks_by_index.keys() + regs = list(self._reg_stacks_by_index.keys()) count[0] = len(regs) reg_buf = (ctypes.c_uint * len(regs))() for i in range(0, len(regs)): @@ -989,7 +989,7 @@ class Architecture(with_metaclass(_ArchitectureMetaClass, object)): def _get_all_intrinsics(self, ctxt, count): try: - regs = self._intrinsics_by_index.keys() + regs = list(self._intrinsics_by_index.keys()) count[0] = len(regs) reg_buf = (ctypes.c_uint * len(regs))() for i in range(0, len(regs)): @@ -2415,8 +2415,8 @@ class CoreArchitecture(Architecture): :param str code: string representation of the instructions to be assembled :param int addr: virtual address that the instructions will be loaded at - :return: the bytes for the assembled instructions or error string - :rtype: (a tuple of instructions and empty string) or (or None and error string) + :return: the bytes for the assembled instructions + :rtype: Python3 - a 'bytes' object; Python2 - a 'str' :Example: >>> arch.assemble("je 10") @@ -2426,8 +2426,11 @@ class CoreArchitecture(Architecture): result = databuffer.DataBuffer() errors = ctypes.c_char_p() if not core.BNAssemble(self.handle, code, addr, result.handle, errors): - return None, errors.value - return str(result), errors.value + raise ValueError("Could not assemble") + if isinstance(str(result), bytes): + return str(result) + else: + return bytes(result) def is_never_branch_patch_available(self, data, addr): """ diff --git a/python/basicblock.py b/python/basicblock.py index b1f95ba3..11d5a7f4 100644 --- a/python/basicblock.py +++ b/python/basicblock.py @@ -20,14 +20,15 @@ import ctypes -# Binary Ninja components -- additional imports belong in the appropriate class +# Binary Ninja components import binaryninja from binaryninja import highlight from binaryninja import _binaryninjacore as core from binaryninja.enums import BranchType, HighlightColorStyle, HighlightStandardColor, InstructionTextTokenType # 2-3 compatibility -from six.moves import range +from binaryninja import range + class BasicBlockEdge(object): def __init__(self, branch_type, source, target, back_edge): diff --git a/python/binaryview.py b/python/binaryview.py index b1bc4822..d439f563 100644 --- a/python/binaryview.py +++ b/python/binaryview.py @@ -23,7 +23,7 @@ import traceback import ctypes import abc -# Binary Ninja components -- additional imports belong in the appropriate class +# Binary Ninja components from binaryninja import _binaryninjacore as core from binaryninja.enums import (AnalysisState, SymbolType, InstructionTextTokenType, Endianness, ModificationStatus, StringType, SegmentFlag, SectionSemantics) @@ -38,8 +38,8 @@ from binaryninja import lineardisassembly from binaryninja import metadata # 2-3 compatibility -from six import with_metaclass -from six.moves import range +from binaryninja import range +from binaryninja import with_metaclass class BinaryDataNotification(object): @@ -98,17 +98,19 @@ class StringReference(object): @property def value(self): - return self.view.read(self.start, self.length) + return self.view.read(self.start, self.length).decode("charmap") def __repr__(self): return "<%s: %#x, len %#x>" % (self.type, self.start, self.length) +_pending_analysis_completion_events = {} class AnalysisCompletionEvent(object): """ The ``AnalysisCompletionEvent`` object provides an asynchronous mechanism for receiving callbacks when analysis is complete. The callback runs once. A completion event must be added - for each new analysis in order to be notified of each analysis completion. + for each new analysis in order to be notified of each analysis completion. The + AnalysisCompletionEvent class takes responcibility for keeping track of the object's lifetime. :Example: >>> def on_complete(self): @@ -122,11 +124,19 @@ class AnalysisCompletionEvent(object): self.callback = callback self._cb = ctypes.CFUNCTYPE(None, ctypes.c_void_p)(self._notify) self.handle = core.BNAddAnalysisCompletionEvent(self.view.handle, None, self._cb) + global _pending_analysis_completion_events + _pending_analysis_completion_events[id(self)] = self def __del__(self): + global _pending_analysis_completion_events + if id(self) in _pending_analysis_completion_events: + del _pending_analysis_completion_events[id(self)] core.BNFreeAnalysisCompletionEvent(self.handle) def _notify(self, ctxt): + global _pending_analysis_completion_events + if id(self) in _pending_analysis_completion_events: + del _pending_analysis_completion_events[id(self)] try: self.callback(self) except: @@ -142,6 +152,9 @@ class AnalysisCompletionEvent(object): """ self.callback = self._empty_callback core.BNCancelAnalysisCompletionEvent(self.handle) + global _pending_analysis_completion_events + if id(self) in _pending_analysis_completion_events: + del _pending_analysis_completion_events[id(self)] class ActiveAnalysisInfo(object): @@ -1731,10 +1744,13 @@ class BinaryView(object): """ ``read`` returns the data reads at most ``length`` bytes from virtual address ``addr``. + Note: Python2 returns a str, but Python3 returns a bytes object. str(DataBufferObject) will + still get you a str in either case. + :param int addr: virtual address to read from. :param int length: number of bytes to read. :return: at most ``length`` bytes from the virtual address ``addr``, empty string on error or no data. - :rtype: str + :rtype: python2 - str; python3 - bytes :Example: >>> #Opening a x86_64 Mach-O binary @@ -2784,7 +2800,9 @@ class BinaryView(object): def add_analysis_completion_event(self, callback): """ ``add_analysis_completion_event`` sets up a call back function to be called when analysis has been completed. - This is helpful when using asynchronously analysis. + This is helpful when using ``update_analysis`` which does not wait for analysis completion before returning. + + The callee of this function is not resposible for maintaining the lifetime of the returned AnalysisCompletionEvent object. :param callable() callback: A function to be called with no parameters when analysis has completed. :return: An initialized AnalysisCompletionEvent object. @@ -3481,7 +3499,7 @@ class BinaryView(object): def get_unique_section_names(self, name_list): incoming_names = (ctypes.c_char_p * len(name_list))() for i in range(0, len(name_list)): - incoming_names[i] = name_list[i] + incoming_names[i] = name_list[i].encode('charmap') outgoing_names = core.BNGetUniqueSectionNames(self.handle, incoming_names, len(name_list)) result = [] for i in range(0, len(name_list)): diff --git a/python/callingconvention.py b/python/callingconvention.py index 1b0d068e..6b906184 100644 --- a/python/callingconvention.py +++ b/python/callingconvention.py @@ -21,18 +21,17 @@ import traceback import ctypes -# Binary Ninja components -- additional imports belong in the appropriate class +# Binary Ninja components import binaryninja from binaryninja import _binaryninjacore as core from binaryninja import log from binaryninja.enums import VariableSourceType # 2-3 compatibility -from six.moves import range +from binaryninja import range class CallingConvention(object): - from binaryninja import types name = None caller_saved_regs = [] int_arg_regs = [] @@ -48,7 +47,7 @@ class CallingConvention(object): _registered_calling_conventions = [] - def __init__(self, arch=None, name=None, handle=None, confidence=types.max_confidence): + def __init__(self, arch=None, name=None, handle=None, confidence=binaryninja.types.max_confidence): if handle is None: if arch is None or name is None: raise ValueError("Must specify either handle or architecture and name") diff --git a/python/databuffer.py b/python/databuffer.py index 1b29a7eb..298d0002 100644 --- a/python/databuffer.py +++ b/python/databuffer.py @@ -20,7 +20,7 @@ import ctypes -# Binary Ninja components -- additional imports belong in the appropriate class +# Binary Ninja components from binaryninja import _binaryninjacore as core @@ -114,7 +114,7 @@ class DataBuffer(object): def __str__(self): buf = ctypes.create_string_buffer(len(self)) ctypes.memmove(buf, core.BNGetDataBufferContents(self.handle), len(self)) - if type(buf.raw) is str: + if isinstance(buf.raw, str): return buf.raw else: return buf.raw.decode("charmap") diff --git a/python/demangle.py b/python/demangle.py index 90a19761..466324bc 100644 --- a/python/demangle.py +++ b/python/demangle.py @@ -20,11 +20,11 @@ import ctypes -# Binary Ninja components -- additional imports belong in the appropriate class +# Binary Ninja components from binaryninja import _binaryninjacore as core # 2-3 compatibility -from six.moves import range +from binaryninja import range def get_qualified_name(names): diff --git a/python/downloadprovider.py b/python/downloadprovider.py index d34ca358..c714cb98 100644 --- a/python/downloadprovider.py +++ b/python/downloadprovider.py @@ -28,6 +28,7 @@ import traceback # Binary Ninja Components import binaryninja._binaryninjacore as core from binaryninja.setting import Setting +from binaryninja import with_metaclass from binaryninja import startup from binaryninja import log @@ -109,8 +110,7 @@ class _DownloadProviderMetaclass(type): raise AttributeError("attribute '%s' is read only" % name) -class DownloadProvider(object): - __metaclass__ = _DownloadProviderMetaclass +class DownloadProvider(with_metaclass(_DownloadProviderMetaclass, object)): name = None instance_class = None _registered_providers = [] diff --git a/python/fileaccessor.py b/python/fileaccessor.py index 3c7c5e45..c2539b39 100644 --- a/python/fileaccessor.py +++ b/python/fileaccessor.py @@ -21,11 +21,10 @@ import traceback import ctypes -# Binary Ninja components -- additional imports belong in the appropriate class +# Binary Ninja components from binaryninja import _binaryninjacore as core class FileAccessor(object): - def __init__(self): self._cb = core.BNFileAccessor() self._cb.context = 0 diff --git a/python/filemetadata.py b/python/filemetadata.py index ee69dce8..cafe1d67 100644 --- a/python/filemetadata.py +++ b/python/filemetadata.py @@ -22,7 +22,7 @@ from __future__ import absolute_import import traceback import ctypes -# Binary Ninja components -- additional imports belong in the appropriate class +# Binary Ninja Components import binaryninja from binaryninja import _binaryninjacore as core from binaryninja import associateddatastore #required for _FileMetadataAssociatedDataStore diff --git a/python/function.py b/python/function.py index 6392d7f2..8407eece 100644 --- a/python/function.py +++ b/python/function.py @@ -23,20 +23,20 @@ import threading import traceback import ctypes -# Binary Ninja components -- additional imports belong in the appropriate class +# Binary Ninja components import binaryninja from binaryninja import _binaryninjacore as core +from binaryninja import associateddatastore #required in the main scope due to being an argument for _FunctionAssociatedDataStore +from binaryninja import highlight +from binaryninja import log +from binaryninja import types from binaryninja.enums import (AnalysisSkipReason, FunctionGraphType, BranchType, SymbolType, InstructionTextTokenType, HighlightStandardColor, HighlightColorStyle, RegisterValueType, ImplicitRegisterExtend, DisassemblyOption, IntegerDisplayType, InstructionTextTokenContext, VariableSourceType, FunctionAnalysisSkipOverride) -from binaryninja import associateddatastore #required in the main scope due to being an argument for _FunctionAssociatedDataStore -from binaryninja import types -from binaryninja import highlight -from binaryninja import log # 2-3 compatibility -from six.moves import range +from binaryninja import range class LookupTableEntry(object): @@ -49,7 +49,7 @@ class LookupTableEntry(object): class RegisterValue(object): - def __init__(self, arch = None, value = None, confidence = binaryninja.types.max_confidence): + def __init__(self, arch = None, value = None, confidence = types.max_confidence): self.is_constant = False if value is None: self.type = RegisterValueType.UndeterminedValue @@ -1160,6 +1160,8 @@ class Function(object): for j in range(0, lines[i].count): token_type = InstructionTextTokenType(lines[i].tokens[j].type) text = lines[i].tokens[j].text + if not isinstance(text, str): + text = text.encode("charmap") value = lines[i].tokens[j].value size = lines[i].tokens[j].size operand = lines[i].tokens[j].operand @@ -1836,6 +1838,7 @@ class DisassemblySettings(object): core.BNSetDisassemblySettingsOption(self.handle, option, state) +_pending_function_graph_completion_events = {} class FunctionGraph(object): def __init__(self, view, handle): self.view = view @@ -1969,6 +1972,9 @@ class FunctionGraph(object): try: if self._on_complete is not None: self._on_complete() + global _pending_function_graph_completion_events + if id(self) in _pending_function_graph_completion_events: + del _pending_function_graph_completion_events[id(self)] except: log.log_error(traceback.format_exc()) @@ -1993,11 +1999,16 @@ class FunctionGraph(object): self._wait_cond.release() def on_complete(self, callback): + global _pending_function_graph_completion_events + _pending_function_graph_completion_events[id(self)] = self self._on_complete = callback core.BNSetFunctionGraphCompleteCallback(self.handle, None, self._cb) def abort(self): core.BNAbortFunctionGraph(self.handle) + global _pending_function_graph_completion_events + if id(self) in _pending_function_graph_completion_events: + del _pending_function_graph_completion_events[id(self)] def get_blocks_in_region(self, left, top, right, bottom): count = ctypes.c_ulonglong() diff --git a/python/functionrecognizer.py b/python/functionrecognizer.py index e390a3c8..319bff0e 100644 --- a/python/functionrecognizer.py +++ b/python/functionrecognizer.py @@ -20,15 +20,15 @@ import traceback -# Binary Ninja components -- additional imports belong in the appropriate class +# Binary Ninja components from binaryninja import _binaryninjacore as core +from binaryninja import function +from binaryninja import filemetadata +from binaryninja import binaryview +from binaryninja import lowlevelil class FunctionRecognizer(object): - from binaryninja import function - from binaryninja import filemetadata - from binaryninja import binaryview - from binaryninja import lowlevelil _instance = None @@ -54,7 +54,7 @@ class FunctionRecognizer(object): try: file_metadata = filemetadata.FileMetadata(handle = core.BNGetFileForView(data)) view = binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(data)) - func = binaryninja.function.Function(view, handle = core.BNNewFunctionReference(func)) + func = function.Function(view, handle = core.BNNewFunctionReference(func)) il = lowlevelil.LowLevelILFunction(func.arch, handle = core.BNNewLowLevelILFunctionReference(il)) return self.recognize_low_level_il(view, func, il) except: @@ -68,7 +68,7 @@ class FunctionRecognizer(object): try: file_metadata = filemetadata.FileMetadata(handle = core.BNGetFileForView(data)) view = binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(data)) - func = binaryninja.function.Function(view, handle = core.BNNewFunctionReference(func)) + func = function.Function(view, handle = core.BNNewFunctionReference(func)) il = mediumlevelil.MediumLevelILFunction(func.arch, handle = core.BNNewMediumLevelILFunctionReference(il)) return self.recognize_medium_level_il(view, func, il) except: diff --git a/python/highlight.py b/python/highlight.py index b04561bd..55246a9a 100644 --- a/python/highlight.py +++ b/python/highlight.py @@ -19,7 +19,7 @@ # IN THE SOFTWARE. -# Binary Ninja components -- additional imports belong in the appropriate class +# Binary Ninja components from binaryninja import _binaryninjacore as core from binaryninja.enums import HighlightColorStyle, HighlightStandardColor diff --git a/python/interaction.py b/python/interaction.py index 5b78b278..49b4e024 100644 --- a/python/interaction.py +++ b/python/interaction.py @@ -21,12 +21,13 @@ import ctypes import traceback -# Binary Ninja components -- additional imports belong in the appropriate class +# Binary Ninja components from binaryninja import _binaryninjacore as core from binaryninja.enums import FormInputFieldType, MessageBoxIcon, MessageBoxButtonSet, MessageBoxButtonResult +from binaryninja import binaryview # 2-3 compatibility -from six.moves import range +from binaryninja import range class LabelField(object): @@ -152,7 +153,11 @@ class AddressField(object): class ChoiceField(object): """ ``ChoiceField`` prompts the user to choose from the list of strings provided in ``choices``. Result is stored - in self.result as an index in to the coices array. + in self.result as an index in to the choices array. + + :attr str prompt: prompt to be presented to the user + :attr list(str) choices: list of choices to choose from + """ def __init__(self, prompt, choices): self.prompt = prompt @@ -164,7 +169,7 @@ class ChoiceField(object): value.prompt = self.prompt choice_buf = (ctypes.c_char_p * len(self.choices))() for i in range(0, len(self.choices)): - choice_buf[i] = str(self.choices[i]) + choice_buf[i] = self.choices[i].encode('charmap') value.choices = choice_buf value.count = len(self.choices) @@ -242,8 +247,6 @@ class DirectoryNameField(object): class InteractionHandler(object): - - from binaryninja import binaryview _interaction_handler = None def __init__(self): @@ -617,7 +620,7 @@ def get_choice_input(prompt, title, choices): """ choice_buf = (ctypes.c_char_p * len(choices))() for i in range(0, len(choices)): - choice_buf[i] = str(choices[i]) + choice_buf[i] = str(choices[i]).encode('charmap') value = ctypes.c_ulonglong() if not core.BNGetChoiceInput(value, prompt, title, choice_buf, len(choices)): return None diff --git a/python/log.py b/python/log.py index d4d87019..9a5e1358 100644 --- a/python/log.py +++ b/python/log.py @@ -19,7 +19,7 @@ # IN THE SOFTWARE. -# Binary Ninja components -- additional imports belong in the appropriate class +# Binary Ninja components from binaryninja import _binaryninjacore as core from binaryninja.enums import LogLevel @@ -55,7 +55,7 @@ def log(level, text): :param str text: message to print :rtype: None """ - core.BNLog(level, "%s", str(text)) + core.BNLog(level,text) def log_debug(text): @@ -70,7 +70,7 @@ def log_debug(text): >>> log_debug("Hotdogs!") Hotdogs! """ - core.BNLogDebug("%s", str(text)) + core.BNLogDebug(text) def log_info(text): @@ -85,7 +85,7 @@ def log_info(text): Saucisson! >>> """ - core.BNLogInfo("%s", str(text)) + core.BNLogInfo(text) def log_warn(text): @@ -101,7 +101,7 @@ def log_warn(text): Chilidogs! >>> """ - core.BNLogWarn("%s", str(text)) + core.BNLogWarn(text) def log_error(text): @@ -117,7 +117,7 @@ def log_error(text): Spanferkel! >>> """ - core.BNLogError("%s", str(text)) + core.BNLogError(text) def log_alert(text): @@ -133,7 +133,7 @@ def log_alert(text): Kielbasa! >>> """ - core.BNLogAlert("%s", str(text)) + core.BNLogAlert(text) def log_to_stdout(min_level=LogLevel.InfoLog): diff --git a/python/lowlevelil.py b/python/lowlevelil.py index 272b2474..e714eb48 100644 --- a/python/lowlevelil.py +++ b/python/lowlevelil.py @@ -21,14 +21,14 @@ import ctypes import struct -# Binary Ninja components -- additional imports belong in the appropriate class +# Binary Ninja components import binaryninja from binaryninja import _binaryninjacore as core from binaryninja.enums import LowLevelILOperation, LowLevelILFlagCondition, InstructionTextTokenType from binaryninja import basicblock #required for LowLevelILBasicBlock # 2-3 compatibility -from six.moves import range +from binaryninja import range class LowLevelILLabel(object): diff --git a/python/mainthread.py b/python/mainthread.py index 16938f1a..5b0cd53c 100644 --- a/python/mainthread.py +++ b/python/mainthread.py @@ -18,7 +18,7 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. -# Binary Ninja components -- additional imports belong in the appropriate class +# Binary Ninja components from binaryninja import _binaryninjacore as core from binaryninja import scriptingprovider diff --git a/python/mediumlevelil.py b/python/mediumlevelil.py index f7e9951a..cfa8f900 100644 --- a/python/mediumlevelil.py +++ b/python/mediumlevelil.py @@ -30,7 +30,7 @@ from binaryninja import types from binaryninja import lowlevelil # 2-3 compatibility -from six.moves import range +from binaryninja import range class SSAVariable(object): diff --git a/python/metadata.py b/python/metadata.py index fed8bdaa..b17bbf4f 100644 --- a/python/metadata.py +++ b/python/metadata.py @@ -22,12 +22,12 @@ from __future__ import absolute_import import ctypes -# Binary Ninja components -- additional imports belong in the appropriate class +# Binary Ninja components from binaryninja import _binaryninjacore as core from binaryninja.enums import MetadataType # 2-3 compatibility -from six.moves import range +from binaryninja import range class Metadata(object): @@ -147,7 +147,7 @@ class Metadata(object): result = core.BNMetadataGetValueStore(self.handle) try: for i in range(result.contents.size): - if type(result.contents.keys[i]) is bytes: + if isinstance(result.contents.keys[i], bytes): yield str(result.contents.keys[i].decode('charmap')) else: yield result.contents.keys[i] diff --git a/python/platform.py b/python/platform.py index c57315ce..9a5f70df 100644 --- a/python/platform.py +++ b/python/platform.py @@ -20,18 +20,18 @@ import ctypes -# Binary Ninja components -- additional imports belong in the appropriate class +# Binary Ninja components import binaryninja from binaryninja import _binaryninjacore as core +from binaryninja import types -#2-3 compatibility -from six import with_metaclass -from six.moves import range +# 2-3 compatibility +from binaryninja import range +from binaryninja import with_metaclass class _PlatformMetaClass(type): - from binaryninja import types @property def list(self): binaryninja._init_plugins() @@ -240,8 +240,8 @@ class Platform(with_metaclass(_PlatformMetaClass, object)): type_list = core.BNGetPlatformTypes(self.handle, count) result = {} for i in range(0, count.value): - name = binaryninja.types.QualifiedName._from_core_struct(type_list[i].name) - result[name] = binaryninja.types.Type(core.BNNewTypeReference(type_list[i].type), platform = self) + name = types.QualifiedName._from_core_struct(type_list[i].name) + result[name] = types.Type(core.BNNewTypeReference(type_list[i].type), platform = self) core.BNFreeTypeList(type_list, count.value) return result @@ -252,8 +252,8 @@ class Platform(with_metaclass(_PlatformMetaClass, object)): type_list = core.BNGetPlatformVariables(self.handle, count) result = {} for i in range(0, count.value): - name = binaryninja.types.QualifiedName._from_core_struct(type_list[i].name) - result[name] = binaryninja.types.Type(core.BNNewTypeReference(type_list[i].type), platform = self) + name = types.QualifiedName._from_core_struct(type_list[i].name) + result[name] = types.Type(core.BNNewTypeReference(type_list[i].type), platform = self) core.BNFreeTypeList(type_list, count.value) return result @@ -264,8 +264,8 @@ class Platform(with_metaclass(_PlatformMetaClass, object)): type_list = core.BNGetPlatformFunctions(self.handle, count) result = {} for i in range(0, count.value): - name = binaryninja.types.QualifiedName._from_core_struct(type_list[i].name) - result[name] = binaryninja.types.Type(core.BNNewTypeReference(type_list[i].type), platform = self) + name = types.QualifiedName._from_core_struct(type_list[i].name) + result[name] = types.Type(core.BNNewTypeReference(type_list[i].type), platform = self) core.BNFreeTypeList(type_list, count.value) return result @@ -276,8 +276,8 @@ class Platform(with_metaclass(_PlatformMetaClass, object)): call_list = core.BNGetPlatformSystemCalls(self.handle, count) result = {} for i in range(0, count.value): - name = binaryninja.types.QualifiedName._from_core_struct(call_list[i].name) - t = binaryninja.types.Type(core.BNNewTypeReference(call_list[i].type), platform = self) + name = types.QualifiedName._from_core_struct(call_list[i].name) + t = types.Type(core.BNNewTypeReference(call_list[i].type), platform = self) result[call_list[i].number] = (name, t) core.BNFreeSystemCallList(call_list, count.value) return result @@ -328,25 +328,25 @@ class Platform(with_metaclass(_PlatformMetaClass, object)): return Platform(None, handle = result), new_addr.value def get_type_by_name(self, name): - name = binaryninja.types.QualifiedName(name)._get_core_struct() + name = types.QualifiedName(name)._get_core_struct() obj = core.BNGetPlatformTypeByName(self.handle, name) if not obj: return None - return binaryninja.types.Type(obj, platform = self) + return types.Type(obj, platform = self) def get_variable_by_name(self, name): - name = binaryninja.types.QualifiedName(name)._get_core_struct() + name = types.QualifiedName(name)._get_core_struct() obj = core.BNGetPlatformVariableByName(self.handle, name) if not obj: return None - return binaryninja.types.Type(obj, platform = self) + return types.Type(obj, platform = self) def get_function_by_name(self, name): - name = binaryninja.types.QualifiedName(name)._get_core_struct() + name = types.QualifiedName(name)._get_core_struct() obj = core.BNGetPlatformFunctionByName(self.handle, name) if not obj: return None - return binaryninja.types.Type(obj, platform = self) + return types.Type(obj, platform = self) def get_system_call_name(self, number): return core.BNGetPlatformSystemCallName(self.handle, number) @@ -355,15 +355,15 @@ class Platform(with_metaclass(_PlatformMetaClass, object)): obj = core.BNGetPlatformSystemCallType(self.handle, number) if not obj: return None - return binaryninja.types.Type(obj, platform = self) + return types.Type(obj, platform = self) def generate_auto_platform_type_id(self, name): - name = binaryninja.types.QualifiedName(name)._get_core_struct() + name = types.QualifiedName(name)._get_core_struct() return core.BNGenerateAutoPlatformTypeId(self.handle, name) def generate_auto_platform_type_ref(self, type_class, name): type_id = self.generate_auto_platform_type_id(name) - return binaryninja.types.NamedTypeReference(type_class, type_id, name) + return types.NamedTypeReference(type_class, type_id, name) def get_auto_platform_type_id_source(self): return core.BNGetAutoPlatformTypeIdSource(self.handle) @@ -391,7 +391,7 @@ class Platform(with_metaclass(_PlatformMetaClass, object)): filename = "input" dir_buf = (ctypes.c_char_p * len(include_dirs))() for i in range(0, len(include_dirs)): - dir_buf[i] = str(include_dirs[i]) + dir_buf[i] = include_dirs[i].encode('charmap') parse = core.BNTypeParserResult() errors = ctypes.c_char_p() result = core.BNParseTypesFromSource(self.handle, source, filename, parse, errors, dir_buf, @@ -404,16 +404,16 @@ class Platform(with_metaclass(_PlatformMetaClass, object)): variables = {} functions = {} for i in range(0, parse.typeCount): - name = binaryninja.types.QualifiedName._from_core_struct(parse.types[i].name) - type_dict[name] = binaryninja.types.Type(core.BNNewTypeReference(parse.types[i].type), platform = self) + name = types.QualifiedName._from_core_struct(parse.types[i].name) + type_dict[name] = types.Type(core.BNNewTypeReference(parse.types[i].type), platform = self) for i in range(0, parse.variableCount): - name = binaryninja.types.QualifiedName._from_core_struct(parse.variables[i].name) - variables[name] = binaryninja.types.Type(core.BNNewTypeReference(parse.variables[i].type), platform = self) + name = types.QualifiedName._from_core_struct(parse.variables[i].name) + variables[name] = types.Type(core.BNNewTypeReference(parse.variables[i].type), platform = self) for i in range(0, parse.functionCount): - name = binaryninja.types.QualifiedName._from_core_struct(parse.functions[i].name) - functions[name] = binaryninja.types.Type(core.BNNewTypeReference(parse.functions[i].type), platform = self) + name = types.QualifiedName._from_core_struct(parse.functions[i].name) + functions[name] = types.Type(core.BNNewTypeReference(parse.functions[i].type), platform = self) core.BNFreeTypeParserResult(parse) - return binaryninja.types.TypeParserResult(type_dict, variables, functions) + return types.TypeParserResult(type_dict, variables, functions) def parse_types_from_source_file(self, filename, include_dirs=[], auto_type_source=None): """ @@ -437,7 +437,7 @@ class Platform(with_metaclass(_PlatformMetaClass, object)): """ dir_buf = (ctypes.c_char_p * len(include_dirs))() for i in range(0, len(include_dirs)): - dir_buf[i] = str(include_dirs[i]) + dir_buf[i] = include_dirs[i].encode('charmap') parse = core.BNTypeParserResult() errors = ctypes.c_char_p() result = core.BNParseTypesFromSourceFile(self.handle, filename, parse, errors, dir_buf, @@ -450,13 +450,13 @@ class Platform(with_metaclass(_PlatformMetaClass, object)): variables = {} functions = {} for i in range(0, parse.typeCount): - name = binaryninja.types.QualifiedName._from_core_struct(parse.types[i].name) - type_dict[name] = binaryninja.types.Type(core.BNNewTypeReference(parse.types[i].type), platform = self) + name = types.QualifiedName._from_core_struct(parse.types[i].name) + type_dict[name] = types.Type(core.BNNewTypeReference(parse.types[i].type), platform = self) for i in range(0, parse.variableCount): - name = binaryninja.types.QualifiedName._from_core_struct(parse.variables[i].name) - variables[name] = binaryninja.types.Type(core.BNNewTypeReference(parse.variables[i].type), platform = self) + name = types.QualifiedName._from_core_struct(parse.variables[i].name) + variables[name] = types.Type(core.BNNewTypeReference(parse.variables[i].type), platform = self) for i in range(0, parse.functionCount): - name = binaryninja.types.QualifiedName._from_core_struct(parse.functions[i].name) - functions[name] = binaryninja.types.Type(core.BNNewTypeReference(parse.functions[i].type), platform = self) + name = types.QualifiedName._from_core_struct(parse.functions[i].name) + functions[name] = types.Type(core.BNNewTypeReference(parse.functions[i].type), platform = self) core.BNFreeTypeParserResult(parse) - return binaryninja.types.TypeParserResult(type_dict, variables, functions) + return types.TypeParserResult(type_dict, variables, functions) diff --git a/python/plugin.py b/python/plugin.py index 52feced4..b8217f0b 100644 --- a/python/plugin.py +++ b/python/plugin.py @@ -22,15 +22,18 @@ import traceback import ctypes import threading -# Binary Ninja components -- additional imports belong in the appropriate class +# Binary Ninja components import binaryninja from binaryninja import log from binaryninja import _binaryninjacore as core from binaryninja.enums import PluginCommandType +from binaryninja import filemetadata +from binaryninja import binaryview +from binaryninja import function -#2-3 compatibility -from six import with_metaclass -from six.moves import range +# 2-3 compatibility +from binaryninja import range +from binaryninja import with_metaclass class PluginCommandContext(object): @@ -43,7 +46,6 @@ class PluginCommandContext(object): class _PluginCommandMetaClass(type): - @property def list(self): binaryninja._init_plugins() @@ -73,11 +75,6 @@ class _PluginCommandMetaClass(type): class PluginCommand(with_metaclass(_PluginCommandMetaClass, object)): - - from binaryninja import filemetadata - from binaryninja import binaryview - from binaryninja import function - _registered_commands = [] def __init__(self, cmd): @@ -540,7 +537,6 @@ class MainThreadAction(object): class MainThreadActionHandler(object): - _main_thread = None def __init__(self): @@ -586,7 +582,6 @@ class _BackgroundTaskMetaclass(type): class BackgroundTask(with_metaclass(_BackgroundTaskMetaclass, object)): - def __init__(self, initial_progress_text = "", can_cancel = False, handle = None): if handle is None: self.handle = core.BNBeginBackgroundTask(initial_progress_text, can_cancel) diff --git a/python/pluginmanager.py b/python/pluginmanager.py index ad314629..c7a0c83a 100644 --- a/python/pluginmanager.py +++ b/python/pluginmanager.py @@ -20,11 +20,12 @@ import ctypes -# Binary Ninja components -- additional imports belong in the appropriate class +# Binary Ninja components from binaryninja import _binaryninjacore as core # 2-3 compatibility -from six.moves import range +from binaryninja import range + class RepoPlugin(object): """ @@ -201,7 +202,6 @@ class RepositoryManager(object): ``RepositoryManager`` Keeps track of all the repositories and keeps the enabled_plugins.json file coherent with the plugins that are installed/unstalled enabled/disabled """ - def __init__(self, handle=None): raise Exception("RepositoryManager temporarily disabled!") self.handle = core.BNGetRepositoryManager() diff --git a/python/scriptingprovider.py b/python/scriptingprovider.py index 374b2f41..37642ed4 100644 --- a/python/scriptingprovider.py +++ b/python/scriptingprovider.py @@ -26,14 +26,15 @@ import threading import abc import sys -# Binary Ninja components -- additional imports belong in the appropriate class +# Binary Ninja components +import binaryninja from binaryninja import _binaryninjacore as core from binaryninja.enums import ScriptingProviderExecuteResult, ScriptingProviderInputReadyState -import binaryninja.log +from binaryninja import log -#2-3 compatibility -from six import with_metaclass -from six.moves import range +# 2-3 compatibility +from binaryninja import range +from binaryninja import with_metaclass class _ThreadActionContext(object): @@ -75,19 +76,19 @@ class ScriptingOutputListener(object): try: self.notify_output(text) except: - binaryninja.log.log_error(traceback.format_exc()) + log.log_error(traceback.format_exc()) def _error(self, ctxt, text): try: self.notify_error(text) except: - binaryninja.log.log_error(traceback.format_exc()) + log.log_error(traceback.format_exc()) def _input_ready_state_changed(self, ctxt, state): try: self.notify_input_ready_state_changed(state) except: - binaryninja.log.log_error(traceback.format_exc()) + log.log_error(traceback.format_exc()) def notify_output(self, text): pass @@ -123,13 +124,13 @@ class ScriptingInstance(object): try: self.perform_destroy_instance() except: - binaryninja.log.log_error(traceback.format_exc()) + log.log_error(traceback.format_exc()) def _execute_script_input(self, ctxt, text): try: return self.perform_execute_script_input(text) except: - binaryninja.log.log_error(traceback.format_exc()) + log.log_error(traceback.format_exc()) return ScriptingProviderExecuteResult.InvalidScriptInput def _set_current_binary_view(self, ctxt, view): @@ -140,7 +141,7 @@ class ScriptingInstance(object): view = None self.perform_set_current_binary_view(view) except: - binaryninja.log.log_error(traceback.format_exc()) + log.log_error(traceback.format_exc()) def _set_current_function(self, ctxt, func): try: @@ -150,7 +151,7 @@ class ScriptingInstance(object): func = None self.perform_set_current_function(func) except: - binaryninja.log.log_error(traceback.format_exc()) + log.log_error(traceback.format_exc()) def _set_current_basic_block(self, ctxt, block): try: @@ -165,19 +166,19 @@ class ScriptingInstance(object): block = None self.perform_set_current_basic_block(block) except: - binaryninja.log.log_error(traceback.format_exc()) + log.log_error(traceback.format_exc()) def _set_current_address(self, ctxt, addr): try: self.perform_set_current_address(addr) except: - binaryninja.log.log_error(traceback.format_exc()) + log.log_error(traceback.format_exc()) def _set_current_selection(self, ctxt, begin, end): try: self.perform_set_current_selection(begin, end) except: - binaryninja.log.log_error(traceback.format_exc()) + log.log_error(traceback.format_exc()) @abc.abstractmethod def perform_destroy_instance(self): @@ -324,7 +325,7 @@ class ScriptingProvider(with_metaclass(_ScriptingProviderMetaclass, object)): return None return ctypes.cast(core.BNNewScriptingInstanceReference(result.handle), ctypes.c_void_p).value except: - binaryninja.log.log_error(traceback.format_exc()) + log.log_error(traceback.format_exc()) return None def create_instance(self): @@ -390,7 +391,7 @@ class _PythonScriptingInstanceOutput(object): interpreter = PythonScriptingInstance._interpreter.value if interpreter is None: - if binaryninja.log.is_output_redirected_to_log(): + if log.is_output_redirected_to_log(): self.buffer += data while True: i = self.buffer.find('\n') @@ -400,9 +401,9 @@ class _PythonScriptingInstanceOutput(object): self.buffer = self.buffer[i + 1:] if self.is_error: - binaryninja.log.log_error(line) + log.log_error(line) else: - binaryninja.log.log_info(line) + log.log_info(line) else: self.orig.write(data) else: @@ -490,7 +491,7 @@ class PythonScriptingInstance(ScriptingInstance): self.code = None self.input = "" - self.interpreter.push("from binaryninja import *\n") + self.interpreter.push("from binaryninja import *") def execute(self, code): self.code = code @@ -553,8 +554,8 @@ class PythonScriptingInstance(ScriptingInstance): self.locals["current_llil"] = self.active_func.low_level_il self.locals["current_mlil"] = self.active_func.medium_level_il - for line in code.split("\n"): - self.interpreter.push(line) + for line in code.split(b'\n'): + self.interpreter.push(line.decode('charmap')) if self.locals["here"] != self.active_addr: if not self.active_view.file.navigate(self.active_view.file.view, self.locals["here"]): @@ -660,3 +661,4 @@ def redirect_stdio(): sys.stdin = _PythonScriptingInstanceInput(sys.stdin) sys.stdout = _PythonScriptingInstanceOutput(sys.stdout, False) sys.stderr = _PythonScriptingInstanceOutput(sys.stderr, True) + sys.excepthook = sys.__excepthook__
\ No newline at end of file diff --git a/python/setting.py b/python/setting.py index fea06140..355492aa 100644 --- a/python/setting.py +++ b/python/setting.py @@ -20,11 +20,11 @@ import ctypes -# Binary Ninja components -- additional imports belong in the appropriate class +# Binary Ninja components from binaryninja import _binaryninjacore as core # 2-3 compatibility -from six.moves import range +from binaryninja import range class Setting(object): @@ -113,7 +113,7 @@ class Setting(object): length.value = len(value) default_list = (ctypes.c_char_p * len(value))() for i in range(len(value)): - default_list[i] = value[i].encode('utf-8') + default_list[i] = value[i].encode('charmap') return core.BNSettingSetStringList(self.plugin_name, name, default_list, length, auto_flush) @@ -141,4 +141,4 @@ class Setting(object): core.BNSettingRemoveSettingGroup(self.plugin_name, auto_flush) def remove_setting(self, setting, auto_flush=True): - core.BNSettingRemoveSetting(self.plugin_name, setting, auto_flush)
\ No newline at end of file + core.BNSettingRemoveSetting(self.plugin_name, setting, auto_flush) diff --git a/python/transform.py b/python/transform.py index e16c6bfc..1734d248 100644 --- a/python/transform.py +++ b/python/transform.py @@ -22,20 +22,19 @@ import traceback import ctypes import abc -# Binary Ninja components -- additional imports belong in the appropriate class +# Binary Ninja components import binaryninja from binaryninja import log from binaryninja import databuffer from binaryninja import _binaryninjacore as core from binaryninja.enums import TransformType -#2-3 compatibility -from six import with_metaclass -from six.moves import range +# 2-3 compatibility +from binaryninja import range +from binaryninja import with_metaclass class _TransformMetaClass(type): - @property def list(self): binaryninja._init_plugins() @@ -96,8 +95,6 @@ class TransformParameter(object): class Transform(with_metaclass(_TransformMetaClass, object)): - - from binaryninja import databuffer transform_type = None name = None long_name = None @@ -224,7 +221,7 @@ class Transform(with_metaclass(_TransformMetaClass, object)): def decode(self, input_buf, params = {}): input_buf = databuffer.DataBuffer(input_buf) output_buf = databuffer.DataBuffer() - keys = params.keys() + keys = list(params.keys()) param_buf = (core.BNTransformParameter * len(keys))() for i in range(0, len(keys)): data = databuffer.DataBuffer(params[keys[i]]) @@ -237,7 +234,7 @@ class Transform(with_metaclass(_TransformMetaClass, object)): def encode(self, input_buf, params = {}): input_buf = databuffer.DataBuffer(input_buf) output_buf = databuffer.DataBuffer() - keys = params.keys() + keys = list(params.keys()) param_buf = (core.BNTransformParameter * len(keys))() for i in range(0, len(keys)): data = databuffer.DataBuffer(params[keys[i]]) diff --git a/python/types.py b/python/types.py index 2ad8d2c2..e8ce7464 100644 --- a/python/types.py +++ b/python/types.py @@ -23,23 +23,26 @@ max_confidence = 255 import ctypes -# Binary Ninja components -- additional imports belong in the appropriate class +# Binary Ninja components import binaryninja from binaryninja import _binaryninjacore as core from binaryninja.enums import SymbolType, TypeClass, NamedTypeReferenceClass, InstructionTextTokenType, StructureType, ReferenceType, VariableSourceType -#2-3 compatibility -from six.moves import range +# 2-3 compatibility +from binaryninja import range class QualifiedName(object): def __init__(self, name = []): if isinstance(name, str): self.name = [name] + self.byte_name = [name.encode('charmap')] elif isinstance(name, QualifiedName): self.name = name.name + self.byte_name = [n.encode('charmap') for n in name.name] else: - self.name = [i.decode('utf-8') for i in name] + self.name = [i.decode('charmap') for i in name] + self.byte_name = name def __str__(self): return "::".join(self.name) @@ -102,7 +105,7 @@ class QualifiedName(object): result = core.BNQualifiedName() name_list = (ctypes.c_char_p * len(self.name))() for i in range(0, len(self.name)): - name_list[i] = self.name[i] + name_list[i] = self.name[i].encode('charmap') result.name = name_list result.nameCount = len(self.name) return result @@ -1022,7 +1025,7 @@ def preprocess_source(source, filename=None, include_dirs=[]): filename = "input" dir_buf = (ctypes.c_char_p * len(include_dirs))() for i in range(0, len(include_dirs)): - dir_buf[i] = str(include_dirs[i]) + dir_buf[i] = include_dirs[i].encode('charmap') output = ctypes.c_char_p() errors = ctypes.c_char_p() result = core.BNPreprocessSource(source, filename, output, errors, dir_buf, len(include_dirs)) diff --git a/python/undoaction.py b/python/undoaction.py index 732d559a..7891bf54 100644 --- a/python/undoaction.py +++ b/python/undoaction.py @@ -22,14 +22,12 @@ import traceback import json import ctypes -# Binary Ninja components -- additional imports belong in the appropriate class +# Binary Ninja components from binaryninja import _binaryninjacore as core from binaryninja.enums import ActionType class UndoAction(object): - - name = None action_type = None _registered = False diff --git a/python/update.py b/python/update.py index 4c77b202..7f5d6f9c 100644 --- a/python/update.py +++ b/python/update.py @@ -21,18 +21,16 @@ import traceback import ctypes -# Binary Ninja components -- additional imports belong in the appropriate class +# Binary Ninja components from binaryninja import _binaryninjacore as core from binaryninja.enums import UpdateResult - -#2-3 compatibility -from six import with_metaclass -from six.moves import range +# 2-3 compatibility +from binaryninja import range +from binaryninja import with_metaclass class _UpdateChannelMetaClass(type): - @property def list(self): binaryninja._init_plugins() @@ -99,7 +97,6 @@ class _UpdateChannelMetaClass(type): class UpdateProgressCallback(object): - def __init__(self, func): self.cb = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.c_ulonglong, ctypes.c_ulonglong)(self.callback) self.func = func @@ -114,7 +111,6 @@ class UpdateProgressCallback(object): class UpdateChannel(with_metaclass(_UpdateChannelMetaClass, object)): - def __init__(self, name, desc, ver): self.name = name self.description = desc diff --git a/scripts/install_api.py b/scripts/install_api.py index 53679ba5..3cc0ed0d 100755 --- a/scripts/install_api.py +++ b/scripts/install_api.py @@ -80,6 +80,6 @@ else: sys.exit(1) binaryninja_pth_path = os.path.join(install_path, 'binaryninja.pth') -open(binaryninja_pth_path, 'wb').write(api_path) +open(binaryninja_pth_path, 'wb').write(api_path.encode('charmap')) print("Binary Ninja API installed using {}".format(binaryninja_pth_path)) diff --git a/suite/binaries b/suite/binaries -Subproject 7d5d207de812ccd7c4648331b00fa936ef0a82c +Subproject ab6b18161a5b9ccd3588c88233f150fb6c7e2ce diff --git a/suite/generator.py b/suite/generator.py index b812b1b5..3302d118 100755 --- a/suite/generator.py +++ b/suite/generator.py @@ -10,24 +10,77 @@ import time unit_test_template = """#!/usr/bin/env python # This is an auto generated unit test file do not edit directly import os +import sys import unittest import pickle import zipfile import testcommon -import binaryninja import api_test import difflib +from collections import Counter +global verbose +verbose = False class TestBinaryNinjaAPI(unittest.TestCase): + # Returns a tuple of: + # bool : Two lists are equal + # string : The string diff + # Args: + # list + # list : (compare list one vs list two) + # string : anything additional wanted to be printed before the string diff + # bool : the ordering of the items in the two lists must be the same + def report(self, oracle, test, firstText='', strictOrdering = False): + stringDiff = "" + + equality = False + if not strictOrdering: + equality = (Counter(oracle) == Counter(test)) + else: + equality = (oracle == test) + + if equality: + return (True, '') + elif not strictOrdering: + try: + for elem in oracle: + test.remove(elem) + oracle.remove(elem) # If it's not in the test, it won't get here! + except ValueError: + pass + + differ = difflib.Differ(charjunk=difflib.IS_CHARACTER_JUNK) + skipped_lines = 0 + for delta in differ.compare(oracle, test): + if delta[0] == ' ': + skipped_lines += 1 + continue + if skipped_lines > 0: + stringDiff += "<---" + str(skipped_lines) + ' same lines--->\\n' + skipped_lines = 0 + delta = delta.replace(\'\\n\', '') + stringDiff += delta + \'\\n\' + + stringDiffList = stringDiff.split(\'\\n\') + + if len(stringDiffList) > 10: + if not verbose: + stringDiff = \'\\n\'.join(line if len(line) <= 100 else line[:100] + "...and " + str(len(line) - 100) + " more characters" for line in stringDiffList[:10]) + stringDiff += \'\\n\\n### And ' + str(len(stringDiffList)) + " more lines, use '-v' to show ###" + elif not verbose: + stringDiff = \'\\n\'.join(line if len(line) <= 100 else line[:100] + "...and " + str(len(line) - 100) + " more characters" for line in stringDiffList) + stringDiff = \'\\n\\n\' + firstText + stringDiff + return (equality, stringDiff) + @classmethod def setUpClass(self): self.builder = testcommon.TestBuilder("{3}") try: - #Python 2 does not have the encodings option - self.oracle_test_data = pickle.load(open(os.path.join("{0}", "oracle.pkl"), "rUb"), errors="ignore") + # Python 2 does not have the encodings option + self.oracle_test_data = pickle.load(open(os.path.join("{0}", "oracle.pkl"), "rb"), encoding='charmap') except TypeError: - self.oracle_test_data = pickle.load(open(os.path.join("{0}", "oracle.pkl"), "rU")) + self.oracle_test_data = pickle.load(open(os.path.join("{0}", "oracle.pkl"), "r")) self.verifybuilder = testcommon.VerifyBuilder("{3}") def run_binary_test(self, testfile): @@ -38,10 +91,10 @@ class TestBinaryNinjaAPI(unittest.TestCase): self.assertTrue(os.path.exists(testname + ".pkl"), "Test pickle doesn't exist") try: - #Python 2 does not have the encodings option - binary_oracle = pickle.load(open(testname + ".pkl", "rUb"), errors="ignore") + # Python 2 does not have the encodings option + binary_oracle = pickle.load(open(testname + ".pkl", "rb"), encoding='charmap') except TypeError: - binary_oracle = pickle.load(open(testname + ".pkl", "rU")) + binary_oracle = pickle.load(open(testname + ".pkl", "r")) test_builder = testcommon.BinaryViewTestBuilder(testname, "{3}") for method in test_builder.methods(): @@ -52,22 +105,16 @@ class TestBinaryNinjaAPI(unittest.TestCase): result = getattr(test_builder, method).__doc__ result += ":\\n" - d = difflib.Differ(charjunk=difflib.IS_CHARACTER_JUNK) - skipped_lines = 0 - for delta in d.compare(test, oracle): - if delta[0] == ' ': - skipped_lines += 1 - continue - if skipped_lines > 0: - result += "<---" + str(skipped_lines) + ' same lines--->\\n' - skipped_lines = 0 - delta = delta.replace('\\n', '') - result += delta + '\\n' - self.assertTrue(False, result) + report = self.report(oracle, test, result) + self.assertTrue(report[0], report[1]) # Test does not agree with oracle os.unlink(testname) {1}{2} if __name__ == "__main__": + if len(sys.argv) > 1: + if sys.argv[1] == '-v' or sys.argv[1] == '-V' or sys.argv[1] == '--verbose': + verbose = True + test_suite = unittest.defaultTestLoader.loadTestsFromModule(api_test) test_suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestBinaryNinjaAPI)) runner = unittest.TextTestRunner(verbosity=2) @@ -83,20 +130,8 @@ test_string = """ def {0}(self): oracle = self.oracle_test_data['{0}'] test = self.builder.{0}() - result = "" - differ = difflib.Differ(charjunk=difflib.IS_CHARACTER_JUNK) - skipped_lines = 0 - for delta in differ.compare(test, oracle): - if delta[0] == ' ': - skipped_lines += 1 - continue - if skipped_lines > 0: - result += "<---" + str(skipped_lines) + ' same lines--->\\n' - skipped_lines = 0 - delta = delta.replace('\\n', '') - result += delta + '\\n' - - self.assertTrue(oracle == test, result) + report = self.report(oracle, test) + self.assertTrue(report[0], report[1]) # Test does not agree with oracle """ verify_string = """ @@ -108,7 +143,7 @@ verify_string = """ class OracleTestFile: def __init__(self, filename): self.f = open(filename + ".pkl", "wb") - self.pkl = pickle.Pickler(self.f) + self.pkl = pickle.Pickler(self.f, protocol=2) self.filename = filename self.oracle_test_data = {} @@ -131,7 +166,7 @@ class UnitTestFile: self.binary_tests = "" def close(self): - self.f.write(self.template.format(self.outdir, self.tests, self.binary_tests, self.test_store).encode('utf-8')) + self.f.write(self.template.format(self.outdir, self.tests, self.binary_tests, self.test_store).encode('charmap')) self.f.close() def add_verify(self, test_name): @@ -146,8 +181,6 @@ class UnitTestFile: quiet = False - - def myprint(stuff): if not quiet: print(stuff) diff --git a/suite/testcommon.py b/suite/testcommon.py index 1bbc0d47..ef85c053 100644 --- a/suite/testcommon.py +++ b/suite/testcommon.py @@ -1,14 +1,54 @@ import tempfile import pickle import os +import sys import zipfile import inspect import binaryninja as binja from binaryninja.binaryview import BinaryViewType, BinaryView from binaryninja.filemetadata import FileMetadata import subprocess -import traceback -import types +import re + + +# Dear people from the future: If you're adding tests or debuging an +# issue where python2 and python3 are producing different output +# for the same function and it's a issue of `longs`, run the output +# through this function. If it's a unicode/bytes issue, fix it in +# api/python/ +def fixOutput(outputList): + # Apply regular expression to detect python2 longs + splitList = [] + for elem in outputList: + if isinstance(elem, str): + splitList.append(re.split(r"((?<=[\[ ])0x[\da-f]+L|[\d]+L)", elem)) + else: + splitList.append(elem) + + # Resolve application of regular expression + result = [] + for elem in splitList: + if isinstance(elem, list): + newElem = [] + for item in elem: + if len(item) > 1 and item[-1] == 'L': + newElem.append(item[:-1]) + else: + newElem.append(item) + result.append(''.join(newElem)) + else: + result.append(elem) + return result + + +# Alright so this one is here for Binja functions that output <in set([blah, blah, blah])> +def fixSet(string): + # Apply regular expression + splitList = (re.split(r"((?<=<in set\(\[).*(?=\]\)>))", string)) + if len(splitList) > 1: + return splitList[0] + ', '.join(sorted(splitList[1].split(', '))) + splitList[2] + else: + return string def get_file_list(test_store): @@ -16,13 +56,12 @@ def get_file_list(test_store): for root, dir, files in os.walk(test_store): for file in files: all_files.append(os.path.join(root, file)) - return all_files def remove_low_confidence(type_string): low_confidence_types = ["int32_t", "void"] for lct in low_confidence_types: - type_string = type_string.replace(lct + " ", '') # done to resolve confidence ties + type_string = type_string.replace(lct + " ", '') # done to resolve confidence ties return type_string class Builder(object): @@ -66,15 +105,24 @@ class BinaryViewTestBuilder(Builder): def test_function_starts(self): """Function starts list doesnt match""" - return ["Function start: " + hex(x.start) for x in self.bv.functions] + result = [] + for x in self.bv.functions: + result.append("Function start: " + hex(x.start)) + return fixOutput(result) def test_function_symbol_names(self): """Function.symbol.name list doesnt match""" - return ["Symbol: " + x.symbol.name + ' ' + str(x.symbol.type) + ' ' + hex(x.symbol.address) for x in self.bv.functions] + result = [] + for x in self.bv.functions: + result.append("Symbol: " + x.symbol.name + ' ' + str(x.symbol.type) + ' ' + hex(x.symbol.address)) + return fixOutput(result) def test_function_can_return(self): """Function.can_return list doesnt match""" - return ["function name: " + x.symbol.name + ' type: ' + str(x.symbol.type) + ' address: ' + hex(x.symbol.address) + ' can_return: ' + str(bool(x.can_return)) for x in self.bv.functions] + result = [] + for x in self.bv.functions: + result.append("function name: " + x.symbol.name + ' type: ' + str(x.symbol.type) + ' address: ' + hex(x.symbol.address) + ' can_return: ' + str(bool(x.can_return))) + return fixOutput(result) def test_function_basic_blocks(self): """Function basic_block list doesnt match (start, end, has_undetermined_outgoing_edges)""" @@ -85,32 +133,31 @@ class BinaryViewTestBuilder(Builder): for anno in func.get_block_annotations(bb.start): bblist.append("basic block {} function annotation: ".format(str(bb)) + str(anno)) bblist.append("basic block {} test get self: ".format(str(bb)) + str(func.get_basic_block_at(bb.start))) - return bblist + return fixOutput(bblist) def test_function_low_il_basic_blocks(self): - """"Function low_il_basic_block list doesnt match""" + """Function low_il_basic_block list doesnt match""" ilbblist = [] for func in self.bv.functions: for bb in func.low_level_il.basic_blocks: ilbblist.append("LLIL basic block {} start: ".format(str(bb)) + hex(bb.start) + ' end: ' + hex(bb.end) + ' outgoing edges: ' + str(len(bb.outgoing_edges))) - return ilbblist + return fixOutput(ilbblist) def test_function_med_il_basic_blocks(self): - """"Function med_il_basic_block list doesn't match""" + """Function med_il_basic_block list doesn't match""" ilbblist = [] for func in self.bv.functions: for bb in func.medium_level_il.basic_blocks: ilbblist.append("MLIL basic block {} start: ".format(str(bb)) + hex(bb.start) + ' end: ' + hex(bb.end) + ' outgoing_edges: ' + str(len(bb.outgoing_edges))) - return ilbblist + return fixOutput(ilbblist) def test_symbols(self): - """"Symbols list doesn't match""" + """Symbols list doesn't match""" return ["Symbol: " + str(i) for i in sorted(self.bv.symbols)] def test_strings(self): """Strings list doesn't match""" - return ["String: " + x.value + ' type: ' + str(x.type) + ' at: ' + hex(x.start) for x in self.bv.strings] - + return fixOutput(["String: " + str(x.value) + ' type: ' + str(x.type) + ' at: ' + hex(x.start) for x in self.bv.strings]) def test_low_il_instructions(self): """LLIL instructions produced different output""" @@ -126,9 +173,7 @@ class BinaryViewTestBuilder(Builder): retinfo.append("Postfix operands: " + str(ins.postfix_operands)) retinfo.append("SSA form: " + str(ins.ssa_form)) retinfo.append("Non-SSA form: " + str(ins.non_ssa_form)) - - return retinfo - + return fixOutput(retinfo) def test_low_il_ssa(self): """LLIL ssa produced different output""" @@ -151,9 +196,7 @@ class BinaryViewTestBuilder(Builder): retinfo.append("SSA instruction index: " + str(func.get_ssa_instruction_index(tempind))) retinfo.append("MLIL instruction index: " + str(func.get_medium_level_il_instruction_index(ins.instr_index))) retinfo.append("Mapped MLIL instruction index: " + str(func.get_mapped_medium_level_il_instruction_index(ins.instr_index))) - - return retinfo - + return fixOutput(retinfo) def test_med_il_instructions(self): """MLIL instructions produced different output""" @@ -165,18 +208,33 @@ class BinaryViewTestBuilder(Builder): retinfo.append("LLIL: " + str(ins.low_level_il)) retinfo.append("Value: " + str(ins.value)) retinfo.append("Possible values: " + str(ins.possible_values)) - retinfo.append("Branch dependence: " + str(ins.branch_dependence)) - retinfo.append("Prefix operands: " + str(sorted([str(i) for i in ins.prefix_operands]))) - retinfo.append("Postfix operands: " + str(sorted([str(i) for i in ins.postfix_operands]))) - retinfo.append("SSA form: " + str(ins.ssa_form)) - retinfo.append("Non-SSA form" + str(ins.non_ssa_form)) - - return retinfo + retinfo.append("Branch dependence: " + str(sorted(ins.branch_dependence.items()))) + prefixList = [] + for i in ins.prefix_operands: + if isinstance(i, float) and 'e' in str(i): + prefixList.append(str(round(i, 21))) + elif isinstance(i, float): + prefixList.append(str(round(i, 11))) + else: + prefixList.append(str(i)) + retinfo.append("Prefix operands: " + str(sorted(prefixList))) + postfixList = [] + for i in ins.prefix_operands: + if isinstance(i, float) and 'e' in str(i): + postfixList.append(str(round(i, 21))) + elif isinstance(i, float): + postfixList.append(str(round(i, 11))) + else: + postfixList.append(str(i)) + retinfo.append("Postfix operands: " + str(sorted(postfixList))) + retinfo.append("SSA form: " + str(ins.ssa_form)) + retinfo.append("Non-SSA form" + str(ins.non_ssa_form)) + return fixOutput(retinfo) def test_med_il_vars(self): - """"Function med_il_vars doesn't match""" + """Function med_il_vars doesn't match""" varlist = [] for func in self.bv.functions: func = func.medium_level_il @@ -184,14 +242,13 @@ class BinaryViewTestBuilder(Builder): for instruction in bb: instruction = instruction.ssa_form for var in (instruction.vars_read + instruction.vars_written): - #varlist.append((func.get_var_uses(var), func.get_var_definitions(var))) if hasattr(var, "var"): varlist.append("SSA var definition: " + str(func.get_ssa_var_definition(var))) varlist.append("SSA var uses: " + str(func.get_ssa_var_uses(var))) varlist.append("SSA var value: " + str(func.get_ssa_var_value(var))) - varlist.append("SSA var possible values: " + str(instruction.get_ssa_var_possible_values(var))) + varlist.append("SSA var possible values: " + fixSet(str(instruction.get_ssa_var_possible_values(var)))) varlist.append("SSA var version: " + str(instruction.get_ssa_var_version)) - return varlist + return fixOutput(varlist) def test_function_stack(self): """Function stack produced different output""" @@ -211,32 +268,26 @@ class BinaryViewTestBuilder(Builder): funcinfo.append("Sample stack var: " + str(func.get_stack_var_at_frame_offset(0, 0))) func.delete_user_stack_var(0) func.delete_auto_stack_var(0) - return funcinfo def test_function_llil(self): """Function LLIL produced different output""" retinfo = [] - for func in self.bv.functions: for llilbb in func.llil_basic_blocks: retinfo.append("LLIL basic block: " + str(llilbb)) for llilins in func.llil_instructions: retinfo.append("LLIL instruction: " + str(llilins)) - for mlilbb in func.mlil_basic_blocks: retinfo.append("MLIL basic block: " + str(mlilbb)) for mlilins in func.mlil_instructions: retinfo.append("MLIL instruction: " + str(mlilins)) - for ins in func.instructions: - retinfo.append("Instructiin: {}: ".format(hex(ins[1])) + ''.join([str(i) for i in ins[0]])) - - return retinfo - + retinfo.append("Instruction: {}: ".format(hex(ins[1])) + ''.join([str(i) for i in ins[0]])) + return fixOutput(retinfo) def test_functions_attributes(self): - """"Function attributes don't match""" + """Function attributes don't match""" funcinfo = [] for func in self.bv.functions: func.comment = "testcomment " + func.name @@ -284,9 +335,7 @@ class BinaryViewTestBuilder(Builder): token = str(token) token = remove_low_confidence(token) funcinfo.append("Function {} type token: ".format(func.name) + str(token)) - - - return funcinfo + return fixOutput(funcinfo) def test_BinaryView(self): """BinaryView produced different results""" @@ -309,11 +358,12 @@ class BinaryViewTestBuilder(Builder): retinfo.append("BV entry point: " + hex(self.bv.entry_point)) retinfo.append("BV start: " + hex(self.bv.start)) retinfo.append("BV length: " + hex(len(self.bv))) - return retinfo + + return fixOutput(retinfo) class TestBuilder(Builder): - """ The TestBuilder is for tests that need to be checked against + """ The TestBuilder is for tests that need to be checked againsttest_BinaryView stored oracle data that isn't from a binary. These test are generated on your local machine then run again on the build machine to verify correctness. @@ -335,23 +385,81 @@ class TestBuilder(Builder): """unexpected assemble result""" result = [] # success cases - result.append("x86 assembly: " + str(binja.Architecture["x86"].assemble("xor eax, eax"))) - result.append("x86_64 assembly: " + str(binja.Architecture["x86_64"].assemble("xor rax, rax"))) - result.append("mips32 assembly: " + str(binja.Architecture["mips32"].assemble("move $ra, $zero"))) - result.append("mipsel32 assembly: " + str(binja.Architecture["mipsel32"].assemble("move $ra, $zero"))) - result.append("armv7 assembly: " + str(binja.Architecture["armv7"].assemble("str r2, [sp, #-0x4]!"))) - result.append("aarch64 assembly: " + str(binja.Architecture["aarch64"].assemble("mov x0, x0"))) - result.append("thumb2 assembly: " + str(binja.Architecture["thumb2"].assemble("ldr r4, [r4]"))) - result.append("thumb2eb assembly: " + str(binja.Architecture["thumb2eb"].assemble("ldr r4, [r4]"))) + + strResult = binja.Architecture["x86"].assemble("xor eax, eax") + if sys.version_info.major == 3 and not strResult[0] is None: + result.append("x86 assembly: " + "'" + str(strResult)[2:-1] + "'") + else: + result.append("x86 assembly: " + repr(str(strResult))) + strResult = binja.Architecture["x86_64"].assemble("xor rax, rax") + if sys.version_info.major == 3 and not strResult[0] is None: + result.append("x86_64 assembly: " + "'" + str(strResult)[2:-1] + "'") + else: + result.append("x86_64 assembly: " + repr(str(strResult))) + strResult = binja.Architecture["mips32"].assemble("move $ra, $zero") + if sys.version_info.major == 3 and not strResult[0] is None: + result.append("mips32 assembly: " + "'" + str(strResult)[2:-1] + "'") + else: + result.append("mips32 assembly: " + repr(str(strResult))) + strResult = binja.Architecture["mipsel32"].assemble("move $ra, $zero") + if sys.version_info.major == 3 and not strResult[0] is None: + result.append("mipsel32 assembly: " + "'" + str(strResult)[2:-1] + "'") + else: + result.append("mipsel32 assembly: " + repr(str(strResult))) + strResult = binja.Architecture["armv7"].assemble("str r2, [sp, #-0x4]!") + if sys.version_info.major == 3 and not strResult[0] is None: + result.append("armv7 assembly: " + "'" + str(strResult)[2:-1] + "'") + else: + result.append("armv7 assembly: " + repr(str(strResult))) + strResult = binja.Architecture["aarch64"].assemble("mov x0, x0") + if sys.version_info.major == 3 and not strResult[0] is None: + result.append("aarch64 assembly: " + "'" + str(strResult)[2:-1] + "'") + else: + result.append("aarch64 assembly: " + repr(str(strResult))) + strResult = binja.Architecture["thumb2"].assemble("ldr r4, [r4]") + if sys.version_info.major == 3 and not strResult[0] is None: + result.append("thumb2 assembly: " + "'" + str(strResult)[2:-1] + "'") + else: + result.append("thumb2 assembly: " + repr(str(strResult))) + strResult = binja.Architecture["thumb2eb"].assemble("ldr r4, [r4]") + if sys.version_info.major == 3 and not strResult[0] is None: + result.append("thumb2eb assembly: " + "'" + str(strResult)[2:-1] + "'") + else: + result.append("thumb2eb assembly: " + repr(str(strResult))) + # fail cases - result.append("x86 assembly: " + str(binja.Architecture["x86"].assemble("thisisnotaninstruction"))) - result.append("x86_64 assembly: " + str(binja.Architecture["x86_64"].assemble("thisisnotaninstruction"))) - result.append("mips32 assembly: " + str(binja.Architecture["mips32"].assemble("thisisnotaninstruction"))) - result.append("mipsel32 assembly: " + str(binja.Architecture["mipsel32"].assemble("thisisnotaninstruction"))) - result.append("armv7 assembly: " + str(binja.Architecture["armv7"].assemble("thisisnotaninstruction"))) - result.append("aarch64 assembly: " + str(binja.Architecture["aarch64"].assemble("thisisnotaninstruction"))) - result.append("thumb2 assembly: " + str(binja.Architecture["thumb2"].assemble("thisisnotaninstruction"))) - result.append("thumb2eb assembly: " + str(binja.Architecture["thumb2eb"].assemble("thisisnotaninstruction"))) + try: + strResult = binja.Architecture["x86"].assemble("thisisnotaninstruction") + except ValueError: + result.append("Assemble Failed As Expected; 'thisisnotaninstruction' is not an instruction on 'x86'") + try: + strResult = binja.Architecture["x86_64"].assemble("thisisnotaninstruction") + except ValueError: + result.append("Assemble Failed As Expected; 'thisisnotaninstruction' is not an instruction on 'x86_64'") + try: + strResult = binja.Architecture["mips32"].assemble("thisisnotaninstruction") + except ValueError: + result.append("Assemble Failed As Expected; 'thisisnotaninstruction' is not an instruction on 'mips32'") + try: + strResult = binja.Architecture["mipsel32"].assemble("thisisnotaninstruction") + except ValueError: + result.append("Assemble Failed As Expected; 'thisisnotaninstruction' is not an instruction on 'mipsel32'") + try: + strResult = binja.Architecture["armv7"].assemble("thisisnotaninstruction") + except ValueError: + result.append("Assemble Failed As Expected; 'thisisnotaninstruction' is not an instruction on 'armv7'") + try: + strResult = binja.Architecture["aarch64"].assemble("thisisnotaninstruction") + except ValueError: + result.append("Assemble Failed As Expected; 'thisisnotaninstruction' is not an instruction on 'aarch64'") + try: + strResult = binja.Architecture["thumb2"].assemble("thisisnotaninstruction") + except ValueError: + result.append("Assemble Failed As Expected; 'thisisnotaninstruction' is not an instruction on 'thumb2'") + try: + strResult = binja.Architecture["thumb2eb"].assemble("thisisnotaninstruction") + except ValueError: + result.append("Assemble Failed As Expected; 'thisisnotaninstruction' is not an instruction on 'thumb2eb'") return result def test_Architecture(self): @@ -385,8 +493,6 @@ class TestBuilder(Builder): retinfo.append("Instruction: " + str(ins)) return retinfo - - def test_Function(self): """Function produced different result""" inttype = binja.Type.int(4) @@ -433,7 +539,6 @@ class TestBuilder(Builder): def test_Types(self): """Types produced different result""" - file_name = os.path.join(self.test_store, "helloworld") bv = binja.BinaryViewType.get_view_of_file(file_name) preprocessed = binja.preprocess_source(""" @@ -445,7 +550,7 @@ class TestBuilder(Builder): long long bar1 = 2; #endif """) - source = '\n'.join([i.decode("utf-8") for i in preprocessed[0].split('\n') if not b"#line" in i and len(i) > 0]) + source = '\n'.join([i.decode('utf-8') for i in preprocessed[0].split(b'\n') if not b'#line' in i and len(i) > 0]) typelist = bv.platform.parse_types_from_source(source) inttype = binja.Type.int(4) @@ -464,12 +569,11 @@ class TestBuilder(Builder): def test_Plugin_bin_info(self): """print_syscalls plugin produced different result""" - file_name = os.path.join(self.test_store, "helloworld") self.unpackage_file(file_name) result = subprocess.Popen(["python", os.path.join(self.examples_dir, "bin_info.py"), file_name], stdout=subprocess.PIPE).communicate()[0] # normalize line endings and path sep - return [result.replace(b"\\", b"/").replace(b"\r\n", b"\n")] + return [result.replace(b"\\", b"/").replace(b"\r\n", b"\n").decode("charmap")] def test_linear_disassembly(self): """linear_disassembly produced different result""" @@ -486,7 +590,6 @@ class TestBuilder(Builder): def test_partial_register_dataflow(self): """partial_register_dataflow produced different results""" - file_name = os.path.join(self.test_store, "partial_register_dataflow") self.unpackage_file(file_name) result = [] @@ -505,7 +608,6 @@ class TestBuilder(Builder): del bv finally: os.unlink(file_name) - return result @@ -524,20 +626,13 @@ class TestBuilder(Builder): retinfo.append("LLIL second stack element: " + str(ins.get_stack_contents_after(0,1))) retinfo.append("LLIL possible first stack element: " + str(ins.get_possible_stack_contents(0,1))) retinfo.append("LLIL possible second stack element: " + str(ins.get_possible_stack_contents_after(0,1))) - - for flag in flag_list: retinfo.append("LLIL flag {} value at: ".format(flag, hex(ins.address)) + str(ins.get_flag_value(flag))) retinfo.append("LLIL flag {} value after {}: ".format(flag, hex(ins.address)) + str(ins.get_flag_value_after(flag))) - retinfo.append("LLIL flag {} possible value at {}: ".format(flag, hex(ins.address)) + str(ins.get_possible_flag_values(flag))) retinfo.append("LLIL flag {} possible value after {}: ".format(flag, hex(ins.address)) + str(ins.get_possible_flag_values_after(flag))) - - os.unlink(file_name) - os.unlink(file_name) - - return retinfo + return fixOutput(retinfo) def test_med_il_stack(self): """MLIL stack produced different output""" @@ -555,25 +650,21 @@ class TestBuilder(Builder): retinfo.append("MLIL second stack element: " + str(ins.get_stack_contents_after(0, 1))) retinfo.append("MLIL possible first stack element: " + str(ins.get_possible_stack_contents(0, 1))) retinfo.append("MLIL possible second stack element: " + str(ins.get_possible_stack_contents_after(0, 1))) - + for reg in reg_list: retinfo.append("MLIL reg {} var at {}: ".format(reg, hex(ins.address)) + str(ins.get_var_for_reg(reg))) retinfo.append("MLIL reg {} value at {}: ".format(reg, hex(ins.address)) + str(ins.get_reg_value(reg))) retinfo.append("MLIL reg {} value after {}: ".format(reg, hex(ins.address)) + str(ins.get_reg_value_after(reg))) - retinfo.append("MLIL reg {} possible value at {}: ".format(reg, hex(ins.address)) + str(ins.get_possible_reg_values(reg))) - retinfo.append("MLIL reg {} possible value after {}: ".format(reg, hex(ins.address)) + str(ins.get_possible_reg_values_after(reg))) - + retinfo.append("MLIL reg {} possible value at {}: ".format(reg, hex(ins.address)) + fixSet(str(ins.get_possible_reg_values(reg)))) + retinfo.append("MLIL reg {} possible value after {}: ".format(reg, hex(ins.address)) + fixSet(str(ins.get_possible_reg_values_after(reg)))) for flag in flag_list: retinfo.append("MLIL flag {} value at: ".format(flag, hex(ins.address)) + str(ins.get_flag_value(flag))) retinfo.append("MLIL flag {} value after {}: ".format(flag, hex(ins.address)) + str(ins.get_flag_value_after(flag))) - retinfo.append("MLIL flag {} possible value at {}: ".format(flag, hex(ins.address)) + str(ins.get_possible_flag_values(flag))) - retinfo.append("MLIL flag {} possible value after {}: ".format(flag, hex(ins.address)) + str(ins.get_possible_flag_values(flag))) - + retinfo.append("MLIL flag {} possible value at {}: ".format(flag, hex(ins.address)) + fixSet(str(ins.get_possible_flag_values(flag)))) + retinfo.append("MLIL flag {} possible value after {}: ".format(flag, hex(ins.address)) + fixSet(str(ins.get_possible_flag_values(flag)))) os.unlink(file_name) - - return retinfo - + return fixOutput(retinfo) def test_events(self): """Event failure""" @@ -582,11 +673,9 @@ class TestBuilder(Builder): bv = binja.BinaryViewType['ELF'].open(file_name) results = [] - def simple_complete(self): results.append("analysis complete") - evt = binja.AnalysisCompletionEvent(bv, simple_complete) class NotifyTest(binja.BinaryDataNotification): @@ -643,7 +732,13 @@ class TestBuilder(Builder): def string_found(self, view, string_type, offset, length): def string_found_complete(self): - results.append("string found: offset {0} length {1}".format(hex(offset), hex(length))) + offset = hex(offset) + length = hex(length) + if offset[-1] == 'L': + offset = offset[:-1] + if length[-1] == 'L': + length = length[:-1] + results.append("string found: offset {0} length {1}".format(offset, length)) evt = binja.AnalysisCompletionEvent(bv, string_found_complete) def string_removed(self, view, string_type, offset, length): @@ -666,7 +761,6 @@ class TestBuilder(Builder): bv.register_notification(test) sacrificial_addr = 0x84fc - type, name = bv.parse_type_string("int foo") type_id = type.generate_auto_type_id("source", name) bv.define_type(type_id, name, type) @@ -684,10 +778,10 @@ class TestBuilder(Builder): bv.remove(sacrificial_addr, 4) bv.update_analysis_and_wait() - + bv.unregister_notification(test) - return sorted(results) + return fixOutput(sorted(results)) def unpackage(self, fileName): testname = None diff --git a/suite/unit.py b/suite/unit.py index 9a59b13f..fe2c4b8c 100644 --- a/suite/unit.py +++ b/suite/unit.py @@ -1,24 +1,77 @@ #!/usr/bin/env python # This is an auto generated unit test file do not edit directly import os +import sys import unittest import pickle import zipfile import testcommon -import binaryninja import api_test import difflib +from collections import Counter +global verbose +verbose = False class TestBinaryNinjaAPI(unittest.TestCase): + # Returns a tuple of: + # bool : Two lists are equal + # string : The string diff + # Args: + # list + # list : (compare list one vs list two) + # string : anything additional wanted to be printed before the string diff + # bool : the ordering of the items in the two lists must be the same + def report(self, oracle, test, firstText='', strictOrdering = False): + stringDiff = "" + + equality = False + if not strictOrdering: + equality = (Counter(oracle) == Counter(test)) + else: + equality = (oracle == test) + + if equality: + return (True, '') + elif not strictOrdering: + try: + for elem in oracle: + test.remove(elem) + oracle.remove(elem) # If it's not in the test, it won't get here! + except ValueError: + pass + + differ = difflib.Differ(charjunk=difflib.IS_CHARACTER_JUNK) + skipped_lines = 0 + for delta in differ.compare(oracle, test): + if delta[0] == ' ': + skipped_lines += 1 + continue + if skipped_lines > 0: + stringDiff += "<---" + str(skipped_lines) + ' same lines--->\n' + skipped_lines = 0 + delta = delta.replace('\n', '') + stringDiff += delta + '\n' + + stringDiffList = stringDiff.split('\n') + + if len(stringDiffList) > 10: + if not verbose: + stringDiff = '\n'.join(line if len(line) <= 100 else line[:100] + "...and " + str(len(line) - 100) + " more characters" for line in stringDiffList[:10]) + stringDiff += '\n\n### And ' + str(len(stringDiffList)) + " more lines, use '-v' to show ###" + elif not verbose: + stringDiff = '\n'.join(line if len(line) <= 100 else line[:100] + "...and " + str(len(line) - 100) + " more characters" for line in stringDiffList) + stringDiff = '\n\n' + firstText + stringDiff + return (equality, stringDiff) + @classmethod def setUpClass(self): self.builder = testcommon.TestBuilder("suite/binaries/test_corpus") try: - #Python 2 does not have the encodings option - self.oracle_test_data = pickle.load(open(os.path.join("suite", "oracle.pkl"), "rUb"), errors="ignore") + # Python 2 does not have the encodings option + self.oracle_test_data = pickle.load(open(os.path.join("suite", "oracle.pkl"), "rb"), encoding='charmap') except TypeError: - self.oracle_test_data = pickle.load(open(os.path.join("suite", "oracle.pkl"), "rU")) + self.oracle_test_data = pickle.load(open(os.path.join("suite", "oracle.pkl"), "r")) self.verifybuilder = testcommon.VerifyBuilder("suite/binaries/test_corpus") def run_binary_test(self, testfile): @@ -29,10 +82,10 @@ class TestBinaryNinjaAPI(unittest.TestCase): self.assertTrue(os.path.exists(testname + ".pkl"), "Test pickle doesn't exist") try: - #Python 2 does not have the encodings option - binary_oracle = pickle.load(open(testname + ".pkl", "rUb"), errors="ignore") + # Python 2 does not have the encodings option + binary_oracle = pickle.load(open(testname + ".pkl", "rb"), encoding='charmap') except TypeError: - binary_oracle = pickle.load(open(testname + ".pkl", "rU")) + binary_oracle = pickle.load(open(testname + ".pkl", "r")) test_builder = testcommon.BinaryViewTestBuilder(testname, "suite/binaries/test_corpus") for method in test_builder.methods(): @@ -43,271 +96,93 @@ class TestBinaryNinjaAPI(unittest.TestCase): result = getattr(test_builder, method).__doc__ result += ":\n" - d = difflib.Differ(charjunk=difflib.IS_CHARACTER_JUNK) - skipped_lines = 0 - for delta in d.compare(test, oracle): - if delta[0] == ' ': - skipped_lines += 1 - continue - if skipped_lines > 0: - result += "<---" + str(skipped_lines) + ' same lines--->\n' - skipped_lines = 0 - delta = delta.replace('\n', '') - result += delta + '\n' - self.assertTrue(False, result) + report = self.report(oracle, test, result) + self.assertTrue(report[0], report[1]) # Test does not agree with oracle os.unlink(testname) def test_Architecture(self): oracle = self.oracle_test_data['test_Architecture'] test = self.builder.test_Architecture() - result = "" - differ = difflib.Differ(charjunk=difflib.IS_CHARACTER_JUNK) - skipped_lines = 0 - for delta in differ.compare(test, oracle): - if delta[0] == ' ': - skipped_lines += 1 - continue - if skipped_lines > 0: - result += "<---" + str(skipped_lines) + ' same lines--->\n' - skipped_lines = 0 - delta = delta.replace('\n', '') - result += delta + '\n' - - self.assertTrue(oracle == test, result) + report = self.report(oracle, test) + self.assertTrue(report[0], report[1]) # Test does not agree with oracle def test_Architecture_list(self): oracle = self.oracle_test_data['test_Architecture_list'] test = self.builder.test_Architecture_list() - result = "" - differ = difflib.Differ(charjunk=difflib.IS_CHARACTER_JUNK) - skipped_lines = 0 - for delta in differ.compare(test, oracle): - if delta[0] == ' ': - skipped_lines += 1 - continue - if skipped_lines > 0: - result += "<---" + str(skipped_lines) + ' same lines--->\n' - skipped_lines = 0 - delta = delta.replace('\n', '') - result += delta + '\n' - - self.assertTrue(oracle == test, result) + report = self.report(oracle, test) + self.assertTrue(report[0], report[1]) # Test does not agree with oracle def test_Assemble(self): oracle = self.oracle_test_data['test_Assemble'] test = self.builder.test_Assemble() - result = "" - differ = difflib.Differ(charjunk=difflib.IS_CHARACTER_JUNK) - skipped_lines = 0 - for delta in differ.compare(test, oracle): - if delta[0] == ' ': - skipped_lines += 1 - continue - if skipped_lines > 0: - result += "<---" + str(skipped_lines) + ' same lines--->\n' - skipped_lines = 0 - delta = delta.replace('\n', '') - result += delta + '\n' - - self.assertTrue(oracle == test, result) + report = self.report(oracle, test) + self.assertTrue(report[0], report[1]) # Test does not agree with oracle def test_BinaryViewType_list(self): oracle = self.oracle_test_data['test_BinaryViewType_list'] test = self.builder.test_BinaryViewType_list() - result = "" - differ = difflib.Differ(charjunk=difflib.IS_CHARACTER_JUNK) - skipped_lines = 0 - for delta in differ.compare(test, oracle): - if delta[0] == ' ': - skipped_lines += 1 - continue - if skipped_lines > 0: - result += "<---" + str(skipped_lines) + ' same lines--->\n' - skipped_lines = 0 - delta = delta.replace('\n', '') - result += delta + '\n' - - self.assertTrue(oracle == test, result) + report = self.report(oracle, test) + self.assertTrue(report[0], report[1]) # Test does not agree with oracle def test_Enumeration(self): oracle = self.oracle_test_data['test_Enumeration'] test = self.builder.test_Enumeration() - result = "" - differ = difflib.Differ(charjunk=difflib.IS_CHARACTER_JUNK) - skipped_lines = 0 - for delta in differ.compare(test, oracle): - if delta[0] == ' ': - skipped_lines += 1 - continue - if skipped_lines > 0: - result += "<---" + str(skipped_lines) + ' same lines--->\n' - skipped_lines = 0 - delta = delta.replace('\n', '') - result += delta + '\n' - - self.assertTrue(oracle == test, result) + report = self.report(oracle, test) + self.assertTrue(report[0], report[1]) # Test does not agree with oracle def test_Function(self): oracle = self.oracle_test_data['test_Function'] test = self.builder.test_Function() - result = "" - differ = difflib.Differ(charjunk=difflib.IS_CHARACTER_JUNK) - skipped_lines = 0 - for delta in differ.compare(test, oracle): - if delta[0] == ' ': - skipped_lines += 1 - continue - if skipped_lines > 0: - result += "<---" + str(skipped_lines) + ' same lines--->\n' - skipped_lines = 0 - delta = delta.replace('\n', '') - result += delta + '\n' - - self.assertTrue(oracle == test, result) + report = self.report(oracle, test) + self.assertTrue(report[0], report[1]) # Test does not agree with oracle def test_Plugin_bin_info(self): oracle = self.oracle_test_data['test_Plugin_bin_info'] test = self.builder.test_Plugin_bin_info() - result = "" - differ = difflib.Differ(charjunk=difflib.IS_CHARACTER_JUNK) - skipped_lines = 0 - for delta in differ.compare(test, oracle): - if delta[0] == ' ': - skipped_lines += 1 - continue - if skipped_lines > 0: - result += "<---" + str(skipped_lines) + ' same lines--->\n' - skipped_lines = 0 - delta = delta.replace('\n', '') - result += delta + '\n' - - self.assertTrue(oracle == test, result) + report = self.report(oracle, test) + self.assertTrue(report[0], report[1]) # Test does not agree with oracle def test_Struct(self): oracle = self.oracle_test_data['test_Struct'] test = self.builder.test_Struct() - result = "" - differ = difflib.Differ(charjunk=difflib.IS_CHARACTER_JUNK) - skipped_lines = 0 - for delta in differ.compare(test, oracle): - if delta[0] == ' ': - skipped_lines += 1 - continue - if skipped_lines > 0: - result += "<---" + str(skipped_lines) + ' same lines--->\n' - skipped_lines = 0 - delta = delta.replace('\n', '') - result += delta + '\n' - - self.assertTrue(oracle == test, result) + report = self.report(oracle, test) + self.assertTrue(report[0], report[1]) # Test does not agree with oracle def test_Types(self): oracle = self.oracle_test_data['test_Types'] test = self.builder.test_Types() - result = "" - differ = difflib.Differ(charjunk=difflib.IS_CHARACTER_JUNK) - skipped_lines = 0 - for delta in differ.compare(test, oracle): - if delta[0] == ' ': - skipped_lines += 1 - continue - if skipped_lines > 0: - result += "<---" + str(skipped_lines) + ' same lines--->\n' - skipped_lines = 0 - delta = delta.replace('\n', '') - result += delta + '\n' - - self.assertTrue(oracle == test, result) + report = self.report(oracle, test) + self.assertTrue(report[0], report[1]) # Test does not agree with oracle def test_events(self): oracle = self.oracle_test_data['test_events'] test = self.builder.test_events() - result = "" - differ = difflib.Differ(charjunk=difflib.IS_CHARACTER_JUNK) - skipped_lines = 0 - for delta in differ.compare(test, oracle): - if delta[0] == ' ': - skipped_lines += 1 - continue - if skipped_lines > 0: - result += "<---" + str(skipped_lines) + ' same lines--->\n' - skipped_lines = 0 - delta = delta.replace('\n', '') - result += delta + '\n' - - self.assertTrue(oracle == test, result) + report = self.report(oracle, test) + self.assertTrue(report[0], report[1]) # Test does not agree with oracle def test_linear_disassembly(self): oracle = self.oracle_test_data['test_linear_disassembly'] test = self.builder.test_linear_disassembly() - result = "" - differ = difflib.Differ(charjunk=difflib.IS_CHARACTER_JUNK) - skipped_lines = 0 - for delta in differ.compare(test, oracle): - if delta[0] == ' ': - skipped_lines += 1 - continue - if skipped_lines > 0: - result += "<---" + str(skipped_lines) + ' same lines--->\n' - skipped_lines = 0 - delta = delta.replace('\n', '') - result += delta + '\n' - - self.assertTrue(oracle == test, result) + report = self.report(oracle, test) + self.assertTrue(report[0], report[1]) # Test does not agree with oracle def test_low_il_stack(self): oracle = self.oracle_test_data['test_low_il_stack'] test = self.builder.test_low_il_stack() - result = "" - differ = difflib.Differ(charjunk=difflib.IS_CHARACTER_JUNK) - skipped_lines = 0 - for delta in differ.compare(test, oracle): - if delta[0] == ' ': - skipped_lines += 1 - continue - if skipped_lines > 0: - result += "<---" + str(skipped_lines) + ' same lines--->\n' - skipped_lines = 0 - delta = delta.replace('\n', '') - result += delta + '\n' - - self.assertTrue(oracle == test, result) + report = self.report(oracle, test) + self.assertTrue(report[0], report[1]) # Test does not agree with oracle def test_med_il_stack(self): oracle = self.oracle_test_data['test_med_il_stack'] test = self.builder.test_med_il_stack() - result = "" - differ = difflib.Differ(charjunk=difflib.IS_CHARACTER_JUNK) - skipped_lines = 0 - for delta in differ.compare(test, oracle): - if delta[0] == ' ': - skipped_lines += 1 - continue - if skipped_lines > 0: - result += "<---" + str(skipped_lines) + ' same lines--->\n' - skipped_lines = 0 - delta = delta.replace('\n', '') - result += delta + '\n' - - self.assertTrue(oracle == test, result) + report = self.report(oracle, test) + self.assertTrue(report[0], report[1]) # Test does not agree with oracle def test_partial_register_dataflow(self): oracle = self.oracle_test_data['test_partial_register_dataflow'] test = self.builder.test_partial_register_dataflow() - result = "" - differ = difflib.Differ(charjunk=difflib.IS_CHARACTER_JUNK) - skipped_lines = 0 - for delta in differ.compare(test, oracle): - if delta[0] == ' ': - skipped_lines += 1 - continue - if skipped_lines > 0: - result += "<---" + str(skipped_lines) + ' same lines--->\n' - skipped_lines = 0 - delta = delta.replace('\n', '') - result += delta + '\n' - - self.assertTrue(oracle == test, result) + report = self.report(oracle, test) + self.assertTrue(report[0], report[1]) # Test does not agree with oracle def test_verify_BNDB_round_trip(self): self.assertTrue(self.verifybuilder.test_verify_BNDB_round_trip(), self.test_verify_BNDB_round_trip.__doc__) @@ -386,6 +261,10 @@ class TestBinaryNinjaAPI(unittest.TestCase): if __name__ == "__main__": + if len(sys.argv) > 1: + if sys.argv[1] == '-v' or sys.argv[1] == '-V' or sys.argv[1] == '--verbose': + verbose = True + test_suite = unittest.defaultTestLoader.loadTestsFromModule(api_test) test_suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestBinaryNinjaAPI)) runner = unittest.TextTestRunner(verbosity=2) |
