diff options
| author | Rusty Wagner <rusty@vector35.com> | 2017-12-26 17:14:16 -0500 |
|---|---|---|
| committer | Rusty Wagner <rusty@vector35.com> | 2017-12-26 17:14:16 -0500 |
| commit | 9fc4766a507d5299cd4de78170c5798b02bb9698 (patch) | |
| tree | a4589f36a46e662cc16613db88f1c83c69512181 /python | |
| parent | 06b97009b2a09dc7816f6ace2d6a4bff66cb3f26 (diff) | |
| parent | 26edabfdd7211012c7c8a03186d3025eea9aa345 (diff) | |
Merge branch 'dev' into fpu
Diffstat (limited to 'python')
| -rw-r--r-- | python/__init__.py | 7 | ||||
| -rw-r--r-- | python/architecture.py | 15 | ||||
| -rw-r--r-- | python/basicblock.py | 11 | ||||
| -rw-r--r-- | python/binaryview.py | 56 | ||||
| -rw-r--r-- | python/examples/nes.py | 1 | ||||
| -rw-r--r-- | python/function.py | 86 | ||||
| -rw-r--r-- | python/functionrecognizer.py | 15 | ||||
| -rw-r--r-- | python/interaction.py | 2 | ||||
| -rw-r--r-- | python/lowlevelil.py | 3 | ||||
| -rw-r--r-- | python/mediumlevelil.py | 9 | ||||
| -rw-r--r-- | python/pluginmanager.py | 1 | ||||
| -rw-r--r-- | python/scriptingprovider.py | 23 | ||||
| -rw-r--r-- | python/update.py | 2 |
13 files changed, 196 insertions, 35 deletions
diff --git a/python/__init__.py b/python/__init__.py index f4a8fac8..729e4f8a 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -21,6 +21,7 @@ import atexit import sys +from time import gmtime # Binary Ninja components import _binaryninjacore as core @@ -147,6 +148,12 @@ core_version = core.BNGetVersionString() core_build_id = core.BNGetBuildId() '''Build ID''' +core_serial = core.BNGetSerialNumber() +'''Serial Number''' + +core_expires = gmtime(core.BNGetLicenseExpirationTime()) +'''License Expiration''' + core_product = core.BNGetProduct() '''Product string from the license file''' diff --git a/python/architecture.py b/python/architecture.py index c4179b00..08049d46 100644 --- a/python/architecture.py +++ b/python/architecture.py @@ -111,6 +111,7 @@ class Architecture(object): endianness = Endianness.LittleEndian address_size = 8 default_int_size = 4 + instr_alignment = 1 max_instr_length = 16 opcode_display_length = 8 regs = {} @@ -133,6 +134,7 @@ class Architecture(object): self.__dict__["endianness"] = Endianness(core.BNGetArchitectureEndianness(self.handle)) self.__dict__["address_size"] = core.BNGetArchitectureAddressSize(self.handle) self.__dict__["default_int_size"] = core.BNGetArchitectureDefaultIntegerSize(self.handle) + self.__dict__["instr_alignment"] = core.BNGetArchitectureInstructionAlignment(self.handle) self.__dict__["max_instr_length"] = core.BNGetArchitectureMaxInstructionLength(self.handle) self.__dict__["opcode_display_length"] = core.BNGetArchitectureOpcodeDisplayLength(self.handle) self.__dict__["stack_pointer"] = core.BNGetArchitectureRegisterName(self.handle, @@ -245,6 +247,7 @@ class Architecture(object): self._cb.getEndianness = self._cb.getEndianness.__class__(self._get_endianness) self._cb.getAddressSize = self._cb.getAddressSize.__class__(self._get_address_size) self._cb.getDefaultIntegerSize = self._cb.getDefaultIntegerSize.__class__(self._get_default_integer_size) + self._cb.getInstructionAlignment = self._cb.getInstructionAlignment.__class__(self._get_instruction_alignment) self._cb.getMaxInstructionLength = self._cb.getMaxInstructionLength.__class__(self._get_max_instruction_length) self._cb.getOpcodeDisplayLength = self._cb.getOpcodeDisplayLength.__class__(self._get_opcode_display_length) self._cb.getAssociatedArchitectureByAddress = \ @@ -429,7 +432,8 @@ class Architecture(object): def __setattr__(self, name, value): if ((name == "name") or (name == "endianness") or (name == "address_size") or - (name == "default_int_size") or (name == "regs") or (name == "get_max_instruction_length")): + (name == "default_int_size") or (name == "regs") or (name == "get_max_instruction_length") or + (name == "get_instruction_alignment")): raise AttributeError("attribute '%s' is read only" % name) else: try: @@ -464,6 +468,13 @@ class Architecture(object): log.log_error(traceback.format_exc()) return 4 + def _get_instruction_alignment(self, ctxt): + try: + return self.__class__.instr_alignment + except: + log.log_error(traceback.format_exc()) + return 1 + def _get_max_instruction_length(self, ctxt): try: return self.__class__.max_instr_length @@ -495,6 +506,7 @@ class Architecture(object): if info is None: return False result[0].length = info.length + result[0].archTransitionByTargetAddr = info.arch_transition_by_target_addr result[0].branchDelay = info.branch_delay result[0].branchCount = len(info.branches) for i in xrange(0, len(info.branches)): @@ -1247,6 +1259,7 @@ class Architecture(object): return None result = function.InstructionInfo() result.length = info.length + result.arch_transition_by_target_addr = info.archTransitionByTargetAddr result.branch_delay = info.branchDelay for i in xrange(0, info.branchCount): target = info.branchTarget[i] diff --git a/python/basicblock.py b/python/basicblock.py index 26db925d..8e64c1c1 100644 --- a/python/basicblock.py +++ b/python/basicblock.py @@ -35,6 +35,14 @@ class BasicBlockEdge(object): self.target = target self.back_edge = back_edge + def __eq__(self, value): + if not isinstance(value, BasicBlockEdge): + return False + return (self.type, self.source, self.target, self.back_edge) == (value.type, value.source, value.target, value.back_edge) + + def __hash__(self): + return hash((self.type, self.source, self.target, self.back_edge)) + def __repr__(self): if self.type == BranchType.UnresolvedBranch: return "<%s>" % BranchType(self.type).name @@ -68,6 +76,9 @@ class BasicBlock(object): """Internal method used to instantiante child instances""" return BasicBlock(view, handle) + def __hash__(self): + return hash((self.start, self.end, self.arch.name)) + @property def function(self): """Basic block function (read-only)""" diff --git a/python/binaryview.py b/python/binaryview.py index 9304d412..6eb59db1 100644 --- a/python/binaryview.py +++ b/python/binaryview.py @@ -103,6 +103,17 @@ class StringReference(object): class AnalysisCompletionEvent(object): + """ + The ``AnalysisCompletionEvent`` object provides an asynchronous mechanism for receiving + callbacks when analysis is complete. + + :Example: + >>> def on_complete(self): + ... print "Analysis Complete", self.view + ... + >>> evt = AnalysisCompletionEvent(bv, on_complete) + >>> + """ def __init__(self, view, callback): self.view = view self.callback = callback @@ -114,7 +125,7 @@ class AnalysisCompletionEvent(object): def _notify(self, ctxt): try: - self.callback() + self.callback(self) except: log.log_error(traceback.format_exc()) @@ -405,12 +416,13 @@ class BinaryViewType(object): class Segment(object): - def __init__(self, start, length, data_offset, data_length, flags): + def __init__(self, start, length, data_offset, data_length, flags, auto_defined): self.start = start self.length = length self.data_offset = data_offset self.data_length = data_length self.flags = flags + self.auto_defined = auto_defined @property def executable(self): @@ -439,7 +451,7 @@ class Segment(object): class Section(object): - def __init__(self, name, section_type, start, length, linked_section, info_section, info_data, align, entry_size, semantics): + def __init__(self, name, section_type, start, length, linked_section, info_section, info_data, align, entry_size, semantics, auto_defined): self.name = name self.type = section_type self.start = start @@ -450,6 +462,7 @@ class Section(object): self.align = align self.entry_size = entry_size self.semantics = SectionSemantics(semantics) + self.auto_defined = auto_defined @property def end(self): @@ -572,6 +585,7 @@ class BinaryView(object): self._cb.getEntryPoint = self._cb.getEntryPoint.__class__(self._get_entry_point) self._cb.isExecutable = self._cb.isExecutable.__class__(self._is_executable) self._cb.getDefaultEndianness = self._cb.getDefaultEndianness.__class__(self._get_default_endianness) + self._cb.isRelocatable = self._cb.isRelocatable.__class__(self._is_relocatable) self._cb.getAddressSize = self._cb.getAddressSize.__class__(self._get_address_size) self._cb.save = self._cb.save.__class__(self._save) self.file = file_metadata @@ -828,6 +842,11 @@ class BinaryView(object): return Endianness(core.BNGetDefaultEndianness(self.handle)) @property + def relocatable(self): + """Boolean - is the binary relocatable (read-only)""" + return core.BNIsRelocatable(self.handle) + + @property def address_size(self): """Address size of the binary (read-only)""" return core.BNGetViewAddressSize(self.handle) @@ -948,7 +967,7 @@ class BinaryView(object): result = [] for i in xrange(0, count.value): result.append(Segment(segment_list[i].start, segment_list[i].length, - segment_list[i].dataOffset, segment_list[i].dataLength, segment_list[i].flags)) + segment_list[i].dataOffset, segment_list[i].dataLength, segment_list[i].flags, segment_list[i].autoDefined)) core.BNFreeSegmentList(segment_list) return result @@ -962,7 +981,7 @@ class BinaryView(object): result[section_list[i].name] = Section(section_list[i].name, section_list[i].type, section_list[i].start, section_list[i].length, section_list[i].linkedSection, section_list[i].infoSection, section_list[i].infoData, section_list[i].align, section_list[i].entrySize, - section_list[i].semantics) + section_list[i].semantics, section_list[i].autoDefined) core.BNFreeSectionList(section_list, count.value) return result @@ -1191,6 +1210,13 @@ class BinaryView(object): log.log_error(traceback.format_exc()) return Endianness.LittleEndian + def _is_relocatable(self, ctxt): + try: + return self.perform_is_relocatable() + except: + log.log_error(traceback.format_exc()) + return False + def _get_address_size(self, ctxt): try: return self.perform_get_address_size() @@ -1483,6 +1509,19 @@ class BinaryView(object): """ return Endianness.LittleEndian + def perform_is_relocatable(self): + """ + ``perform_is_relocatable`` implements a check which returns true if the BinaryView is relocatable. Defaults to + True. + + .. note:: This method **may** be implemented for custom BinaryViews that are relocatable. + .. warning:: This method **must not** be called directly. + + :return: True if the BinaryView is relocatable, False otherwise + :rtype: boolean + """ + return True + def create_database(self, filename, progress_func=None): """ ``create_database`` writes the current database (.bndb) file out to the specified file. @@ -3316,7 +3355,7 @@ class BinaryView(object): if not core.BNGetSegmentAt(self.handle, addr, segment): return None result = Segment(segment.start, segment.length, segment.dataOffset, segment.dataLength, - segment.flags) + segment.flags, segment.autoDefined) return result def get_address_for_data_offset(self, offset): @@ -3349,7 +3388,7 @@ class BinaryView(object): result.append(Section(section_list[i].name, section_list[i].type, section_list[i].start, section_list[i].length, section_list[i].linkedSection, section_list[i].infoSection, section_list[i].infoData, section_list[i].align, section_list[i].entrySize, - section_list[i].semantics)) + section_list[i].semantics, section_list[i].autoDefined)) core.BNFreeSectionList(section_list, count.value) return result @@ -3358,7 +3397,8 @@ class BinaryView(object): if not core.BNGetSectionByName(self.handle, name, section): return None result = Section(section.name, section.type, section.start, section.length, section.linkedSection, - section.infoSection, section.infoData, section.align, section.entrySize, section.semantics) + section.infoSection, section.infoData, section.align, section.entrySize, section.semantics, + section_list.autoDefined) core.BNFreeSection(section) return result diff --git a/python/examples/nes.py b/python/examples/nes.py index e55a90b7..39544c9f 100644 --- a/python/examples/nes.py +++ b/python/examples/nes.py @@ -367,6 +367,7 @@ class M6502(Architecture): name = "6502" address_size = 2 default_int_size = 1 + instr_alignment = 1 max_instr_length = 3 regs = { "a": RegisterInfo("a", 1), diff --git a/python/function.py b/python/function.py index db4ca0f5..10583b58 100644 --- a/python/function.py +++ b/python/function.py @@ -171,6 +171,8 @@ class PossibleValueSet(object): self.reg = arch.get_reg_name(value.value) elif value.state == RegisterValueType.ConstantValue: self.value = value.value + elif value.state == RegisterValueType.ConstantPointerValue: + self.value = value.value elif value.state == RegisterValueType.StackFrameOffset: self.offset = value.value elif value.state == RegisterValueType.SignedRangeValue: @@ -212,6 +214,8 @@ class PossibleValueSet(object): return "<entry %s>" % self.reg if self.type == RegisterValueType.ConstantValue: return "<const %#x>" % self.value + if self.type == RegisterValueType.ConstantPointerValue: + return "<const ptr %#x>" % self.value if self.type == RegisterValueType.StackFrameOffset: return "<stack frame offset %#x>" % self.offset if self.type == RegisterValueType.SignedRangeValue: @@ -290,10 +294,10 @@ class Variable(object): return self.name def __eq__(self, other): - return self.identifier == other.identifier + return (self.identifier, self.function) == (other.identifier, other.function) def __hash__(self): - return hash(self.identifier) + return hash((self.identifier, self.function)) class ConstantReference(object): @@ -356,6 +360,8 @@ class Function(object): self._view = view self.handle = core.handle_of_type(handle, core.BNFunction) self._advanced_analysis_requests = 0 + self._arch = None + self._platform = None def __del__(self): if self._advanced_analysis_requests > 0: @@ -407,18 +413,26 @@ class Function(object): @property def arch(self): """Function architecture (read-only)""" - arch = core.BNGetFunctionArchitecture(self.handle) - if arch is None: - return None - return architecture.Architecture(arch) + if self._arch: + return self._arch + else: + arch = core.BNGetFunctionArchitecture(self.handle) + if arch is None: + return None + self._arch = architecture.Architecture(arch) + return self._arch @property def platform(self): """Function platform (read-only)""" - plat = core.BNGetFunctionPlatform(self.handle) - if plat is None: - return None - return platform.Platform(None, handle = plat) + if self._platform: + return self._platform + else: + plat = core.BNGetFunctionPlatform(self.handle) + if plat is None: + return None + self._platform = platform.Platform(None, handle = plat) + return self._platform @property def start(self): @@ -768,6 +782,41 @@ class Function(object): """Sets a comment for the current function""" return core.BNSetFunctionComment(self.handle, comment) + @property + def llil_basic_blocks(self): + """A generator of all LowLevelILBasicBlock objects in the current function""" + for block in self.low_level_il: + yield block + + @property + def mlil_basic_blocks(self): + """A generator of all MediumLevelILBasicBlock objects in the current function""" + for block in self.medium_level_il: + yield block + + @property + def instructions(self): + """A generator of instruction tokens and their start addresses for the current function""" + for block in self.basic_blocks: + start = block.start + for i in block: + yield (i[0], start) + start += i[1] + + @property + def llil_instructions(self): + """A generator of llil instructions of the current function""" + for block in self.llil_basic_blocks: + for i in block: + yield i + + @property + def mlil_instructions(self): + """A generator of mlil instructions of the current function""" + for block in self.mlil_basic_blocks: + for i in block: + yield i + def __iter__(self): count = ctypes.c_ulonglong() blocks = core.BNGetFunctionBasicBlockList(self.handle, count) @@ -829,7 +878,13 @@ class Function(object): """ if arch is None: arch = self.arch - return self.low_level_il[core.BNGetLowLevelILForInstruction(self.handle, arch.handle, addr)] + + idx = core.BNGetLowLevelILForInstruction(self.handle, arch.handle, addr) + + if idx == len(self.low_level_il): + return None + + return self.low_level_il[idx] def get_low_level_il_exits_at(self, addr, arch=None): if arch is None: @@ -980,7 +1035,13 @@ class Function(object): def get_lifted_il_at(self, addr, arch=None): if arch is None: arch = self.arch - return self.lifted_il[core.BNGetLiftedILForInstruction(self.handle, arch.handle, addr)] + + idx = core.BNGetLiftedILForInstruction(self.handle, arch.handle, addr) + + if idx == len(self.lifted_il): + return None + + return self.lifted_il[idx] def get_lifted_il_flag_uses_for_definition(self, i, flag): flag = self.arch.get_flag_index(flag) @@ -1833,6 +1894,7 @@ class InstructionBranch(object): class InstructionInfo(object): def __init__(self): self.length = 0 + self.arch_transition_by_target_addr = False self.branch_delay = False self.branches = [] diff --git a/python/functionrecognizer.py b/python/functionrecognizer.py index 8514a2ee..4ac304ca 100644 --- a/python/functionrecognizer.py +++ b/python/functionrecognizer.py @@ -36,6 +36,7 @@ class FunctionRecognizer(object): self._cb = core.BNFunctionRecognizer() self._cb.context = 0 self._cb.recognizeLowLevelIL = self._cb.recognizeLowLevelIL.__class__(self._recognize_low_level_il) + self._cb.recognizeMediumLevelIL = self._cb.recognizeMediumLevelIL.__class__(self._recognize_medium_level_il) @classmethod def register_global(cls): @@ -62,3 +63,17 @@ class FunctionRecognizer(object): def recognize_low_level_il(self, data, func, il): return False + + def _recognize_medium_level_il(self, ctxt, data, func, il): + try: + file_metadata = filemetadata.FileMetadata(handle = core.BNGetFileForView(data)) + view = binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(data)) + func = function.Function(view, handle = core.BNNewFunctionReference(func)) + il = mediumlevelil.MediumLevelILFunction(func.arch, handle = core.BNNewMediumLevelILFunctionReference(il)) + return self.recognize_medium_level_il(view, func, il) + except: + log.log_error(traceback.format_exc()) + return False + + def recognize_medium_level_il(self, data, func, il): + return False diff --git a/python/interaction.py b/python/interaction.py index 979549f5..96cac42d 100644 --- a/python/interaction.py +++ b/python/interaction.py @@ -520,7 +520,7 @@ def show_html_report(title, contents, plaintext=""): :param str contents: HTML contents to display :param str plaintext: Plain text version to display (used on the command line) :rtype: None - :Example" + :Example: >>> show_html_report("title", "<h1>Contents</h1>", "Plain text contents") Plain text contents """ diff --git a/python/lowlevelil.py b/python/lowlevelil.py index 3810e742..42c34ee0 100644 --- a/python/lowlevelil.py +++ b/python/lowlevelil.py @@ -2171,6 +2171,9 @@ class LowLevelILBasicBlock(basicblock.BasicBlock): """Internal method by super to instantiante child instances""" return LowLevelILBasicBlock(view, handle, self.il_function) + def __hash__(self): + return hash((self.start, self.end, self.il_function)) + def LLIL_TEMP(n): return n | 0x80000000 diff --git a/python/mediumlevelil.py b/python/mediumlevelil.py index 144c7e0b..85feba0a 100644 --- a/python/mediumlevelil.py +++ b/python/mediumlevelil.py @@ -40,12 +40,12 @@ class SSAVariable(object): def __eq__(self, other): return ( - (self.var.identifier, self.version) == - (other.var.identifier, other.version) + (self.var, self.version) == + (other.var, other.version) ) def __hash__(self): - return hash((self.var.identifier, self.version)) + return hash((self.var, self.version)) class MediumLevelILLabel(object): @@ -921,3 +921,6 @@ class MediumLevelILBasicBlock(basicblock.BasicBlock): def _create_instance(self, view, handle): """Internal method by super to instantiante child instances""" return MediumLevelILBasicBlock(view, handle, self.il_function) + + def __hash__(self): + return hash((self.start, self.end, self.il_function)) diff --git a/python/pluginmanager.py b/python/pluginmanager.py index 6896d699..2f293577 100644 --- a/python/pluginmanager.py +++ b/python/pluginmanager.py @@ -382,7 +382,6 @@ class RepositoryManager(object): >>> mgr = RepositoryManager() >>> mgr.add_repository(url="https://github.com/vector35/community-plugins.git", repopath="myrepo", - repomanifest="plugins", localreference="master", remotereference="origin") True >>> diff --git a/python/scriptingprovider.py b/python/scriptingprovider.py index ed9688b9..c92c249a 100644 --- a/python/scriptingprovider.py +++ b/python/scriptingprovider.py @@ -42,14 +42,14 @@ class _ThreadActionContext(object): def __init__(self, func): self.func = func self.interpreter = None - if "value" in dir(PythonScriptingInstance._interpreter): + if hasattr(PythonScriptingInstance._interpreter, "value"): self.interpreter = PythonScriptingInstance._interpreter.value self.__class__._actions.append(self) self.callback = ctypes.CFUNCTYPE(None, ctypes.c_void_p)(lambda ctxt: self.execute()) def execute(self): old_interpreter = None - if "value" in dir(PythonScriptingInstance._interpreter): + if hasattr(PythonScriptingInstance._interpreter, "value"): old_interpreter = PythonScriptingInstance._interpreter.value PythonScriptingInstance._interpreter.value = self.interpreter try: @@ -380,7 +380,7 @@ class _PythonScriptingInstanceOutput(object): def write(self, data): interpreter = None - if "value" in dir(PythonScriptingInstance._interpreter): + if hasattr(PythonScriptingInstance._interpreter, "value"): interpreter = PythonScriptingInstance._interpreter.value if interpreter is None: @@ -419,7 +419,7 @@ class _PythonScriptingInstanceInput(object): def read(self, size): interpreter = None - if "value" in dir(PythonScriptingInstance._interpreter): + if hasattr(PythonScriptingInstance._interpreter, "value"): interpreter = PythonScriptingInstance._interpreter.value if interpreter is None: @@ -434,7 +434,7 @@ class _PythonScriptingInstanceInput(object): def readline(self): interpreter = None - if "value" in dir(PythonScriptingInstance._interpreter): + if hasattr(PythonScriptingInstance._interpreter, "value"): interpreter = PythonScriptingInstance._interpreter.value if interpreter is None: @@ -457,7 +457,7 @@ class PythonScriptingInstance(ScriptingInstance): super(PythonScriptingInstance.InterpreterThread, self).__init__() self.instance = instance self.locals = {"__name__": "__console__", "__doc__": None, "binaryninja": sys.modules[__name__]} - self.interpreter = code.InteractiveInterpreter(self.locals) + self.interpreter = code.InteractiveConsole(self.locals) self.event = threading.Event() self.daemon = True @@ -484,7 +484,7 @@ class PythonScriptingInstance(ScriptingInstance): self.code = None self.input = "" - self.interpreter.runsource("from binaryninja import *\n") + self.interpreter.push("from binaryninja import *\n") def execute(self, code): self.code = code @@ -540,8 +540,15 @@ class PythonScriptingInstance(ScriptingInstance): self.locals["current_address"] = self.active_addr self.locals["here"] = self.active_addr self.locals["current_selection"] = (self.active_selection_begin, self.active_selection_end) + if self.active_func == None: + self.locals["current_llil"] = None + self.locals["current_mlil"] = None + else: + self.locals["current_llil"] = self.active_func.low_level_il + self.locals["current_mlil"] = self.active_func.medium_level_il - self.interpreter.runsource(code) + for line in code.split("\n"): + self.interpreter.push(line) if self.locals["here"] != self.active_addr: if not self.active_view.file.navigate(self.active_view.file.view, self.locals["here"]): diff --git a/python/update.py b/python/update.py index be6962d7..1eb8ea61 100644 --- a/python/update.py +++ b/python/update.py @@ -154,7 +154,7 @@ class UpdateChannel(object): def updates_available(self): """Whether updates are available (read-only)""" errors = ctypes.c_char_p() - result = core.BNAreUpdatesAvailable(self.name, errors) + result = core.BNAreUpdatesAvailable(self.name, None, None, errors) if errors: error_str = errors.value core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) |
