diff options
| author | Rusty Wagner <rusty@vector35.com> | 2017-08-31 21:41:25 -0400 |
|---|---|---|
| committer | Rusty Wagner <rusty@vector35.com> | 2017-08-31 21:41:25 -0400 |
| commit | 7cbb40a71ffb2583862191b7999e436807f9a0e8 (patch) | |
| tree | 25846f8d0e811b16b28291aeaf6359bee51e28c4 /python | |
| parent | 980e2f090fb47f7f71a46b03e8c636819f3214ec (diff) | |
| parent | 0b30396eb319e89e4f69d9cbac12fc3d4b453f53 (diff) | |
Merge branch 'dev'
Diffstat (limited to 'python')
| -rw-r--r-- | python/__init__.py | 50 | ||||
| -rw-r--r-- | python/architecture.py | 130 | ||||
| -rw-r--r-- | python/basicblock.py | 50 | ||||
| -rw-r--r-- | python/binaryview.py | 232 | ||||
| -rw-r--r-- | python/callingconvention.py | 114 | ||||
| -rw-r--r-- | python/examples/angr_plugin.py | 4 | ||||
| -rw-r--r-- | python/function.py | 422 | ||||
| -rw-r--r-- | python/generator.cpp | 72 | ||||
| -rw-r--r-- | python/interaction.py | 257 | ||||
| -rw-r--r-- | python/lineardisassembly.py | 2 | ||||
| -rw-r--r-- | python/lowlevelil.py | 52 | ||||
| -rw-r--r-- | python/mediumlevelil.py | 73 | ||||
| -rw-r--r-- | python/metadata.py | 266 | ||||
| -rw-r--r-- | python/platform.py | 121 | ||||
| -rw-r--r-- | python/pluginmanager.py | 17 | ||||
| -rw-r--r-- | python/scriptingprovider.py | 7 | ||||
| -rw-r--r-- | python/setting.py | 141 | ||||
| -rw-r--r-- | python/types.py | 314 |
18 files changed, 2030 insertions, 294 deletions
diff --git a/python/__init__.py b/python/__init__.py index 4f58a6db..f4a8fac8 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -19,6 +19,9 @@ # IN THE SOFTWARE. +import atexit +import sys + # Binary Ninja components import _binaryninjacore as core from .enums import * @@ -47,6 +50,8 @@ from .undoaction import * from .highlight import * from .scriptingprovider import * from .pluginmanager import * +from .setting import * +from .metadata import * def shutdown(): @@ -56,6 +61,9 @@ def shutdown(): core.BNShutdown() +atexit.register(shutdown) + + def get_unique_identifier(): return core.BNGetUniqueIdentifierString() @@ -64,11 +72,51 @@ def get_install_directory(): """ ``get_install_directory`` returns a string pointing to the installed binary currently running - .warning:: ONLY for use within the Binary Ninja UI, behavior is undefined and unreliable if run headlessly + ..warning:: ONLY for use within the Binary Ninja UI, behavior is undefined and unreliable if run headlessly """ return core.BNGetInstallDirectory() +_plugin_api_name = "python2" + + +class PluginManagerLoadPluginCallback(object): + """Callback for BNLoadPluginForApi("python2", ...), dynamicly loads python plugins.""" + def __init__(self): + self.cb = ctypes.CFUNCTYPE( + ctypes.c_bool, + ctypes.c_char_p, + ctypes.c_char_p, + ctypes.c_void_p)(self._load_plugin) + + def _load_plugin(self, repo_path, plugin_path, ctx): + try: + repo = RepositoryManager()[repo_path] + plugin = repo[plugin_path] + + if plugin.api != _plugin_api_name: + raise ValueError("Plugin api name is not " + _plugin_api_name) + + if not plugin.installed: + plugin.installed = True + + if repo.full_path not in sys.path: + sys.path.append(repo.full_path) + + __import__(plugin.path) + log_info("Successfully loaded plugin: {}/{}: ".format(repo_path, plugin_path)) + return True + except KeyError: + log_error("Failed to find python plugin: {}/{}".format(repo_path, plugin_path)) + except ImportError as ie: + log_error("Failed to import python plugin: {}/{}: {}".format(repo_path, plugin_path, ie)) + return False + + +load_plugin = PluginManagerLoadPluginCallback() +core.BNRegisterForPluginLoading(_plugin_api_name, load_plugin.cb, 0) + + class _DestructionCallbackHandler(object): def __init__(self): self._cb = core.BNObjectDestructionCallbacks() diff --git a/python/architecture.py b/python/architecture.py index 2eb3c717..72403fec 100644 --- a/python/architecture.py +++ b/python/architecture.py @@ -116,6 +116,7 @@ class Architecture(object): regs = {} stack_pointer = None link_reg = None + global_regs = [] flags = [] flag_write_types = [] flag_roles = {} @@ -208,6 +209,13 @@ class Architecture(object): core.BNFreeRegisterList(flags) self._flags_written_by_flag_write_type[self._flag_write_types[write_type]] = flag_indexes self.__dict__["flags_written_by_flag_write_type"][write_type] = flag_names + + count = ctypes.c_ulonglong() + regs = core.BNGetArchitectureGlobalRegisters(self.handle, count) + self.__dict__["global_regs"] = [] + for i in xrange(0, count.value): + self.global_regs.append(core.BNGetArchitectureRegisterName(self.handle, regs[i])) + core.BNFreeRegisterList(regs) else: startup._init_plugins() @@ -250,6 +258,7 @@ class Architecture(object): self._cb.getStackPointerRegister = self._cb.getStackPointerRegister.__class__( self._get_stack_pointer_register) self._cb.getLinkRegister = self._cb.getLinkRegister.__class__(self._get_link_register) + self._cb.getGlobalRegisters = self._cb.getGlobalRegisters.__class__(self._get_global_registers) self._cb.assemble = self._cb.assemble.__class__(self._assemble) self._cb.isNeverBranchPatchAvailable = self._cb.isNeverBranchPatchAvailable.__class__( self._is_never_branch_patch_available) @@ -330,6 +339,8 @@ class Architecture(object): flags.append(self._flags[flag]) self._flags_written_by_flag_write_type[self._flag_write_types[write_type]] = flags + self.__dict__["global_regs"] = self.__class__.global_regs + self._pending_reg_lists = {} self._pending_token_lists = {} @@ -361,7 +372,7 @@ class Architecture(object): cc = core.BNGetArchitectureCallingConventions(self.handle, count) result = {} for i in xrange(0, count.value): - obj = callingconvention.CallingConvention(None, core.BNNewCallingConventionReference(cc[i])) + obj = callingconvention.CallingConvention(handle=core.BNNewCallingConventionReference(cc[i])) result[obj.name] = obj core.BNFreeCallingConventionList(cc, count) return result @@ -478,6 +489,7 @@ class Architecture(object): token_buf[i].size = tokens[i].size token_buf[i].operand = tokens[i].operand token_buf[i].context = tokens[i].context + token_buf[i].confidence = tokens[i].confidence token_buf[i].address = tokens[i].address result[0] = token_buf ptr = ctypes.cast(token_buf, ctypes.c_void_p) @@ -718,6 +730,20 @@ class Architecture(object): log.log_error(traceback.format_exc()) return 0 + def _get_global_registers(self, ctxt, count): + try: + count[0] = len(self.__class__.global_regs) + reg_buf = (ctypes.c_uint * len(self.__class__.global_regs))() + for i in xrange(0, len(self.__class__.global_regs)): + reg_buf[i] = self._all_regs[self.__class__.global_regs[i]] + result = ctypes.cast(reg_buf, ctypes.c_void_p) + self._pending_reg_lists[result.value] = (result, reg_buf) + return result.value + except KeyError: + log.log_error(traceback.format_exc()) + count[0] = 0 + return None + def _assemble(self, ctxt, code, addr, result, errors): try: data, error_str = self.perform_assemble(code, addr) @@ -897,7 +923,7 @@ class Architecture(object): :param str data: bytes to be interpreted as low-level IL instructions :param int addr: virtual address of start of ``data`` :param LowLevelILFunction il: LowLevelILFunction object to append LowLevelILExpr objects to - :rtype: None + :rtype: length of bytes read on success, None on failure """ raise NotImplementedError @@ -1163,8 +1189,9 @@ class Architecture(object): size = tokens[i].size operand = tokens[i].operand context = tokens[i].context + confidence = tokens[i].confidence address = tokens[i].address - result.append(function.InstructionTextToken(token_type, text, value, size, operand, context, address)) + result.append(function.InstructionTextToken(token_type, text, value, size, operand, context, address, confidence)) core.BNFreeInstructionText(tokens, count.value) return result, length.value @@ -1293,7 +1320,7 @@ class Architecture(object): for i in xrange(len(operands)): if isinstance(operands[i], str): operand_list[i].constant = False - operand_list[i].reg = self.regs[operands[i]] + operand_list[i].reg = self.regs[operands[i]].index elif isinstance(operands[i], lowlevelil.ILRegister): operand_list[i].constant = False operand_list[i].reg = operands[i].index @@ -1317,7 +1344,7 @@ class Architecture(object): for i in xrange(len(operands)): if isinstance(operands[i], str): operand_list[i].constant = False - operand_list[i].reg = self.regs[operands[i]] + operand_list[i].reg = self.regs[operands[i]].index elif isinstance(operands[i], lowlevelil.ILRegister): operand_list[i].constant = False operand_list[i].reg = operands[i].index @@ -1647,99 +1674,6 @@ class Architecture(object): """ core.BNSetBinaryViewTypeArchitectureConstant(self.handle, type_name, const_name, value) - def parse_types_from_source(self, source, filename=None, include_dirs=[], auto_type_source=None): - """ - ``parse_types_from_source`` parses the source string and any needed headers searching for them in - the optional list of directories provided in ``include_dirs``. - - :param str source: source string to be parsed - :param str filename: optional source filename - :param list(str) include_dirs: optional list of string filename include directories - :param str auto_type_source: optional source of types if used for automatically generated types - :return: py:class:`TypeParserResult` (a SyntaxError is thrown on parse error) - :rtype: TypeParserResult - :Example: - - >>> arch.parse_types_from_source('int foo;\\nint bar(int x);\\nstruct bas{int x,y;};\\n') - ({types: {'bas': <type: struct bas>}, variables: {'foo': <type: int32_t>}, functions:{'bar': - <type: int32_t(int32_t x)>}}, '') - >>> - """ - - if filename is None: - filename = "input" - dir_buf = (ctypes.c_char_p * len(include_dirs))() - for i in xrange(0, len(include_dirs)): - dir_buf[i] = str(include_dirs[i]) - parse = core.BNTypeParserResult() - errors = ctypes.c_char_p() - result = core.BNParseTypesFromSource(self.handle, source, filename, parse, errors, dir_buf, - len(include_dirs), auto_type_source) - error_str = errors.value - core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) - if not result: - raise SyntaxError(error_str) - type_dict = {} - variables = {} - functions = {} - for i in xrange(0, parse.typeCount): - name = types.QualifiedName._from_core_struct(parse.types[i].name) - type_dict[name] = types.Type(core.BNNewTypeReference(parse.types[i].type)) - for i in xrange(0, parse.variableCount): - name = types.QualifiedName._from_core_struct(parse.variables[i].name) - variables[name] = types.Type(core.BNNewTypeReference(parse.variables[i].type)) - for i in xrange(0, parse.functionCount): - name = types.QualifiedName._from_core_struct(parse.functions[i].name) - functions[name] = types.Type(core.BNNewTypeReference(parse.functions[i].type)) - core.BNFreeTypeParserResult(parse) - return types.TypeParserResult(type_dict, variables, functions) - - def parse_types_from_source_file(self, filename, include_dirs=[], auto_type_source=None): - """ - ``parse_types_from_source_file`` parses the source file ``filename`` and any needed headers searching for them in - the optional list of directories provided in ``include_dirs``. - - :param str filename: filename of file to be parsed - :param list(str) include_dirs: optional list of string filename include directories - :param str auto_type_source: optional source of types if used for automatically generated types - :return: py:class:`TypeParserResult` (a SyntaxError is thrown on parse error) - :rtype: TypeParserResult - :Example: - - >>> file = "/Users/binja/tmp.c" - >>> open(file).read() - 'int foo;\\nint bar(int x);\\nstruct bas{int x,y;};\\n' - >>> arch.parse_types_from_source_file(file) - ({types: {'bas': <type: struct bas>}, variables: {'foo': <type: int32_t>}, functions: - {'bar': <type: int32_t(int32_t x)>}}, '') - >>> - """ - dir_buf = (ctypes.c_char_p * len(include_dirs))() - for i in xrange(0, len(include_dirs)): - dir_buf[i] = str(include_dirs[i]) - parse = core.BNTypeParserResult() - errors = ctypes.c_char_p() - result = core.BNParseTypesFromSourceFile(self.handle, filename, parse, errors, dir_buf, - len(include_dirs), auto_type_source) - error_str = errors.value - core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) - if not result: - raise SyntaxError(error_str) - type_dict = {} - variables = {} - functions = {} - for i in xrange(0, parse.typeCount): - name = types.QualifiedName._from_core_struct(parse.types[i].name) - type_dict[name] = types.Type(core.BNNewTypeReference(parse.types[i].type)) - for i in xrange(0, parse.variableCount): - name = types.QualifiedName._from_core_struct(parse.variables[i].name) - variables[name] = types.Type(core.BNNewTypeReference(parse.variables[i].type)) - for i in xrange(0, parse.functionCount): - name = types.QualifiedName._from_core_struct(parse.functions[i].name) - functions[name] = types.Type(core.BNNewTypeReference(parse.functions[i].type)) - core.BNFreeTypeParserResult(parse) - return types.TypeParserResult(type_dict, variables, functions) - def register_calling_convention(self, cc): """ ``register_calling_convention`` registers a new calling convention for the Architecture. diff --git a/python/basicblock.py b/python/basicblock.py index 7858cc60..72f31876 100644 --- a/python/basicblock.py +++ b/python/basicblock.py @@ -48,6 +48,8 @@ class BasicBlock(object): def __init__(self, view, handle): self.view = view self.handle = core.handle_of_type(handle, core.BNBasicBlock) + self._arch = None + self._func = None def __del__(self): core.BNFreeBasicBlock(self.handle) @@ -62,21 +64,33 @@ class BasicBlock(object): return True return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) + def _create_instance(self, view, handle): + """Internal method used to instantiante child instances""" + return BasicBlock(view, handle) + @property def function(self): """Basic block function (read-only)""" + if self._func is not None: + return self._func func = core.BNGetBasicBlockFunction(self.handle) if func is None: return None - return function.Function(self.view, func) + self._func = function.Function(self.view, func) + return self._func @property def arch(self): """Basic block architecture (read-only)""" + # The arch for a BasicBlock isn't going to change so just cache + # it the first time we need it + if self._arch is not None: + return self._arch arch = core.BNGetBasicBlockArchitecture(self.handle) if arch is None: return None - return architecture.Architecture(arch) + self._arch = architecture.Architecture(arch) + return self._arch @property def start(self): @@ -107,7 +121,7 @@ class BasicBlock(object): for i in xrange(0, count.value): branch_type = BranchType(edges[i].type) if edges[i].target: - target = BasicBlock(self.view, core.BNNewBasicBlockReference(edges[i].target)) + target = self._create_instance(self.view, core.BNNewBasicBlockReference(edges[i].target)) else: target = None result.append(BasicBlockEdge(branch_type, self, target, edges[i].backEdge)) @@ -123,7 +137,7 @@ class BasicBlock(object): for i in xrange(0, count.value): branch_type = BranchType(edges[i].type) if edges[i].target: - target = BasicBlock(self.view, core.BNNewBasicBlockReference(edges[i].target)) + target = self._create_instance(self.view, core.BNNewBasicBlockReference(edges[i].target)) else: target = None result.append(BasicBlockEdge(branch_type, self, target, edges[i].backEdge)) @@ -136,13 +150,18 @@ class BasicBlock(object): return core.BNBasicBlockHasUndeterminedOutgoingEdges(self.handle) @property + def can_exit(self): + """Whether basic block can return or is tagged as 'No Return' (read-only)""" + return core.BNBasicBlockCanExit(self.handle) + + @property def dominators(self): """List of dominators for this basic block (read-only)""" count = ctypes.c_ulonglong() blocks = core.BNGetBasicBlockDominators(self.handle, count) result = [] for i in xrange(0, count.value): - result.append(BasicBlock(self.view, core.BNNewBasicBlockReference(blocks[i]))) + result.append(self._create_instance(self.view, core.BNNewBasicBlockReference(blocks[i]))) core.BNFreeBasicBlockList(blocks, count.value) return result @@ -153,7 +172,7 @@ class BasicBlock(object): blocks = core.BNGetBasicBlockStrictDominators(self.handle, count) result = [] for i in xrange(0, count.value): - result.append(BasicBlock(self.view, core.BNNewBasicBlockReference(blocks[i]))) + result.append(self._create_instance(self.view, core.BNNewBasicBlockReference(blocks[i]))) core.BNFreeBasicBlockList(blocks, count.value) return result @@ -163,7 +182,7 @@ class BasicBlock(object): result = core.BNGetBasicBlockImmediateDominator(self.handle) if not result: return None - return BasicBlock(self.view, result) + return self._create_instance(self.view, result) @property def dominator_tree_children(self): @@ -172,7 +191,7 @@ class BasicBlock(object): blocks = core.BNGetBasicBlockDominatorTreeChildren(self.handle, count) result = [] for i in xrange(0, count.value): - result.append(BasicBlock(self.view, core.BNNewBasicBlockReference(blocks[i]))) + result.append(self._create_instance(self.view, core.BNNewBasicBlockReference(blocks[i]))) core.BNFreeBasicBlockList(blocks, count.value) return result @@ -183,7 +202,7 @@ class BasicBlock(object): blocks = core.BNGetBasicBlockDominanceFrontier(self.handle, count) result = [] for i in xrange(0, count.value): - result.append(BasicBlock(self.view, core.BNNewBasicBlockReference(blocks[i]))) + result.append(self._create_instance(self.view, core.BNNewBasicBlockReference(blocks[i]))) core.BNFreeBasicBlockList(blocks, count.value) return result @@ -264,8 +283,8 @@ class BasicBlock(object): idx = start while idx < end: data = self.view.read(idx, 16) - inst_info = self.view.arch.get_instruction_info(data, idx) - inst_text = self.view.arch.get_instruction_text(data, idx) + inst_info = self.arch.get_instruction_info(data, idx) + inst_text = self.arch.get_instruction_text(data, idx) yield inst_text idx += inst_info.length @@ -276,9 +295,11 @@ class BasicBlock(object): def get_disassembly_text(self, settings=None): """ ``get_disassembly_text`` returns a list of function.DisassemblyTextLine objects for the current basic block. + + :param DisassemblySettings settings: (optional) DisassemblySettings object :Example: - >>>current_basic_block.get_disassembly_text() + >>> current_basic_block.get_disassembly_text() [<0x100000f30: _main:>, <0x100000f30: push rbp>, ... ] """ settings_obj = None @@ -298,8 +319,9 @@ class BasicBlock(object): size = lines[i].tokens[j].size operand = lines[i].tokens[j].operand context = lines[i].tokens[j].context + confidence = lines[i].tokens[j].confidence address = lines[i].tokens[j].address - tokens.append(function.InstructionTextToken(token_type, text, value, size, operand, context, address)) + tokens.append(function.InstructionTextToken(token_type, text, value, size, operand, context, address, confidence)) result.append(function.DisassemblyTextLine(addr, tokens)) core.BNFreeDisassemblyTextLines(lines, count.value) return result @@ -308,7 +330,7 @@ class BasicBlock(object): """ ``set_auto_highlight`` highlights the current BasicBlock with the supplied color. - .warning:: Use only in analysis plugins. Do not use in regular plugins, as colors won't be saved to the database. + ..warning:: Use only in analysis plugins. Do not use in regular plugins, as colors won't be saved to the database. :param HighlightStandardColor or highlight.HighlightColor color: Color value to use for highlighting """ diff --git a/python/binaryview.py b/python/binaryview.py index c0ac0abc..d002b38f 100644 --- a/python/binaryview.py +++ b/python/binaryview.py @@ -26,7 +26,8 @@ import threading # Binary Ninja components import _binaryninjacore as core -from enums import AnalysisState, SymbolType, InstructionTextTokenType, Endianness, ModificationStatus, StringType, SegmentFlag +from enums import (AnalysisState, SymbolType, InstructionTextTokenType, + Endianness, ModificationStatus, StringType, SegmentFlag, SectionSemantics) import function import startup import architecture @@ -39,6 +40,7 @@ import databuffer import basicblock import types import lineardisassembly +import metadata class BinaryDataNotification(object): @@ -115,6 +117,10 @@ class AnalysisCompletionEvent(object): pass def cancel(self): + """ + .. warning: This method should only be used when the system is being + shut down and no further analysis should be done afterward. + """ self.callback = self._empty_callback core.BNCancelAnalysisCompletionEvent(self.handle) @@ -211,7 +217,7 @@ class BinaryDataNotificationCallbacks(object): def _data_var_added(self, ctxt, view, var): try: address = var[0].address - var_type = types.Type(core.BNNewTypeReference(var[0].type)) + var_type = types.Type(core.BNNewTypeReference(var[0].type), platform = self.view.platform, confidence = var[0].typeConfidence) auto_discovered = var[0].autoDiscovered self.notify.data_var_added(self.view, DataVariable(address, var_type, auto_discovered)) except: @@ -220,7 +226,7 @@ class BinaryDataNotificationCallbacks(object): def _data_var_removed(self, ctxt, view, var): try: address = var[0].address - var_type = types.Type(core.BNNewTypeReference(var[0].type)) + var_type = types.Type(core.BNNewTypeReference(var[0].type), platform = self.view.platform, confidence = var[0].typeConfidence) auto_discovered = var[0].autoDiscovered self.notify.data_var_removed(self.view, DataVariable(address, var_type, auto_discovered)) except: @@ -229,7 +235,7 @@ class BinaryDataNotificationCallbacks(object): def _data_var_updated(self, ctxt, view, var): try: address = var[0].address - var_type = types.Type(core.BNNewTypeReference(var[0].type)) + var_type = types.Type(core.BNNewTypeReference(var[0].type), platform = self.view.platform, confidence = var[0].typeConfidence) auto_discovered = var[0].autoDiscovered self.notify.data_var_updated(self.view, DataVariable(address, var_type, auto_discovered)) except: @@ -250,14 +256,14 @@ class BinaryDataNotificationCallbacks(object): def _type_defined(self, ctxt, view, name, type_obj): try: qualified_name = types.QualifiedName._from_core_struct(name[0]) - self.notify.type_defined(view, qualified_name, types.Type(core.BNNewTypeReference(type_obj))) + self.notify.type_defined(view, qualified_name, types.Type(core.BNNewTypeReference(type_obj), platform = self.view.platform)) except: log.log_error(traceback.format_exc()) def _type_undefined(self, ctxt, view, name, type_obj): try: qualified_name = types.QualifiedName._from_core_struct(name[0]) - self.notify.type_undefined(view, qualified_name, types.Type(core.BNNewTypeReference(type_obj))) + self.notify.type_undefined(view, qualified_name, types.Type(core.BNNewTypeReference(type_obj), platform = self.view.platform)) except: log.log_error(traceback.format_exc()) @@ -416,7 +422,7 @@ class Segment(object): class Section(object): - def __init__(self, name, section_type, start, length, linked_section, info_section, info_data, align, entry_size): + def __init__(self, name, section_type, start, length, linked_section, info_section, info_data, align, entry_size, semantics): self.name = name self.type = section_type self.start = start @@ -426,6 +432,7 @@ class Section(object): self.info_data = info_data self.align = align self.entry_size = entry_size + self.semantics = SectionSemantics(semantics) @property def end(self): @@ -854,7 +861,7 @@ class BinaryView(object): result = {} for i in xrange(0, count.value): addr = var_list[i].address - var_type = types.Type(core.BNNewTypeReference(var_list[i].type)) + var_type = types.Type(core.BNNewTypeReference(var_list[i].type), platform = self.platform, confidence = var_list[i].typeConfidence) auto_discovered = var_list[i].autoDiscovered result[addr] = DataVariable(addr, var_type, auto_discovered) core.BNFreeDataVariables(var_list, count.value) @@ -868,7 +875,7 @@ class BinaryView(object): result = {} for i in xrange(0, count.value): name = types.QualifiedName._from_core_struct(type_list[i].name) - result[name] = types.Type(core.BNNewTypeReference(type_list[i].type)) + result[name] = types.Type(core.BNNewTypeReference(type_list[i].type), platform = self.platform) core.BNFreeTypeList(type_list, count.value) return result @@ -893,7 +900,8 @@ class BinaryView(object): for i in xrange(0, count.value): result[section_list[i].name] = Section(section_list[i].name, section_list[i].type, section_list[i].start, section_list[i].length, section_list[i].linkedSection, section_list[i].infoSection, - section_list[i].infoData, section_list[i].align, section_list[i].entrySize) + section_list[i].infoData, section_list[i].align, section_list[i].entrySize, + section_list[i].semantics) core.BNFreeSectionList(section_list, count.value) return result @@ -919,6 +927,12 @@ class BinaryView(object): else: return BinaryView._associated_data[handle.value] + @property + def global_pointer_value(self): + """Discovered value of the global pointer register, if the binary uses one (read-only)""" + result = core.BNGetGlobalPointerValue(self.handle) + return function.RegisterValue(self.arch, result.value, confidence = result.confidence) + def __len__(self): return int(core.BNGetViewLength(self.handle)) @@ -1423,7 +1437,7 @@ class BinaryView(object): """ ``save_auto_snapshot`` saves the current database to the already created file. - .. note:: :py:method:`create_database` should have been called prior to executing this method + .. note:: :py:meth:`create_database` should have been called prior to executing this method :param callable() progress_func: optional function to be called with the current progress and total count. :return: True if it successfully saved the snapshot, False otherwise @@ -1672,6 +1686,28 @@ class BinaryView(object): """ return core.BNIsOffsetExecutable(self.handle, addr) + def is_offset_code_semantics(self, addr): + """ + ``is_offset_code_semantics`` checks if an virtual address ``addr`` is semantically valid for code. + + :param int addr: a virtual address to be checked + :return: true if the virtual address is valid for writing, false if the virtual address is invalid or error + :rtype: bool + """ + return core.BNIsOffsetCodeSemantics(self.handle, addr) + + def is_offset_writable_semantics(self, addr): + """ + ``is_offset_writable_semantics`` checks if an virtual address ``addr`` is semantically writable. Some sections + may have writable permissions for linking purposes but can be treated as read-only for the purposes of + analysis. + + :param int addr: a virtual address to be checked + :return: true if the virtual address is valid for writing, false if the virtual address is invalid or error + :rtype: bool + """ + return core.BNIsOffsetWritableSemantics(self.handle, addr) + def save(self, dest): """ ``save`` saves the original binary file to the provided destination ``dest`` along with any modifications. @@ -1685,11 +1721,25 @@ class BinaryView(object): return core.BNSaveToFilename(self.handle, str(dest)) def register_notification(self, notify): + """ + `register_notification` provides a mechanism for receiving callbacks for various analysis events. A full + list of callbacks can be seen in :py:Class:`BinaryDataNotification`. + + :param BinaryDataNotification notify: notify is a subclassed instance of :py:Class:`BinaryDataNotification`. + :rtype: None + """ cb = BinaryDataNotificationCallbacks(self, notify) cb._register() self.notifications[notify] = cb def unregister_notification(self, notify): + """ + `unregister_notification` unregisters the :py:Class:`BinaryDataNotification` object passed to + `register_notification` + + :param BinaryDataNotification notify: notify is a subclassed instance of :py:Class:`BinaryDataNotification`. + :rtype: None + """ if notify in self.notifications: self.notifications[notify]._unregister() del self.notifications[notify] @@ -1781,6 +1831,22 @@ class BinaryView(object): """ core.BNRemoveUserFunction(self.handle, func.handle) + def add_analysis_option(self, name): + """ + ``add_analysis_option`` adds an analysis option. Analysis options elaborate the analysis phase. The user must + start analysis by calling either ``update_analysis()`` or ``update_analysis_and_wait()``. + + :param str name: name of the analysis option. Available options: + "linearsweep" : apply linearsweep analysis during the next analysis update (run-once semantics) + + :rtype: None + :Example: + + >>> bv.add_analysis_option("linearsweep") + >>> bv.update_analysis_and_wait() + """ + core.BNAddAnalysisOption(self.handle, name) + def update_analysis(self): """ ``update_analysis`` asynchronously starts the analysis running and returns immediately. Analysis of BinaryViews @@ -1801,28 +1867,7 @@ class BinaryView(object): :rtype: None """ - class WaitEvent(object): - def __init__(self): - self.cond = threading.Condition() - self.done = False - - def complete(self): - self.cond.acquire() - self.done = True - self.cond.notify() - self.cond.release() - - def wait(self): - self.cond.acquire() - while not self.done: - self.cond.wait() - self.cond.release() - - wait = WaitEvent() - # TODO: figure out if we actually need this 'event' variable, likely we do - event = AnalysisCompletionEvent(self, lambda: wait.complete()) - core.BNUpdateAnalysis(self.handle) - wait.wait() + core.BNUpdateAnalysisAndWait(self.handle) def abort_analysis(self): """ @@ -1847,7 +1892,10 @@ class BinaryView(object): >>> bv.define_data_var(bv.entry_point, t[0]) >>> """ - core.BNDefineDataVariable(self.handle, addr, var_type.handle) + tc = core.BNTypeWithConfidence() + tc.type = var_type.handle + tc.confidence = var_type.confidence + core.BNDefineDataVariable(self.handle, addr, tc) def define_user_data_var(self, addr, var_type): """ @@ -1864,7 +1912,10 @@ class BinaryView(object): >>> bv.define_user_data_var(bv.entry_point, t[0]) >>> """ - core.BNDefineUserDataVariable(self.handle, addr, var_type.handle) + tc = core.BNTypeWithConfidence() + tc.type = var_type.handle + tc.confidence = var_type.confidence + core.BNDefineUserDataVariable(self.handle, addr, tc) def undefine_data_var(self, addr): """ @@ -1910,13 +1961,29 @@ class BinaryView(object): var = core.BNDataVariable() if not core.BNGetDataVariableAtAddress(self.handle, addr, var): return None - return DataVariable(var.address, types.Type(var.type), var.autoDiscovered) + return DataVariable(var.address, types.Type(var.type, platform = self.platform, confidence = var.typeConfidence), var.autoDiscovered) + + def get_functions_containing(self, addr): + """ + ``get_functions_containing`` returns a list of functions which contain the given address or None on failure. + + :param int addr: virtual address to query. + :rtype: list of Function objects or None + """ + basic_blocks = self.get_basic_blocks_at(addr) + if len(basic_blocks) == 0: + return None + + result = [] + for block in basic_blocks: + result.append(block.function) + return result def get_function_at(self, addr, plat=None): """ - ``get_function_at`` gets a binaryninja.Function object for the function at the virtual address ``addr``: + ``get_function_at`` gets a Function object for the function that starts at virtual address ``addr``: - :param int addr: virtual address of the desired function + :param int addr: starting virtual address of the desired function :param Platform plat: plat of the desired function :return: returns a Function object or None for the function at the virtual address provided :rtype: Function @@ -2722,7 +2789,7 @@ class BinaryView(object): def get_linear_disassembly_position_at(self, addr, settings): """ ``get_linear_disassembly_position_at`` instantiates a :py:class:`LinearDisassemblyPosition` object for use in - :py:method:`get_previous_linear_disassembly_lines` or :py:method:`get_next_linear_disassembly_lines`. + :py:meth:`get_previous_linear_disassembly_lines` or :py:meth:`get_next_linear_disassembly_lines`. :param int addr: virtual address of linear disassembly position :param DisassemblySettings settings: an instantiated :py:class:`DisassemblySettings` object @@ -2781,8 +2848,9 @@ class BinaryView(object): size = lines[i].contents.tokens[j].size operand = lines[i].contents.tokens[j].operand context = lines[i].contents.tokens[j].context + confidence = lines[i].contents.tokens[j].confidence address = lines[i].contents.tokens[j].address - tokens.append(function.InstructionTextToken(token_type, text, value, size, operand, context, address)) + tokens.append(function.InstructionTextToken(token_type, text, value, size, operand, context, address, confidence)) contents = function.DisassemblyTextLine(addr, tokens) result.append(lineardisassembly.LinearDisassemblyLine(lines[i].type, func, block, lines[i].lineOffset, contents)) @@ -2896,7 +2964,7 @@ class BinaryView(object): error_str = errors.value core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) raise SyntaxError(error_str) - type_obj = types.Type(core.BNNewTypeReference(result.type)) + type_obj = types.Type(core.BNNewTypeReference(result.type), platform = self.platform) name = types.QualifiedName._from_core_struct(result.name) core.BNFreeQualifiedNameAndType(result) return type_obj, name @@ -2920,7 +2988,7 @@ class BinaryView(object): obj = core.BNGetAnalysisTypeByName(self.handle, name) if not obj: return None - return types.Type(obj) + return types.Type(obj, platform = self.platform) def get_type_by_id(self, id): """ @@ -2941,7 +3009,7 @@ class BinaryView(object): obj = core.BNGetAnalysisTypeById(self.handle, id) if not obj: return None - return types.Type(obj) + return types.Type(obj, platform = self.platform) def get_type_name_by_id(self, id): """ @@ -3196,17 +3264,17 @@ class BinaryView(object): return None return address.value - def add_auto_section(self, name, start, length, type = "", align = 1, entry_size = 1, linked_section = "", - info_section = "", info_data = 0): - core.BNAddAutoSection(self.handle, name, start, length, type, align, entry_size, linked_section, + def add_auto_section(self, name, start, length, semantics = SectionSemantics.DefaultSectionSemantics, + type = "", align = 1, entry_size = 1, linked_section = "", info_section = "", info_data = 0): + core.BNAddAutoSection(self.handle, name, start, length, semantics, type, align, entry_size, linked_section, info_section, info_data) def remove_auto_section(self, name): core.BNRemoveAutoSection(self.handle, name) - def add_user_section(self, name, start, length, type = "", align = 1, entry_size = 1, linked_section = "", - info_section = "", info_data = 0): - core.BNAddUserSection(self.handle, name, start, length, type, align, entry_size, linked_section, + def add_user_section(self, name, start, length, semantics = SectionSemantics.DefaultSectionSemantics, + type = "", align = 1, entry_size = 1, linked_section = "", info_section = "", info_data = 0): + core.BNAddUserSection(self.handle, name, start, length, semantics, type, align, entry_size, linked_section, info_section, info_data) def remove_user_section(self, name): @@ -3219,7 +3287,8 @@ class BinaryView(object): for i in xrange(0, count.value): result.append(Section(section_list[i].name, section_list[i].type, section_list[i].start, section_list[i].length, section_list[i].linkedSection, section_list[i].infoSection, - section_list[i].infoData, section_list[i].align, section_list[i].entrySize)) + section_list[i].infoData, section_list[i].align, section_list[i].entrySize, + section_list[i].semantics)) core.BNFreeSectionList(section_list, count.value) return result @@ -3228,7 +3297,7 @@ 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.infoSection, section.infoData, section.align, section.entrySize, section.semantics) core.BNFreeSection(section) return result @@ -3243,6 +3312,67 @@ class BinaryView(object): core.BNFreeStringList(outgoing_names, len(name_list)) return result + def query_metadata(self, key): + """ + `query_metadata` retrieves a metadata associated with the given key stored in the current BinaryView. + + :param string key: key to query + :rtype: metadata associated with the key + :Example: + + >>> bv.store_metadata("integer", 1337) + >>> bv.query_metadata("integer") + 1337L + >>> bv.store_metadata("list", [1,2,3]) + >>> bv.query_metadata("list") + [1L, 2L, 3L] + >>> bv.store_metadata("string", "my_data") + >>> bv.query_metadata("string") + 'my_data' + """ + md_handle = core.BNBinaryViewQueryMetadata(self.handle, key) + if md_handle is None: + raise KeyError(key) + return metadata.Metadata(handle=md_handle).value + + def store_metadata(self, key, md): + """ + `store_metadata` stores an object for the given key in the current BinaryView. Objects stored using + `store_metadata` can be retrieved when the database is reopend. Objects stored are not arbitrary python + objects! The values stored must be able to be held in a Metadata object. See :py:class:`Metadata` + for more information. Python objects could obviously be serialized using pickle but this intentionally + a task left to the user since there is the potential security issues. + + :param string key: key value to associate the Metadata object with + :param Varies md: object to store. + :rtype: None + :Example: + + >>> bv.store_metadata("integer", 1337) + >>> bv.query_metadata("integer") + 1337L + >>> bv.store_metadata("list", [1,2,3]) + >>> bv.query_metadata("list") + [1L, 2L, 3L] + >>> bv.store_metadata("string", "my_data") + >>> bv.query_metadata("string") + 'my_data' + """ + core.BNBinaryViewStoreMetadata(self.handle, key, metadata.Metadata(md).handle) + + def remove_metadata(self, key): + """ + `remove_metadata` removes the metadata associated with key from the current BinaryView. + + :param string key: key associated with metadata to remove from the BinaryView + :rtype: None + :Example: + + >>> bv.store_metadata("integer", 1337) + >>> bv.remove_metadata("integer") + """ + core.BNBinaryViewRemoveMetadata(self.handle, key) + def __setattr__(self, name, value): try: object.__setattr__(self, name, value) diff --git a/python/callingconvention.py b/python/callingconvention.py index 4c87eef6..e72475c9 100644 --- a/python/callingconvention.py +++ b/python/callingconvention.py @@ -25,6 +25,9 @@ import ctypes import _binaryninjacore as core import architecture import log +import types +import function +import binaryview class CallingConvention(object): @@ -34,14 +37,19 @@ class CallingConvention(object): float_arg_regs = [] arg_regs_share_index = False stack_reserved_for_arg_regs = False + stack_adjusted_on_return = False int_return_reg = None high_int_return_reg = None float_return_reg = None + global_pointer_reg = None + implicitly_defined_regs = [] _registered_calling_conventions = [] - def __init__(self, arch, handle = None): + def __init__(self, arch=None, name=None, handle=None, confidence=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") self.arch = arch self._pending_reg_lists = {} self._cb = core.BNCustomCallingConvention() @@ -52,10 +60,15 @@ class CallingConvention(object): self._cb.freeRegisterList = self._cb.freeRegisterList.__class__(self._free_register_list) self._cb.areArgumentRegistersSharedIndex = self._cb.areArgumentRegistersSharedIndex.__class__(self._arg_regs_share_index) self._cb.isStackReservedForArgumentRegisters = self._cb.isStackReservedForArgumentRegisters.__class__(self._stack_reserved_for_arg_regs) + self._cb.isStackAdjustedOnReturn = self._cb.isStackAdjustedOnReturn.__class__(self._stack_adjusted_on_return) self._cb.getIntegerReturnValueRegister = self._cb.getIntegerReturnValueRegister.__class__(self._get_int_return_reg) self._cb.getHighIntegerReturnValueRegister = self._cb.getHighIntegerReturnValueRegister.__class__(self._get_high_int_return_reg) self._cb.getFloatReturnValueRegister = self._cb.getFloatReturnValueRegister.__class__(self._get_float_return_reg) - self.handle = core.BNCreateCallingConvention(arch.handle, self.__class__.name, self._cb) + self._cb.getGlobalPointerRegister = self._cb.getGlobalPointerRegister.__class__(self._get_global_pointer_reg) + self._cb.getImplicitlyDefinedRegisters = self._cb.getImplicitlyDefinedRegisters.__class__(self._get_implicitly_defined_regs) + self._cb.getIncomingRegisterValue = self._cb.getIncomingRegisterValue.__class__(self._get_incoming_reg_value) + self._cb.getIncomingFlagValue = self._cb.getIncomingFlagValue.__class__(self._get_incoming_flag_value) + self.handle = core.BNCreateCallingConvention(arch.handle, name, self._cb) self.__class__._registered_calling_conventions.append(self) else: self.handle = handle @@ -63,6 +76,7 @@ class CallingConvention(object): self.__dict__["name"] = core.BNGetCallingConventionName(self.handle) self.__dict__["arg_regs_share_index"] = core.BNAreArgumentRegistersSharedIndex(self.handle) self.__dict__["stack_reserved_for_arg_regs"] = core.BNIsStackReservedForArgumentRegisters(self.handle) + self.__dict__["stack_adjusted_on_return"] = core.BNIsStackAdjustedOnReturn(self.handle) count = ctypes.c_ulonglong() regs = core.BNGetCallerSavedRegisters(self.handle, count) @@ -109,6 +123,23 @@ class CallingConvention(object): else: self.__dict__["float_return_reg"] = self.arch.get_reg_name(reg) + reg = core.BNGetGlobalPointerRegister(self.handle) + if reg == 0xffffffff: + self.__dict__["global_pointer_reg"] = None + else: + self.__dict__["global_pointer_reg"] = self.arch.get_reg_name(reg) + + count = ctypes.c_ulonglong() + regs = core.BNGetImplicitlyDefinedRegisters(self.handle, count) + result = [] + arch = self.arch + for i in xrange(0, count.value): + result.append(arch.get_reg_name(regs[i])) + core.BNFreeRegisterList(regs, count.value) + self.__dict__["implicitly_defined_regs"] = result + + self.confidence = confidence + def __del__(self): core.BNFreeCallingConvention(self.handle) @@ -190,6 +221,13 @@ class CallingConvention(object): log.log_error(traceback.format_exc()) return False + def _stack_adjusted_on_return(self, ctxt): + try: + return self.__class__.stack_adjusted_on_return + except: + log.log_error(traceback.format_exc()) + return False + def _get_int_return_reg(self, ctxt): try: return self.arch.regs[self.__class__.int_return_reg].index @@ -215,8 +253,80 @@ class CallingConvention(object): log.log_error(traceback.format_exc()) return False + def _get_global_pointer_reg(self, ctxt): + try: + if self.__class__.global_pointer_reg is None: + return 0xffffffff + return self.arch.regs[self.__class__.global_pointer_reg].index + except: + log.log_error(traceback.format_exc()) + return False + + def _get_implicitly_defined_regs(self, ctxt, count): + try: + regs = self.__class__.implicitly_defined_regs + count[0] = len(regs) + reg_buf = (ctypes.c_uint * len(regs))() + for i in xrange(0, len(regs)): + reg_buf[i] = self.arch.regs[regs[i]].index + result = ctypes.cast(reg_buf, ctypes.c_void_p) + self._pending_reg_lists[result.value] = (result, reg_buf) + return result.value + except: + log.log_error(traceback.format_exc()) + count[0] = 0 + return None + + def _get_incoming_reg_value(self, ctxt, reg, func, result): + try: + func_obj = function.Function(binaryview.BinaryView(handle = core.BNGetFunctionData(func)), + core.BNNewFunctionReference(func)) + reg_name = self.arch.get_reg_name(reg) + api_obj = self.perform_get_incoming_reg_value(reg_name, func_obj)._to_api_object() + except: + log.log_error(traceback.format_exc()) + api_obj = function.RegisterValue()._to_api_object() + result[0].state = api_obj.state + result[0].value = api_obj.value + + def _get_incoming_flag_value(self, ctxt, reg, func, result): + try: + func_obj = function.Function(binaryview.BinaryView(handle = core.BNGetFunctionData(func)), + core.BNNewFunctionReference(func)) + reg_name = self.arch.get_reg_name(reg) + api_obj = self.perform_get_incoming_flag_value(reg_name, func_obj)._to_api_object() + except: + log.log_error(traceback.format_exc()) + api_obj = function.RegisterValue()._to_api_object() + result[0].state = api_obj.state + result[0].value = api_obj.value + def __repr__(self): return "<calling convention: %s %s>" % (self.arch.name, self.name) def __str__(self): return self.name + + def perform_get_incoming_reg_value(self, reg, func): + return function.RegisterValue() + + def perform_get_incoming_flag_value(self, reg, func): + return function.RegisterValue() + + def with_confidence(self, confidence): + return CallingConvention(self.arch, handle = core.BNNewCallingConventionReference(self.handle), + confidence = confidence) + + def get_incoming_reg_value(self, reg, func): + reg_num = self.arch.get_reg_index(reg) + func_handle = None + if func is not None: + func_handle = func.handle + return function.RegisterValue(self.arch, core.BNGetIncomingRegisterValue(self.handle, reg_num, func_handle)) + + def get_incoming_flag_value(self, flag, func): + reg_num = self.arch.get_flag_index(flag) + func_handle = None + if func is not None: + func_handle = func.handle + return function.RegisterValue(self.arch, core.BNGetIncomingFlagValue(self.handle, reg_num, func_handle)) diff --git a/python/examples/angr_plugin.py b/python/examples/angr_plugin.py index c84373be..26f8040c 100644 --- a/python/examples/angr_plugin.py +++ b/python/examples/angr_plugin.py @@ -42,7 +42,7 @@ from binaryninja.binaryview import BinaryView from binaryninja.plugin import BackgroundTaskThread, PluginCommand from binaryninja.interaction import show_plain_text_report, show_message_box from binaryninja.highlight import HighlightColor -from binaryninja.enums import HighlightStandardColor, MessageBoxButtonSet +from binaryninja.enums import HighlightStandardColor, MessageBoxButtonSet, MessageBoxIcon # Disable warning logs as they show up as errors in the UI logging.disable(logging.WARNING) @@ -137,7 +137,7 @@ def solve(bv): if len(bv.session_data.angr_find) == 0: show_message_box("Angr Solve", "You have not specified a goal instruction.\n\n" + "Please right click on the goal instruction and select \"Find Path to This Instruction\" to " + - "continue.", MessageBoxButtonSet.OKButtonSet, MessageBoxButtonSet.ErrorIcon) + "continue.", MessageBoxButtonSet.OKButtonSet, MessageBoxIcon.ErrorIcon) return # Start a solver thread for the path associated with the view diff --git a/python/function.py b/python/function.py index 8beebe66..5daa7b2a 100644 --- a/python/function.py +++ b/python/function.py @@ -37,6 +37,7 @@ import lowlevelil import mediumlevelil import binaryview import log +import callingconvention class LookupTableEntry(object): @@ -49,26 +50,59 @@ class LookupTableEntry(object): class RegisterValue(object): - def __init__(self, arch, value): - self.type = RegisterValueType(value.state) - if value.state == RegisterValueType.EntryValue: - self.reg = arch.get_reg_name(value.value) - elif value.state == RegisterValueType.ConstantValue: - self.value = value.value - elif value.state == RegisterValueType.StackFrameOffset: - self.offset = value.value + def __init__(self, arch = None, value = None, confidence = types.max_confidence): + if value is None: + self.type = RegisterValueType.UndeterminedValue + else: + self.type = RegisterValueType(value.state) + self.is_constant = False + if value.state == RegisterValueType.EntryValue: + self.arch = arch + if arch is not None: + self.reg = arch.get_reg_name(value.value) + else: + self.reg = value.value + elif (value.state == RegisterValueType.ConstantValue) or (value.state == RegisterValueType.ConstantPointerValue): + self.value = value.value + self.is_constant = True + elif value.state == RegisterValueType.StackFrameOffset: + self.offset = value.value + elif value.state == RegisterValueType.ImportedAddressValue: + self.value = value.value + self.confidence = confidence def __repr__(self): if self.type == RegisterValueType.EntryValue: 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.ReturnAddressValue: return "<return address>" + if self.type == RegisterValueType.ImportedAddressValue: + return "<imported address from entry %#x>" % self.value return "<undetermined>" + def _to_api_object(self): + result = core.BNRegisterValue() + result.state = self.type + result.value = 0 + if self.type == RegisterValueType.EntryValue: + if self.arch is not None: + result.value = self.arch.get_reg_index(self.reg) + else: + result.value = self.reg + elif (self.type == RegisterValueType.ConstantValue) or (self.type == RegisterValueType.ConstantPointerValue): + result.value = self.value + elif self.type == RegisterValueType.StackFrameOffset: + result.value = self.offset + elif self.type == RegisterValueType.ImportedAddressValue: + result.value = self.value + return result + class ValueRange(object): def __init__(self, start, end, step): @@ -148,12 +182,13 @@ class PossibleValueSet(object): class StackVariableReference(object): - def __init__(self, src_operand, t, name, var, ref_ofs): + def __init__(self, src_operand, t, name, var, ref_ofs, size): self.source_operand = src_operand self.type = t self.name = name self.var = var self.referenced_offset = ref_ofs + self.size = size if self.source_operand == 0xffffffff: self.source_operand = None @@ -183,9 +218,11 @@ class Variable(object): if name is None: name = core.BNGetVariableName(func.handle, var) if var_type is None: - var_type = core.BNGetVariableType(func.handle, var) - if var_type: - var_type = types.Type(var_type) + var_type_conf = core.BNGetVariableType(func.handle, var) + if var_type_conf.type: + var_type = types.Type(var_type_conf.type, platform = func.platform, confidence = var_type_conf.confidence) + else: + var_type = None self.name = name self.type = var_type @@ -203,6 +240,12 @@ class Variable(object): def __str__(self): return self.name + def __eq__(self, other): + return self.identifier == other.identifier + + def __hash__(self): + return hash(self.identifier) + class ConstantReference(object): def __init__(self, val, size, ptr, intermediate): @@ -231,6 +274,28 @@ class IndirectBranchInfo(object): return "<branch %s:%#x -> %s:%#x>" % (self.source_arch.name, self.source_addr, self.dest_arch.name, self.dest_addr) +class ParameterVariables(object): + def __init__(self, var_list, confidence = types.max_confidence): + self.vars = var_list + self.confidence = confidence + + def __repr__(self): + return repr(self.vars) + + def __iter__(self): + for var in self.vars: + yield var + + def __getitem__(self, idx): + return self.vars[idx] + + def __len__(self): + return len(self.vars) + + def with_confidence(self, confidence): + return ParameterVariables(list(self.vars), confidence = confidence) + + class _FunctionAssociatedDataStore(associateddatastore._AssociatedDataStore): _defaults = {} @@ -258,6 +323,9 @@ class Function(object): return True return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) + def __hash__(self): + return hash((self.start, self.arch.name, self.platform.name)) + @classmethod def _unregister(cls, func): handle = ctypes.cast(func, ctypes.c_void_p) @@ -323,8 +391,19 @@ class Function(object): @property def can_return(self): - """Whether function can return (read-only)""" - return core.BNCanFunctionReturn(self.handle) + """Whether function can return""" + result = core.BNCanFunctionReturn(self.handle) + return types.BoolWithConfidence(result.value, confidence = result.confidence) + + @can_return.setter + def can_return(self, value): + bc = core.BNBoolWithConfidence() + bc.value = bool(value) + if hasattr(value, 'confidence'): + bc.confidence = value.confidence + else: + bc.confidence = types.max_confidence + core.BNSetUserFunctionCanReturn(self.handle, bc) @property def explicitly_defined_type(self): @@ -376,7 +455,7 @@ class Function(object): @property def function_type(self): """Function type object""" - return types.Type(core.BNGetFunctionType(self.handle)) + return types.Type(core.BNGetFunctionType(self.handle), platform = self.platform) @function_type.setter def function_type(self, value): @@ -390,7 +469,7 @@ class Function(object): result = [] for i in xrange(0, count.value): result.append(Variable(self, v[i].var.type, v[i].var.index, v[i].var.storage, v[i].name, - types.Type(handle = core.BNNewTypeReference(v[i].type)))) + types.Type(handle = core.BNNewTypeReference(v[i].type), platform = self.platform, confidence = v[i].typeConfidence))) result.sort(key = lambda x: x.identifier) core.BNFreeVariableList(v, count.value) return result @@ -403,7 +482,7 @@ class Function(object): result = [] for i in xrange(0, count.value): result.append(Variable(self, v[i].var.type, v[i].var.index, v[i].var.storage, v[i].name, - types.Type(handle = core.BNNewTypeReference(v[i].type)))) + types.Type(handle = core.BNNewTypeReference(v[i].type), platform = self.platform, confidence = v[i].typeConfidence))) result.sort(key = lambda x: x.identifier) core.BNFreeVariableList(v, count.value) return result @@ -440,6 +519,153 @@ class Function(object): core.BNFreeAnalysisPerformanceInfo(info, count.value) return result + @property + def type_tokens(self): + """Text tokens for this function's prototype""" + return self.get_type_tokens()[0].tokens + + @property + def return_type(self): + """Return type of the function""" + result = core.BNGetFunctionReturnType(self.handle) + if not result.type: + return None + return types.Type(result.type, platform = self.platform, confidence = result.confidence) + + @return_type.setter + def return_type(self, value): + type_conf = core.BNTypeWithConfidence() + if value is None: + type_conf.type = None + type_conf.confidence = 0 + else: + type_conf.type = value.handle + type_conf.confidence = value.confidence + core.BNSetUserFunctionReturnType(self.handle, type_conf) + + @property + def calling_convention(self): + """Calling convention used by the function""" + result = core.BNGetFunctionCallingConvention(self.handle) + if not result.convention: + return None + return callingconvention.CallingConvention(None, handle = result.convention, confidence = result.confidence) + + @calling_convention.setter + def calling_convention(self, value): + conv_conf = core.BNCallingConventionWithConfidence() + if value is None: + conv_conf.convention = None + conv_conf.confidence = 0 + else: + conv_conf.convention = value.handle + conv_conf.confidence = value.confidence + core.BNSetUserFunctionCallingConvention(self.handle, conv_conf) + + @property + def parameter_vars(self): + """List of variables for the incoming function parameters""" + result = core.BNGetFunctionParameterVariables(self.handle) + var_list = [] + for i in xrange(0, result.count): + var_list.append(Variable(self, result.vars[i].type, result.vars[i].index, result.vars[i].storage)) + confidence = result.confidence + core.BNFreeParameterVariables(result) + return ParameterVariables(var_list, confidence = confidence) + + @parameter_vars.setter + def parameter_vars(self, value): + if value is None: + var_list = [] + else: + var_list = list(value) + var_conf = core.BNParameterVariablesWithConfidence() + var_conf.vars = (core.BNVariable * len(var_list))() + var_conf.count = len(var_list) + for i in xrange(0, len(var_list)): + var_conf.vars[i].type = var_list[i].source_type + var_conf.vars[i].index = var_list[i].index + var_conf.vars[i].storage = var_list[i].storage + if value is None: + var_conf.confidence = 0 + elif hasattr(value, 'confidence'): + var_conf.confidence = value.confidence + else: + var_conf.confidence = types.max_confidence + core.BNSetUserFunctionParameterVariables(self.handle, var_conf) + + @property + def has_variable_arguments(self): + """Whether the function takes a variable number of arguments""" + result = core.BNFunctionHasVariableArguments(self.handle) + return types.BoolWithConfidence(result.value, confidence = result.confidence) + + @has_variable_arguments.setter + def has_variable_arguments(self, value): + bc = core.BNBoolWithConfidence() + bc.value = bool(value) + if hasattr(value, 'confidence'): + bc.confidence = value.confidence + else: + bc.confidence = types.max_confidence + core.BNSetUserFunctionHasVariableArguments(self.handle, bc) + + @property + def stack_adjustment(self): + """Number of bytes removed from the stack after return""" + result = core.BNGetFunctionStackAdjustment(self.handle) + return types.SizeWithConfidence(result.value, confidence = result.confidence) + + @stack_adjustment.setter + def stack_adjustment(self, value): + sc = core.BNSizeWithConfidence() + sc.value = int(value) + if hasattr(value, 'confidence'): + sc.confidence = value.confidence + else: + sc.confidence = types.max_confidence + core.BNSetUserFunctionStackAdjustment(self.handle, sc) + + @property + def clobbered_regs(self): + """Registers that are modified by this function""" + result = core.BNGetFunctionClobberedRegisters(self.handle) + reg_set = [] + for i in xrange(0, result.count): + reg_set.append(self.arch.get_reg_name(result.regs[i])) + regs = types.RegisterSet(reg_set, confidence = result.confidence) + core.BNFreeClobberedRegisters(result) + return regs + + @clobbered_regs.setter + def clobbered_regs(self, value): + regs = core.BNRegisterSetWithConfidence() + regs.regs = (ctypes.c_uint * len(value))() + regs.count = len(value) + for i in xrange(0, len(value)): + regs.regs[i] = self.arch.get_reg_index(value[i]) + if hasattr(value, 'confidence'): + regs.confidence = value.confidence + else: + regs.confidence = types.max_confidence + core.BNSetUserFunctionClobberedRegisters(self.handle, regs) + + @property + def global_pointer_value(self): + """Discovered value of the global pointer register, if the function uses one (read-only)""" + result = core.BNGetFunctionGlobalPointerValue(self.handle) + return RegisterValue(self.arch, result.value, confidence = result.confidence) + + @property + def comment(self): + """Gets the comment for the current function""" + return core.BNGetFunctionComment(self.handle) + + @comment.setter + def comment(self, comment): + """Sets a comment for the current function""" + return core.BNSetFunctionComment(self.handle, comment) + def __iter__(self): count = ctypes.c_ulonglong() blocks = core.BNGetFunctionBasicBlockList(self.handle, count) @@ -469,6 +695,21 @@ class Function(object): return core.BNGetCommentForAddress(self.handle, addr) def set_comment(self, addr, comment): + """Deprecated use set_comment_at instead""" + core.BNSetCommentForAddress(self.handle, addr, comment) + + def set_comment_at(self, addr, comment): + """ + ``set_comment_at`` sets a comment for the current function at the address specified + + :param addr int: virtual address within the current function to apply the comment to + :param comment str: string comment to apply + :rtype: None + :Example: + + >>> current_function.set_comment_at(here, "hi") + + """ core.BNSetCommentForAddress(self.handle, addr, comment) def get_low_level_il_at(self, addr, arch=None): @@ -616,10 +857,10 @@ class Function(object): refs = core.BNGetStackVariablesReferencedByInstruction(self.handle, arch.handle, addr, count) result = [] for i in xrange(0, count.value): - var_type = types.Type(core.BNNewTypeReference(refs[i].type)) + var_type = types.Type(core.BNNewTypeReference(refs[i].type), platform = self.platform, confidence = refs[i].typeConfidence) result.append(StackVariableReference(refs[i].sourceOperand, var_type, refs[i].name, Variable.from_identifier(self, refs[i].varIdentifier, refs[i].name, var_type), - refs[i].referencedOffset)) + refs[i].referencedOffset, refs[i].size)) core.BNFreeStackVariableReferenceList(refs, count.value) return result @@ -730,8 +971,9 @@ class Function(object): size = lines[i].tokens[j].size operand = lines[i].tokens[j].operand context = lines[i].tokens[j].context + confidence = lines[i].tokens[j].confidence address = lines[i].tokens[j].address - tokens.append(InstructionTextToken(token_type, text, value, size, operand, context, address)) + tokens.append(InstructionTextToken(token_type, text, value, size, operand, context, address, confidence)) result.append(tokens) core.BNFreeInstructionTextLines(lines, count.value) return result @@ -742,6 +984,85 @@ class Function(object): def set_user_type(self, value): core.BNSetFunctionUserType(self.handle, value.handle) + def set_auto_return_type(self, value): + type_conf = core.BNTypeWithConfidence() + if value is None: + type_conf.type = None + type_conf.confidence = 0 + else: + type_conf.type = value.handle + type_conf.confidence = value.confidence + core.BNSetAutoFunctionReturnType(self.handle, type_conf) + + def set_auto_calling_convention(self, value): + conv_conf = core.BNCallingConventionWithConfidence() + if value is None: + conv_conf.convention = None + conv_conf.confidence = 0 + else: + conv_conf.convention = value.handle + conv_conf.confidence = value.confidence + core.BNSetAutoFunctionCallingConvention(self.handle, conv_conf) + + def set_auto_parameter_vars(self, value): + if value is None: + var_list = [] + else: + var_list = list(value) + var_conf = core.BNParameterVariablesWithConfidence() + var_conf.vars = (core.BNVariable * len(var_list))() + var_conf.count = len(var_list) + for i in xrange(0, len(var_list)): + var_conf.vars[i].type = var_list[i].source_type + var_conf.vars[i].index = var_list[i].index + var_conf.vars[i].storage = var_list[i].storage + if value is None: + var_conf.confidence = 0 + elif hasattr(value, 'confidence'): + var_conf.confidence = value.confidence + else: + var_conf.confidence = types.max_confidence + core.BNSetAutoFunctionParameterVariables(self.handle, var_conf) + + def set_auto_has_variable_arguments(self, value): + bc = core.BNBoolWithConfidence() + bc.value = bool(value) + if hasattr(value, 'confidence'): + bc.confidence = value.confidence + else: + bc.confidence = types.max_confidence + core.BNSetAutoFunctionHasVariableArguments(self.handle, bc) + + def set_auto_can_return(self, value): + bc = core.BNBoolWithConfidence() + bc.value = bool(value) + if hasattr(value, 'confidence'): + bc.confidence = value.confidence + else: + bc.confidence = types.max_confidence + core.BNSetAutoFunctionCanReturn(self.handle, bc) + + def set_auto_stack_adjustment(self, value): + sc = core.BNSizeWithConfidence() + sc.value = int(value) + if hasattr(value, 'confidence'): + sc.confidence = value.confidence + else: + sc.confidence = types.max_confidence + core.BNSetAutoFunctionStackAdjustment(self.handle, sc) + + def set_auto_clobbered_regs(self, value): + regs = core.BNRegisterSetWithConfidence() + regs.regs = (ctypes.c_uint * len(value))() + regs.count = len(value) + for i in xrange(0, len(value)): + regs.regs[i] = self.arch.get_reg_index(value[i]) + if hasattr(value, 'confidence'): + regs.confidence = value.confidence + else: + regs.confidence = types.max_confidence + core.BNSetAutoFunctionClobberedRegisters(self.handle, regs) + def get_int_display_type(self, instr_addr, value, operand, arch=None): if arch is None: arch = self.arch @@ -818,7 +1139,7 @@ class Function(object): """ ``set_auto_instr_highlight`` highlights the instruction at the specified address with the supplied color - .warning:: Use only in analysis plugins. Do not use in regular plugins, as colors won't be saved to the database. + ..warning:: Use only in analysis plugins. Do not use in regular plugins, as colors won't be saved to the database. :param int addr: virtual address of the instruction to be highlighted :param HighlightStandardColor or highlight.HighlightColor color: Color value to use for highlighting @@ -853,10 +1174,16 @@ class Function(object): core.BNSetUserInstructionHighlight(self.handle, arch.handle, addr, color._get_core_struct()) def create_auto_stack_var(self, offset, var_type, name): - core.BNCreateAutoStackVariable(self.handle, offset, var_type.handle, name) + tc = core.BNTypeWithConfidence() + tc.type = var_type.handle + tc.confidence = var_type.confidence + core.BNCreateAutoStackVariable(self.handle, offset, tc, name) def create_user_stack_var(self, offset, var_type, name): - core.BNCreateUserStackVariable(self.handle, offset, var_type.handle, name) + tc = core.BNTypeWithConfidence() + tc.type = var_type.handle + tc.confidence = var_type.confidence + core.BNCreateUserStackVariable(self.handle, offset, tc, name) def delete_auto_stack_var(self, offset): core.BNDeleteAutoStackVariable(self.handle, offset) @@ -869,14 +1196,20 @@ class Function(object): var_data.type = var.source_type var_data.index = var.index var_data.storage = var.storage - core.BNCreateAutoVariable(self.handle, var_data, var_type.handle, name, ignore_disjoint_uses) + tc = core.BNTypeWithConfidence() + tc.type = var_type.handle + tc.confidence = var_type.confidence + core.BNCreateAutoVariable(self.handle, var_data, tc, name, ignore_disjoint_uses) def create_user_var(self, var, var_type, name, ignore_disjoint_uses = False): var_data = core.BNVariable() var_data.type = var.source_type var_data.index = var.index var_data.storage = var.storage - core.BNCreateUserVariable(self.handle, var_data, var_type.handle, name, ignore_disjoint_uses) + tc = core.BNTypeWithConfidence() + tc.type = var_type.handle + tc.confidence = var_type.confidence + core.BNCreateUserVariable(self.handle, var_data, tc, name, ignore_disjoint_uses) def delete_auto_var(self, var): var_data = core.BNVariable() @@ -899,10 +1232,38 @@ class Function(object): if not core.BNGetStackVariableAtFrameOffset(self.handle, arch.handle, addr, offset, found_var): return None result = Variable(self, found_var.var.type, found_var.var.index, found_var.var.storage, - found_var.name, types.Type(handle = core.BNNewTypeReference(found_var.type))) + found_var.name, types.Type(handle = core.BNNewTypeReference(found_var.type), platform = self.platform, + confidence = found_var.typeConfidence)) core.BNFreeVariableNameAndType(found_var) return result + def get_type_tokens(self, settings=None): + if settings is not None: + settings = settings.handle + count = ctypes.c_ulonglong() + lines = core.BNGetFunctionTypeTokens(self.handle, settings, count) + result = [] + for i in xrange(0, count.value): + addr = lines[i].addr + tokens = [] + for j in xrange(0, lines[i].count): + token_type = InstructionTextTokenType(lines[i].tokens[j].type) + text = lines[i].tokens[j].text + value = lines[i].tokens[j].value + size = lines[i].tokens[j].size + operand = lines[i].tokens[j].operand + context = lines[i].tokens[j].context + confidence = lines[i].tokens[j].confidence + address = lines[i].tokens[j].address + tokens.append(InstructionTextToken(token_type, text, value, size, operand, context, address, confidence)) + result.append(DisassemblyTextLine(addr, tokens)) + core.BNFreeDisassemblyTextLines(lines, count.value) + return result + + def get_reg_value_at_exit(self, reg): + result = core.BNGetFunctionRegisterValueAtExit(self.handle, self.arch.get_reg_index(reg)) + return RegisterValue(self.arch, result.value, confidence = result.confidence) + class AdvancedFunctionAnalysisDataRequestor(object): def __init__(self, func = None): @@ -1043,8 +1404,9 @@ class FunctionGraphBlock(object): size = lines[i].tokens[j].size operand = lines[i].tokens[j].operand context = lines[i].tokens[j].context + confidence = lines[i].tokens[j].confidence address = lines[i].tokens[j].address - tokens.append(InstructionTextToken(token_type, text, value, size, operand, context, address)) + tokens.append(InstructionTextToken(token_type, text, value, size, operand, context, address, confidence)) result.append(DisassemblyTextLine(addr, tokens)) core.BNFreeDisassemblyTextLines(lines, count.value) return result @@ -1101,8 +1463,9 @@ class FunctionGraphBlock(object): size = lines[i].tokens[j].size operand = lines[i].tokens[j].operand context = lines[i].tokens[j].context + confidence = lines[i].tokens[j].confidence address = lines[i].tokens[j].address - tokens.append(InstructionTextToken(token_type, text, value, size, operand, context, address)) + tokens.append(InstructionTextToken(token_type, text, value, size, operand, context, address, confidence)) yield DisassemblyTextLine(addr, tokens) finally: core.BNFreeDisassemblyTextLines(lines, count.value) @@ -1384,13 +1747,14 @@ class InstructionTextToken(object): """ def __init__(self, token_type, text, value = 0, size = 0, operand = 0xffffffff, - context = InstructionTextTokenContext.NoTokenContext, address = 0): + context = InstructionTextTokenContext.NoTokenContext, address = 0, confidence = types.max_confidence): self.type = InstructionTextTokenType(token_type) self.text = text self.value = value self.size = size self.operand = operand self.context = InstructionTextTokenContext(context) + self.confidence = confidence self.address = address def __str__(self): diff --git a/python/generator.cpp b/python/generator.cpp index 6f19db66..f838b36d 100644 --- a/python/generator.cpp +++ b/python/generator.cpp @@ -165,8 +165,15 @@ int main(int argc, char* argv[]) // Parse API header to get type and function information map<QualifiedName, Ref<Type>> types, vars, funcs; string errors; - bool ok = Architecture::GetByName("generator")->ParseTypesFromSourceFile(argv[1], types, vars, funcs, errors); - fprintf(stderr, "%s", errors.c_str()); + auto arch = Architecture::GetByName("generator"); + if (!arch) + { + printf("ERROR: License file validation failed (most likely)\n"); + return 1; + } + + bool ok = arch->GetStandalonePlatform()->ParseTypesFromSourceFile(argv[1], types, vars, funcs, errors); + fprintf(stderr, "Errors: %s", errors.c_str()); if (!ok) return 1; @@ -230,22 +237,61 @@ int main(int argc, char* argv[]) fprintf(out, "\n# Structure definitions\n"); + set<QualifiedName> structsToProcess; + set<QualifiedName> finishedStructs; for (auto& i : types) + structsToProcess.insert(i.first); + while (structsToProcess.size() != 0) { - string name; - if (i.first.size() != 1) - continue; - name = i.first[0]; - if ((i.second->GetClass() == StructureTypeClass) && (i.second->GetStructure()->GetMembers().size() != 0)) + set<QualifiedName> currentStructList = structsToProcess; + structsToProcess.clear(); + bool processedSome = false; + for (auto& i : currentStructList) { - fprintf(out, "%s._fields_ = [\n", name.c_str()); - for (auto& j : i.second->GetStructure()->GetMembers()) + string name; + if (i.size() != 1) + continue; + Ref<Type> type = types[i]; + name = i[0]; + if ((type->GetClass() == StructureTypeClass) && (type->GetStructure()->GetMembers().size() != 0)) { - fprintf(out, "\t\t(\"%s\", ", j.name.c_str()); - OutputType(out, j.type); - fprintf(out, "),\n"); + bool requiresDependency = false; + for (auto& j : type->GetStructure()->GetMembers()) + { + if ((j.type->GetClass() == NamedTypeReferenceClass) && + (types[j.type->GetNamedTypeReference()->GetName()]->GetClass() == StructureTypeClass) && + (finishedStructs.count(j.type->GetNamedTypeReference()->GetName()) == 0)) + { + // This structure needs another structure that isn't fully defined yet, need to wait + // for the dependencies to be defined + structsToProcess.insert(i); + requiresDependency = true; + break; + } + } + + if (requiresDependency) + continue; + + fprintf(out, "%s._fields_ = [\n", name.c_str()); + for (auto& j : type->GetStructure()->GetMembers()) + { + fprintf(out, "\t\t(\"%s\", ", j.name.c_str()); + OutputType(out, j.type); + fprintf(out, "),\n"); + } + fprintf(out, "\t]\n"); + finishedStructs.insert(i); + processedSome = true; } - fprintf(out, "\t]\n"); + } + + if (!processedSome) + { + fprintf(stderr, "Detected dependency cycle in structures\n"); + for (auto& i : structsToProcess) + fprintf(stderr, "%s\n", i.GetString().c_str()); + return 1; } } diff --git a/python/interaction.py b/python/interaction.py index 60607692..979549f5 100644 --- a/python/interaction.py +++ b/python/interaction.py @@ -29,6 +29,9 @@ import log class LabelField(object): + """ + ``LabelField`` adds a text label to the display. + """ def __init__(self, text): self.text = text @@ -44,6 +47,9 @@ class LabelField(object): class SeparatorField(object): + """ + ``SeparatorField`` adds vertical separation to the display. + """ def _fill_core_struct(self, value): value.type = FormInputFieldType.SeparatorFormField @@ -55,6 +61,9 @@ class SeparatorField(object): class TextLineField(object): + """ + ``TextLineField`` Adds prompt for text string input. Result is stored in self.result as a string on completion. + """ def __init__(self, prompt): self.prompt = prompt self.result = None @@ -71,6 +80,10 @@ class TextLineField(object): class MultilineTextField(object): + """ + ``MultilineTextField`` add multi-line text string input field. Result is stored in self.result + as a string. This option is not supported on the command line. + """ def __init__(self, prompt): self.prompt = prompt self.result = None @@ -87,6 +100,9 @@ class MultilineTextField(object): class IntegerField(object): + """ + ``IntegerField`` add prompt for integer. Result is stored in self.result as an int. + """ def __init__(self, prompt): self.prompt = prompt self.result = None @@ -103,7 +119,15 @@ class IntegerField(object): class AddressField(object): - def __init__(self, prompt, view = None, current_address = 0): + """ + ``AddressField`` prompts the user for an address. By passing the optional view and current_address parameters + offsets can be used instead of just an address. Th reslut is stored as in int in self.result. + + Note: This API currenlty functions differently on the command line, as the view and current_address are + disregarded. Additionally where as in the ui the result defaults to hexidecimal on the command line 0x must be + specified. + """ + def __init__(self, prompt, view=None, current_address=0): self.prompt = prompt self.view = view self.current_address = current_address @@ -125,6 +149,10 @@ 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. + """ def __init__(self, prompt, choices): self.prompt = prompt self.choices = choices @@ -147,7 +175,10 @@ class ChoiceField(object): class OpenFileNameField(object): - def __init__(self, prompt, ext = ""): + """ + ``OpenFileNameField`` prompts the user to specify a file name to open. Result is stored in self.result as a string. + """ + def __init__(self, prompt, ext=""): self.prompt = prompt self.ext = ext self.result = None @@ -165,7 +196,10 @@ class OpenFileNameField(object): class SaveFileNameField(object): - def __init__(self, prompt, ext = "", default_name = ""): + """ + ``SaveFileNameField`` prompts the user to specify a file name to save. Result is stored in self.result as a string. + """ + def __init__(self, prompt, ext="", default_name=""): self.prompt = prompt self.ext = ext self.default_name = default_name @@ -185,13 +219,17 @@ class SaveFileNameField(object): class DirectoryNameField(object): - def __init__(self, prompt, default_name = ""): + """ + ``DirectoryNameField`` prompts the user to specify a directory name to open. Result is stored in self.result as + a string. + """ + def __init__(self, prompt, default_name=""): self.prompt = prompt self.default_name = default_name self.result = None def _fill_core_struct(self, value): - value.type = DirectoryNameField + value.type = FormInputFieldType.DirectoryNameFormField value.prompt = self.prompt value.defaultName = self.default_name @@ -353,14 +391,14 @@ class InteractionHandler(object): field_objs.append(AddressField(fields[i].prompt, view, fields[i].currentAddress)) elif fields[i].type == FormInputFieldType.ChoiceFormField: choices = [] - for i in xrange(0, fields[i].count): - choices.append(fields[i].choices[i]) + for j in xrange(0, fields[i].count): + choices.append(fields[i].choices[j]) field_objs.append(ChoiceField(fields[i].prompt, choices)) elif fields[i].type == FormInputFieldType.OpenFileNameFormField: field_objs.append(OpenFileNameField(fields[i].prompt, fields[i].ext)) elif fields[i].type == FormInputFieldType.SaveFileNameFormField: field_objs.append(SaveFileNameField(fields[i].prompt, fields[i].ext, fields[i].defaultName)) - elif fields[i].type == DirectoryNameField: + elif fields[i].type == FormInputFieldType.DirectoryNameFormField: field_objs.append(DirectoryNameField(fields[i].prompt, fields[i].defaultName)) else: field_objs.append(LabelField(fields[i].prompt)) @@ -424,22 +462,86 @@ class InteractionHandler(object): def markdown_to_html(contents): + """ + ``markdown_to_html`` converts the provided markdown to HTML. + + :param string contents: Markdown contents to convert to HTML. + :rtype: string + :Example: + >>> markdown_to_html("##Yay") + '<h2>Yay</h2>' + """ return core.BNMarkdownToHTML(contents) def show_plain_text_report(title, contents): + """ + ``show_plain_text_report`` displays contents to the user in the UI or on the command line. + + Note: This API function differently on the command line vs. the UI. In the UI a popup is used. On the commandline + a simple text prompt is used. + + :param str title: title to display in the UI popup. + :param str contents: plain text contents to display + :rtype: None + :Example: + >>> show_plain_text_report("title", "contents") + contents + """ core.BNShowPlainTextReport(None, title, contents) -def show_markdown_report(title, contents, plaintext = ""): +def show_markdown_report(title, contents, plaintext=""): + """ + ``show_markdown_report`` displays the markdown contents in UI applications and plaintext in command line + applications. + + Note: This API function differently on the command line vs. the UI. In the UI a popup is used. On the commandline + a simple text prompt is used. + + :param str contents: markdown contents to display + :param str plaintext: Plain text version to display (used on the command line) + :rtype: None + :Example: + >>> show_markdown_report("title", "##Contents", "Plain text contents") + Plain text contents + """ core.BNShowMarkdownReport(None, title, contents, plaintext) -def show_html_report(title, contents, plaintext = ""): +def show_html_report(title, contents, plaintext=""): + """ + ``show_html_report`` displays the html contents in UI applications and plaintext in command line + applications. + + Note: This API function differently on the command line vs. the UI. In the UI a popup is used. On the commandline + a simple text prompt is used. + + :param str contents: HTML contents to display + :param str plaintext: Plain text version to display (used on the command line) + :rtype: None + :Example" + >>> show_html_report("title", "<h1>Contents</h1>", "Plain text contents") + Plain text contents + """ core.BNShowHTMLReport(None, title, contents, plaintext) def get_text_line_input(prompt, title): + """ + ``get_text_line_input`` prompts the user to input a string with the given prompt and title. + + Note: This API function differently on the command line vs. the UI. In the UI a popup is used. On the commandline + a simple text prompt is used. + + :param str prompt: String to prompt with. + :param str title: Title of the window when executed in the UI. + :rtype: string containing the input without trailing newline character. + :Example: + >>> get_text_line_input("PROMPT>", "getinfo") + PROMPT> Input! + 'Input!' + """ value = ctypes.c_char_p() if not core.BNGetTextLineInput(value, prompt, title): return None @@ -449,6 +551,20 @@ def get_text_line_input(prompt, title): def get_int_input(prompt, title): + """ + ``get_int_input`` prompts the user to input a integer with the given prompt and title. + + Note: This API function differently on the command line vs. the UI. In the UI a popup is used. On the commandline + a simple text prompt is used. + + :param str prompt: String to prompt with. + :param str title: Title of the window when executed in the UI. + :rtype: integer value input by the user. + :Example: + >>> get_int_input("PROMPT>", "getinfo") + PROMPT> 10 + 10 + """ value = ctypes.c_longlong() if not core.BNGetIntegerInput(value, prompt, title): return None @@ -456,6 +572,20 @@ def get_int_input(prompt, title): def get_address_input(prompt, title): + """ + ``get_address_input`` prompts the user for an address with the given prompt and title. + + Note: This API function differently on the command line vs. the UI. In the UI a popup is used. On the commandline + a simple text prompt is used. + + :param str prompt: String to prompt with. + :param str title: Title of the window when executed in the UI. + :rtype: integer value input by the user. + :Example: + >>> get_address_input("PROMPT>", "getinfo") + PROMPT> 10 + 10L + """ value = ctypes.c_ulonglong() if not core.BNGetAddressInput(value, prompt, title, None, 0): return None @@ -463,6 +593,25 @@ def get_address_input(prompt, title): def get_choice_input(prompt, title, choices): + """ + ``get_choice_input`` prompts the user to select the one of the provided choices. + + Note: This API function differently on the command line vs. the UI. In the UI a popup is used. On the commandline + a simple text prompt is used. The ui uses a combo box. + + :param str prompt: String to prompt with. + :param str title: Title of the window when executed in the UI. + :param list choices: A list of strings for the user to choose from. + :rtype: integer array index of the selected option + :Example: + >>> get_choice_input("PROMPT>", "choices", ["Yes", "No", "Maybe"]) + choices + 1) Yes + 2) No + 3) Maybe + PROMPT> 1 + 0L + """ choice_buf = (ctypes.c_char_p * len(choices))() for i in xrange(0, len(choices)): choice_buf[i] = str(choices[i]) @@ -472,7 +621,20 @@ def get_choice_input(prompt, title, choices): return value.value -def get_open_filename_input(prompt, ext = ""): +def get_open_filename_input(prompt, ext=""): + """ + ``get_open_filename_input`` prompts the user for a file name to open. + + Note: This API function differently on the command line vs. the UI. In the UI a popup is used. On the commandline + a simple text prompt is used. The ui uses the native window popup for file selection. + + :param str prompt: Prompt to display. + :param str ext: Optional, file extension + :Example: + >>> get_open_filename_input("filename:", "exe") + filename: foo.exe + 'foo.exe' + """ value = ctypes.c_char_p() if not core.BNGetOpenFileNameInput(value, prompt, ext): return None @@ -481,7 +643,22 @@ def get_open_filename_input(prompt, ext = ""): return result -def get_save_filename_input(prompt, ext = "", default_name = ""): +def get_save_filename_input(prompt, ext="", default_name=""): + """ + ``get_save_filename_input`` prompts the user for a file name to save as, optionally providing a file extension and + default_name. + + Note: This API function differently on the command line vs. the UI. In the UI a popup is used. On the commandline + a simple text prompt is used. The ui uses the native window popup for file selection. + + :param str prompt: Prompt to display. + :param str ext: Optional, file extension + :param str default_name: Optional, default file name. + :Example: + >>> get_save_filename_input("filename:", "exe", "foo.exe") + filename: foo.exe + 'foo.exe' + """ value = ctypes.c_char_p() if not core.BNGetSaveFileNameInput(value, prompt, ext, default_name): return None @@ -490,7 +667,22 @@ def get_save_filename_input(prompt, ext = "", default_name = ""): return result -def get_directory_name_input(prompt, default_name = ""): +def get_directory_name_input(prompt, default_name=""): + """ + ``get_directory_name_input`` prompts the user for a directory name to save as, optionally providing and + default_name. + + Note: This API function differently on the command line vs. the UI. In the UI a popup is used. On the commandline + a simple text prompt is used. The ui uses the native window popup for file selection. + + :param str prompt: Prompt to display. + :param str default_name: Optional, default directory name. + :rtype: str + :Example: + >>> get_directory_name_input("prompt") + prompt dirname + 'dirname' + """ value = ctypes.c_char_p() if not core.BNGetDirectoryNameInput(value, prompt, default_name): return None @@ -500,6 +692,43 @@ def get_directory_name_input(prompt, default_name = ""): def get_form_input(fields, title): + """ + ``get_from_input`` Prompts the user for a set of inputs specified in ``fields`` with given title. + The fields parameter is a list which can contain the following types: + - str - an alias for LabelField + - None - an alias for SeparatorField + - LabelField - Text output + - SeparatorField - Vertical spacing + - TextLineField - Prompt for a string value + - MultilineTextField - Prompt for multi-line string value + - IntegerField - Prompt for an integer + - AddressField - Prompt for an address + - ChoiceField - Prompt for a choice from provided options + - OpenFileNameField - Prompt for file to open + - SaveFileNameField - Prompt for file to save to + - DirectoryNameField - Prompt for directory name + This API is flexible and works both in the UI via a popup dialog and on the command line. + :params list fields: A list containing of the above specified classes, strings or None + :params str title: The title of the popup dialog. + :Example: + + >>> int_f = IntegerField("Specify Integer") + >>> tex_f = TextLineField("Specify name") + >>> choice_f = ChoiceField("Options", ["Yes", "No", "Maybe"]) + >>> get_form_input(["Get Data", None, int_f, tex_f, choice_f], "The options") + Get Data + + Specify Integer 1337 + Specify name Peter + The options + 1) Yes + 2) No + 3) Maybe + Options 1 + >>> True + >>> print tex_f.result, int_f.result, choice_f.result + Peter 1337 0 + """ value = (core.BNFormInputField * len(fields))() for i in xrange(0, len(fields)): if isinstance(fields[i], str): @@ -517,7 +746,7 @@ def get_form_input(fields, title): return True -def show_message_box(title, text, buttons = MessageBoxButtonSet.OKButtonSet, icon = MessageBoxIcon.InformationIcon): +def show_message_box(title, text, buttons=MessageBoxButtonSet.OKButtonSet, icon=MessageBoxIcon.InformationIcon): """ ``show_message_box`` Displays a configurable message box in the UI, or prompts on the console as appropriate retrieves a list of all Symbol objects of the provided symbol type in the optionally diff --git a/python/lineardisassembly.py b/python/lineardisassembly.py index 5ef8d623..f41fdfcb 100644 --- a/python/lineardisassembly.py +++ b/python/lineardisassembly.py @@ -24,7 +24,7 @@ class LinearDisassemblyPosition(object): ``class LinearDisassemblyPosition`` is a helper object containing the position of the current Linear Disassembly. .. note:: This object should not be instantiated directly. Rather call \ - :py:method:`get_linear_disassembly_position_at` which instantiates this object. + :py:meth:`get_linear_disassembly_position_at` which instantiates this object. """ def __init__(self, func, block, addr): self.function = func diff --git a/python/lowlevelil.py b/python/lowlevelil.py index c359a79c..75a3f1ad 100644 --- a/python/lowlevelil.py +++ b/python/lowlevelil.py @@ -161,6 +161,7 @@ class LowLevelILInstruction(object): LowLevelILOperation.LLIL_JUMP: [("dest", "expr")], LowLevelILOperation.LLIL_JUMP_TO: [("dest", "expr"), ("targets", "int_list")], LowLevelILOperation.LLIL_CALL: [("dest", "expr")], + LowLevelILOperation.LLIL_CALL_STACK_ADJUST: [("dest", "expr"), ("stack_adjustment", "int")], LowLevelILOperation.LLIL_RET: [("dest", "expr")], LowLevelILOperation.LLIL_NORET: [], LowLevelILOperation.LLIL_IF: [("condition", "expr"), ("true", "int"), ("false", "int")], @@ -309,8 +310,9 @@ class LowLevelILInstruction(object): size = tokens[i].size operand = tokens[i].operand context = tokens[i].context + confidence = tokens[i].confidence address = tokens[i].address - result.append(function.InstructionTextToken(token_type, text, value, size, operand, context, address)) + result.append(function.InstructionTextToken(token_type, text, value, size, operand, context, address, confidence)) core.BNFreeInstructionText(tokens, count.value) return result @@ -327,8 +329,16 @@ class LowLevelILInstruction(object): core.BNGetLowLevelILNonSSAExprIndex(self.function.handle, self.expr_index)) @property + def medium_level_il(self): + """Gets the medium level IL expression corresponding to this expression (may be None for eliminated instructions)""" + expr = self.function.get_medium_level_il_expr_index(self.expr_index) + if expr is None: + return None + return mediumlevelil.MediumLevelILInstruction(self.function.medium_level_il, expr) + + @property def mapped_medium_level_il(self): - """Gets the medium level IL expression corresponding to this expression""" + """Gets the mapped medium level IL expression corresponding to this expression""" expr = self.function.get_mapped_medium_level_il_expr_index(self.expr_index) if expr is None: return None @@ -722,17 +732,18 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_LOAD, addr.index, size=size) - def store(self, size, addr, value): + def store(self, size, addr, value, flags=None): """ ``store`` Writes ``size`` bytes to expression ``addr`` read from expression ``value`` :param int size: number of bytes to write :param LowLevelILExpr addr: the expression to write to :param LowLevelILExpr value: the expression to be written + :param str flags: which flags are set by this operation :return: The expression ``[addr].size = value`` :rtype: LowLevelILExpr """ - return self.expr(LowLevelILOperation.LLIL_STORE, addr.index, value.index, size=size) + return self.expr(LowLevelILOperation.LLIL_STORE, addr.index, value.index, size=size, flags=flags) def push(self, size, value): """ @@ -1252,6 +1263,18 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_CALL, dest.index) + def call_stack_adjust(self, dest, stack_adjust): + """ + ``call_stack_adjust`` returns an expression which first pushes the address of the next instruction onto the stack + then jumps (branches) to the expression ``dest``. After the function exits, ``stack_adjust`` is added to the + stack pointer register. + + :param LowLevelILExpr dest: the expression to call + :return: The expression ``call(dest), stack += stack_adjust`` + :rtype: LowLevelILExpr + """ + return self.expr(LowLevelILOperation.LLIL_CALL_STACK_ADJUST, dest.index, stack_adjust) + def ret(self, dest): """ ``ret`` returns an expression which jumps (branches) to the expression ``dest``. ``ret`` is a special alias for @@ -1651,6 +1674,24 @@ class LowLevelILFunction(object): result = function.RegisterValue(self.arch, value) return result + def get_medium_level_il_instruction_index(self, instr): + med_il = self.medium_level_il + if med_il is None: + return None + result = core.BNGetMediumLevelILInstructionIndex(self.handle, instr) + if result >= core.BNGetMediumLevelILInstructionCount(med_il.handle): + return None + return result + + def get_medium_level_il_expr_index(self, expr): + med_il = self.medium_level_il + if med_il is None: + return None + result = core.BNGetMediumLevelILExprIndex(self.handle, expr) + if result >= core.BNGetMediumLevelILExprCount(med_il.handle): + return None + return result + def get_mapped_medium_level_il_instruction_index(self, instr): med_il = self.mapped_medium_level_il if med_il is None: @@ -1688,6 +1729,9 @@ class LowLevelILBasicBlock(basicblock.BasicBlock): else: return self.il_function[self.end + idx] + def _create_instance(self, view, handle): + """Internal method by super to instantiante child instances""" + return LowLevelILBasicBlock(view, handle, self.il_function) def LLIL_TEMP(n): return n | 0x80000000 diff --git a/python/mediumlevelil.py b/python/mediumlevelil.py index 1274bd9b..07759a47 100644 --- a/python/mediumlevelil.py +++ b/python/mediumlevelil.py @@ -26,6 +26,7 @@ from .enums import MediumLevelILOperation, InstructionTextTokenType, ILBranchDep import function import basicblock import lowlevelil +import types class SSAVariable(object): @@ -36,6 +37,15 @@ class SSAVariable(object): def __repr__(self): return "<ssa %s version %d>" % (repr(self.var), self.version) + def __eq__(self, other): + return ( + (self.var.identifier, self.version) == + (other.var.identifier, other.version) + ) + + def __hash__(self): + return hash((self.var.identifier, self.version)) + class MediumLevelILLabel(object): def __init__(self, handle = None): @@ -70,17 +80,20 @@ class MediumLevelILInstruction(object): MediumLevelILOperation.MLIL_SET_VAR_FIELD: [("dest", "var"), ("offset", "int"), ("src", "expr")], MediumLevelILOperation.MLIL_SET_VAR_SPLIT: [("high", "var"), ("low", "var"), ("src", "expr")], MediumLevelILOperation.MLIL_LOAD: [("src", "expr")], + MediumLevelILOperation.MLIL_LOAD_STRUCT: [("src", "expr"), ("offset", "int")], MediumLevelILOperation.MLIL_STORE: [("dest", "expr"), ("src", "expr")], + MediumLevelILOperation.MLIL_STORE_STRUCT: [("dest", "expr"), ("offset", "int"), ("src", "expr")], MediumLevelILOperation.MLIL_VAR: [("src", "var")], MediumLevelILOperation.MLIL_VAR_FIELD: [("src", "var"), ("offset", "int")], MediumLevelILOperation.MLIL_ADDRESS_OF: [("src", "var")], MediumLevelILOperation.MLIL_ADDRESS_OF_FIELD: [("src", "var"), ("offset", "int")], MediumLevelILOperation.MLIL_CONST: [("constant", "int")], MediumLevelILOperation.MLIL_CONST_PTR: [("constant", "int")], + MediumLevelILOperation.MLIL_IMPORT: [("constant", "int")], MediumLevelILOperation.MLIL_ADD: [("left", "expr"), ("right", "expr")], - MediumLevelILOperation.MLIL_ADC: [("left", "expr"), ("right", "expr")], + MediumLevelILOperation.MLIL_ADC: [("left", "expr"), ("right", "expr"), ("carry", "expr")], MediumLevelILOperation.MLIL_SUB: [("left", "expr"), ("right", "expr")], - MediumLevelILOperation.MLIL_SBB: [("left", "expr"), ("right", "expr")], + MediumLevelILOperation.MLIL_SBB: [("left", "expr"), ("right", "expr"), ("carry", "expr")], MediumLevelILOperation.MLIL_AND: [("left", "expr"), ("right", "expr")], MediumLevelILOperation.MLIL_OR: [("left", "expr"), ("right", "expr")], MediumLevelILOperation.MLIL_XOR: [("left", "expr"), ("right", "expr")], @@ -88,9 +101,9 @@ class MediumLevelILInstruction(object): MediumLevelILOperation.MLIL_LSR: [("left", "expr"), ("right", "expr")], MediumLevelILOperation.MLIL_ASR: [("left", "expr"), ("right", "expr")], MediumLevelILOperation.MLIL_ROL: [("left", "expr"), ("right", "expr")], - MediumLevelILOperation.MLIL_RLC: [("left", "expr"), ("right", "expr")], + MediumLevelILOperation.MLIL_RLC: [("left", "expr"), ("right", "expr"), ("carry", "expr")], MediumLevelILOperation.MLIL_ROR: [("left", "expr"), ("right", "expr")], - MediumLevelILOperation.MLIL_RRC: [("left", "expr"), ("right", "expr")], + MediumLevelILOperation.MLIL_RRC: [("left", "expr"), ("right", "expr"), ("carry", "expr")], MediumLevelILOperation.MLIL_MUL: [("left", "expr"), ("right", "expr")], MediumLevelILOperation.MLIL_MULU_DP: [("left", "expr"), ("right", "expr")], MediumLevelILOperation.MLIL_MULS_DP: [("left", "expr"), ("right", "expr")], @@ -139,7 +152,7 @@ class MediumLevelILInstruction(object): MediumLevelILOperation.MLIL_UNIMPL_MEM: [("src", "expr")], MediumLevelILOperation.MLIL_SET_VAR_SSA: [("dest", "var_ssa"), ("src", "expr")], MediumLevelILOperation.MLIL_SET_VAR_SSA_FIELD: [("prev", "var_ssa_dest_and_src"), ("offset", "int"), ("src", "expr")], - MediumLevelILOperation.MLIL_SET_VAR_SPLIT_SSA: [("high", "expr"), ("low", "expr"), ("src", "expr")], + MediumLevelILOperation.MLIL_SET_VAR_SPLIT_SSA: [("high", "var_ssa"), ("low", "var_ssa"), ("src", "expr")], MediumLevelILOperation.MLIL_SET_VAR_ALIASED: [("prev", "var_ssa_dest_and_src"), ("src", "expr")], MediumLevelILOperation.MLIL_SET_VAR_ALIASED_FIELD: [("prev", "var_ssa_dest_and_src"), ("offset", "int"), ("src", "expr")], MediumLevelILOperation.MLIL_VAR_SSA: [("src", "var_ssa")], @@ -153,7 +166,9 @@ class MediumLevelILInstruction(object): MediumLevelILOperation.MLIL_CALL_OUTPUT_SSA: [("dest_memory", "int"), ("dest", "var_ssa_list")], MediumLevelILOperation.MLIL_CALL_PARAM_SSA: [("src_memory", "int"), ("src", "var_ssa_list")], MediumLevelILOperation.MLIL_LOAD_SSA: [("src", "expr"), ("src_memory", "int")], + MediumLevelILOperation.MLIL_LOAD_STRUCT_SSA: [("src", "expr"), ("offset", "int"), ("src_memory", "int")], MediumLevelILOperation.MLIL_STORE_SSA: [("dest", "expr"), ("dest_memory", "int"), ("src_memory", "int"), ("src", "expr")], + MediumLevelILOperation.MLIL_STORE_STRUCT_SSA: [("dest", "expr"), ("offset", "int"), ("dest_memory", "int"), ("src_memory", "int"), ("src", "expr")], MediumLevelILOperation.MLIL_VAR_PHI: [("dest", "var_ssa"), ("src", "var_ssa_list")], MediumLevelILOperation.MLIL_MEM_PHI: [("dest_memory", "int"), ("src_memory", "int_list")] } @@ -169,6 +184,7 @@ class MediumLevelILInstruction(object): self.operation = MediumLevelILOperation(instr.operation) self.size = instr.size self.address = instr.address + self.source_operand = instr.sourceOperand operands = MediumLevelILInstruction.ILOperations[instr.operation] self.operands = [] i = 0 @@ -265,8 +281,9 @@ class MediumLevelILInstruction(object): size = tokens[i].size operand = tokens[i].operand context = tokens[i].context + confidence = tokens[i].confidence address = tokens[i].address - result.append(function.InstructionTextToken(token_type, text, value, size, operand, context, address)) + result.append(function.InstructionTextToken(token_type, text, value, size, operand, context, address, confidence)) core.BNFreeInstructionText(tokens, count.value) return result @@ -396,13 +413,25 @@ class MediumLevelILInstruction(object): result += operand.vars_read return result + @property + def expr_type(self): + """Type of expression""" + result = core.BNGetMediumLevelILExprType(self.function.handle, self.expr_index) + if result.type: + platform = None + if self.function.source_function: + platform = self.function.source_function.platform + return types.Type(result.type, platform = platform, confidence = result.confidence) + return None + def get_ssa_var_possible_values(self, ssa_var): var_data = core.BNVariable() var_data.type = ssa_var.var.source_type var_data.index = ssa_var.var.index var_data.storage = ssa_var.var.storage value = core.BNGetMediumLevelILPossibleSSAVarValues(self.function.handle, var_data, ssa_var.version, self.instr_index) - result = function.RegisterValue(self.function.arch, value) + result = function.PossibleValueSet(self.function.arch, value) + core.BNFreePossibleValueSet(value) return result def get_ssa_var_version(self, var): @@ -783,6 +812,32 @@ class MediumLevelILFunction(object): core.BNFreeILInstructionList(instrs) return result + def get_var_definitions(self, var): + count = ctypes.c_ulonglong() + var_data = core.BNVariable() + var_data.type = var.source_type + var_data.index = var.index + var_data.storage = var.storage + instrs = core.BNGetMediumLevelILVariableDefinitions(self.handle, var_data, count) + result = [] + for i in xrange(0, count.value): + result.append(instrs[i]) + core.BNFreeILInstructionList(instrs) + return result + + def get_var_uses(self, var): + count = ctypes.c_ulonglong() + var_data = core.BNVariable() + var_data.type = var.source_type + var_data.index = var.index + var_data.storage = var.storage + instrs = core.BNGetMediumLevelILVariableUses(self.handle, var_data, count) + result = [] + for i in xrange(0, count.value): + result.append(instrs[i]) + core.BNFreeILInstructionList(instrs) + return result + def get_ssa_var_value(self, ssa_var): var_data = core.BNVariable() var_data.type = ssa_var.var.source_type @@ -834,3 +889,7 @@ class MediumLevelILBasicBlock(basicblock.BasicBlock): return self.il_function[idx + self.start] else: return self.il_function[self.end + idx] + + def _create_instance(self, view, handle): + """Internal method by super to instantiante child instances""" + return MediumLevelILBasicBlock(view, handle, self.il_function) diff --git a/python/metadata.py b/python/metadata.py new file mode 100644 index 00000000..554bbcf4 --- /dev/null +++ b/python/metadata.py @@ -0,0 +1,266 @@ +# Copyright (c) 2015-2017 Vector 35 LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + + +import ctypes + +# Binary Ninja components +import _binaryninjacore as core +from enums import MetadataType + + +class Metadata(object): + def __init__(self, value=None, signed=None, raw=None, handle=None): + if handle is not None: + self.handle = handle + elif isinstance(value, int): + if signed: + self.handle = core.BNCreateMetadataSignedIntegerData(value) + else: + self.handle = core.BNCreateMetadataUnsignedIntegerData(value) + elif isinstance(value, bool): + self.handle = core.BNCreateMetadataBooleanData(value) + elif isinstance(value, str): + if raw: + buffer = (ctypes.c_ubyte * len(value)).from_buffer_copy(value) + self.handle = core.BNCreateMetadataRawData(buffer, len(value)) + else: + self.handle = core.BNCreateMetadataStringData(value) + elif isinstance(value, float): + self.handle = core.BNCreateMetadataDoubleData(value) + elif isinstance(value, list): + self.handle = core.BNCreateMetadataOfType(MetadataType.ArrayDataType) + for elm in value: + md = Metadata(elm, signed, raw) + core.BNMetadataArrayAppend(self.handle, md.handle) + elif isinstance(value, dict): + self.handle = core.BNCreateMetadataOfType(MetadataType.KeyValueDataType) + for elm in value: + md = Metadata(value[elm], signed, raw) + core.BNMetadataSetValueForKey(self.handle, str(elm), md.handle) + else: + raise ValueError("List doesn't not contain type of: int, bool, str, float, list, dict") + + @property + def value(self): + if self.is_integer: + return int(self) + elif self.is_string or self.is_raw: + return str(self) + elif self.is_float: + return float(self) + elif self.is_boolean: + return bool(self) + elif self.is_array: + return list(self) + elif self.is_dict: + return self.get_dict() + raise TypeError() + + def get_dict(self): + if not self.is_dict: + raise TypeError() + result = {} + for key in self: + result[key] = self[key] + return result + + @property + def type(self): + return MetadataType(core.BNMetadataGetType(self.handle)) + + @property + def is_integer(self): + return self.is_signed_integer or self.is_unsigned_integer + + @property + def is_signed_integer(self): + return core.BNMetadataIsSignedInteger(self.handle) + + @property + def is_unsigned_integer(self): + return core.BNMetadataIsUnsignedInteger(self.handle) + + @property + def is_float(self): + return core.BNMetadataIsDouble(self.handle) + + @property + def is_boolean(self): + return core.BNMetadataIsBoolean(self.handle) + + @property + def is_string(self): + return core.BNMetadataIsString(self.handle) + + @property + def is_raw(self): + return core.BNMetadataIsRaw(self.handle) + + @property + def is_array(self): + return core.BNMetadataIsArray(self.handle) + + @property + def is_dict(self): + return core.BNMetadataIsKeyValueStore(self.handle) + + def remove(self, key_or_index): + if isinstance(key_or_index, str) and self.is_dict: + core.BNMetadataRemoveKey(self.handle, key_or_index) + elif isinstance(key_or_index, int) and self.is_array: + core.BNMetadataRemoveIndex(self.handle, key_or_index) + else: + raise TypeError("remove only valid for dict and array objects") + + def __len__(self): + if self.is_array or self.is_dict or self.is_string or self.is_raw: + return core.BNMetadataSize(self.handle) + raise Exception("Metadata object doesn't support len()") + + def __iter__(self): + if self.is_array: + for i in xrange(core.BNMetadataSize(self.handle)): + yield Metadata(handle=core.BNMetadataGetForIndex(self.handle, i)).value + elif self.is_dict: + result = core.BNMetadataGetValueStore(self.handle) + try: + for i in xrange(result.contents.size): + yield result.contents.keys[i] + finally: + core.BNFreeMetadataValueStore(result) + else: + raise Exception("Metadata object doesn't support iteration") + + def __getitem__(self, value): + if self.is_array: + if not isinstance(value, int): + raise ValueError("Metadata object only supports integers for indexing") + if value >= len(self): + raise IndexError("Index value out of range") + return Metadata(handle=core.BNMetadataGetForIndex(self.handle, value)).value + if self.is_dict: + if not isinstance(value, str): + raise ValueError("Metadata object only supports strings for indexing") + handle = core.BNMetadataGetForKey(self.handle, value) + if handle is None: + raise KeyError(value) + return Metadata(handle=handle).value + + raise NotImplementedError("Metadata object doesn't support indexing") + + def __str__(self): + if self.is_string: + return core.BNMetadataGetString(self.handle) + if self.is_raw: + length = ctypes.c_ulonglong() + length.value = 0 + native_list = core.BNMetadataGetRaw(self.handle, ctypes.byref(length)) + out_list = [] + for i in xrange(length.value): + out_list.append(native_list[i]) + core.BNFreeMetadataRaw(native_list) + return ''.join(chr(a) for a in out_list) + + raise ValueError("Metadata object not a string or raw type") + + def __int__(self): + if self.is_signed_integer: + return core.BNMetadataGetSignedInteger(self.handle) + if self.is_unsigned_integer: + return core.BNMetadataGetUnsignedInteger(self.handle) + + raise ValueError("Metadata object not of integer type") + + def __float__(self): + if not self.is_float: + raise ValueError("Metadata object is not float type") + return core.BNMetadataGetDouble(self.handle) + + def __nonzero__(self): + if not self.is_boolean: + raise ValueError("Metadata object is not boolean type") + return core.BNMetadataGetBoolean(self.handle) + + def __eq__(self, other): + if isinstance(other, int) and self.is_integer: + return int(self) == other + elif isinstance(other, str) and (self.is_string or self.is_raw): + return str(self) == other + elif isinstance(other, float) and self.is_float: + return float(self) == other + elif isinstance(other, bool) and self.is_boolean: + return bool(self) == other + elif self.is_array and ((isinstance(other, Metadata) and other.is_array) or isinstance(other, list)): + if len(self) != len(other): + return False + for a, b in zip(self, other): + if a != b: + return False + return True + elif self.is_dict and ((isinstance(other, Metadata) and other.is_dict) or isinstance(other, dict)): + if len(self) != len(other): + return False + for a, b in zip(self, other): + if a != b or self[a] != other[b]: + return False + return True + elif isinstance(other, Metadata) and self.is_integer and other.is_integer: + return int(self) == int(other) + elif isinstance(other, Metadata) and (self.is_string or self.is_raw) and (other.is_string or other.is_raw): + return str(self) == str(other) + elif isinstance(other, Metadata) and self.is_float and other.is_float: + return float(self) == float(other) + elif isinstance(other, Metadata) and self.is_boolean and other.is_boolean: + return bool(self) == bool(other) + raise NotImplementedError() + + def __ne__(self, other): + if isinstance(other, int) and self.is_integer: + return int(self) != other + elif isinstance(other, str) and (self.is_string or self.is_raw): + return str(self) != other + elif isinstance(other, float) and self.is_float: + return float(self) != other + elif isinstance(other, bool): + return bool(self) != other + elif self.is_array and ((isinstance(other, Metadata) and other.is_array) or isinstance(other, list)): + if len(self) != len(other): + return True + areEqual = True + for a, b in zip(self, other): + if a != b: + areEqual = False + return not areEqual + elif self.is_dict and ((isinstance(other, Metadata) and other.is_dict) or isinstance(other, dict)): + if len(self) != len(other): + return True + for a, b in zip(self, other): + if a != b or self[a] != other[b]: + return True + return False + elif isinstance(other, Metadata) and self.is_integer and other.is_integer: + return int(self) != int(other) + elif isinstance(other, Metadata) and (self.is_string or self.is_raw) and (other.is_string or other.is_raw): + return str(self) != str(other) + elif isinstance(other, Metadata) and self.is_float and other.is_float: + return float(self) != float(other) + elif isinstance(other, Metadata) and self.is_boolean and other.is_boolean: + return bool(self) != bool(other) diff --git a/python/platform.py b/python/platform.py index 9ba7625f..5e63d836 100644 --- a/python/platform.py +++ b/python/platform.py @@ -132,7 +132,7 @@ class Platform(object): result = core.BNGetPlatformDefaultCallingConvention(self.handle) if result is None: return None - return callingconvention.CallingConvention(None, result) + return callingconvention.CallingConvention(handle=result) @default_calling_convention.setter def default_calling_convention(self, value): @@ -150,7 +150,7 @@ class Platform(object): result = core.BNGetPlatformCdeclCallingConvention(self.handle) if result is None: return None - return callingconvention.CallingConvention(None, result) + return callingconvention.CallingConvention(handle=result) @cdecl_calling_convention.setter def cdecl_calling_convention(self, value): @@ -168,7 +168,7 @@ class Platform(object): result = core.BNGetPlatformStdcallCallingConvention(self.handle) if result is None: return None - return callingconvention.CallingConvention(None, result) + return callingconvention.CallingConvention(handle=result) @stdcall_calling_convention.setter def stdcall_calling_convention(self, value): @@ -186,7 +186,7 @@ class Platform(object): result = core.BNGetPlatformFastcallCallingConvention(self.handle) if result is None: return None - return callingconvention.CallingConvention(None, result) + return callingconvention.CallingConvention(handle=result) @fastcall_calling_convention.setter def fastcall_calling_convention(self, value): @@ -204,7 +204,7 @@ class Platform(object): result = core.BNGetPlatformSystemCallConvention(self.handle) if result is None: return None - return callingconvention.CallingConvention(None, result) + return callingconvention.CallingConvention(handle=result) @system_call_convention.setter def system_call_convention(self, value): @@ -222,7 +222,7 @@ class Platform(object): cc = core.BNGetPlatformCallingConventions(self.handle, count) result = [] for i in xrange(0, count.value): - result.append(callingconvention.CallingConvention(None, core.BNNewCallingConventionReference(cc[i]))) + result.append(callingconvention.CallingConvention(handle=core.BNNewCallingConventionReference(cc[i]))) core.BNFreeCallingConventionList(cc, count.value) return result @@ -234,7 +234,7 @@ class Platform(object): result = {} for i in xrange(0, count.value): name = types.QualifiedName._from_core_struct(type_list[i].name) - result[name] = types.Type(core.BNNewTypeReference(type_list[i].type)) + result[name] = types.Type(core.BNNewTypeReference(type_list[i].type), platform = self) core.BNFreeTypeList(type_list, count.value) return result @@ -246,7 +246,7 @@ class Platform(object): result = {} for i in xrange(0, count.value): name = types.QualifiedName._from_core_struct(type_list[i].name) - result[name] = types.Type(core.BNNewTypeReference(type_list[i].type)) + result[name] = types.Type(core.BNNewTypeReference(type_list[i].type), platform = self) core.BNFreeTypeList(type_list, count.value) return result @@ -258,7 +258,7 @@ class Platform(object): result = {} for i in xrange(0, count.value): name = types.QualifiedName._from_core_struct(type_list[i].name) - result[name] = types.Type(core.BNNewTypeReference(type_list[i].type)) + result[name] = types.Type(core.BNNewTypeReference(type_list[i].type), platform = self) core.BNFreeTypeList(type_list, count.value) return result @@ -270,7 +270,7 @@ class Platform(object): result = {} for i in xrange(0, count.value): name = types.QualifiedName._from_core_struct(call_list[i].name) - t = types.Type(core.BNNewTypeReference(call_list[i].type)) + 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 @@ -325,21 +325,21 @@ class Platform(object): obj = core.BNGetPlatformTypeByName(self.handle, name) if not obj: return None - return types.Type(obj) + return types.Type(obj, platform = self) def get_variable_by_name(self, name): name = types.QualifiedName(name)._get_core_struct() obj = core.BNGetPlatformVariableByName(self.handle, name) if not obj: return None - return types.Type(obj) + return types.Type(obj, platform = self) def get_function_by_name(self, name): name = types.QualifiedName(name)._get_core_struct() obj = core.BNGetPlatformFunctionByName(self.handle, name) if not obj: return None - return types.Type(obj) + return types.Type(obj, platform = self) def get_system_call_name(self, number): return core.BNGetPlatformSystemCallName(self.handle, number) @@ -348,7 +348,7 @@ class Platform(object): obj = core.BNGetPlatformSystemCallType(self.handle, number) if not obj: return None - return types.Type(obj) + return types.Type(obj, platform = self) def generate_auto_platform_type_id(self, name): name = types.QualifiedName(name)._get_core_struct() @@ -360,3 +360,96 @@ class Platform(object): def get_auto_platform_type_id_source(self): return core.BNGetAutoPlatformTypeIdSource(self.handle) + + def parse_types_from_source(self, source, filename=None, include_dirs=[], auto_type_source=None): + """ + ``parse_types_from_source`` parses the source string and any needed headers searching for them in + the optional list of directories provided in ``include_dirs``. + + :param str source: source string to be parsed + :param str filename: optional source filename + :param list(str) include_dirs: optional list of string filename include directories + :param str auto_type_source: optional source of types if used for automatically generated types + :return: :py:class:`TypeParserResult` (a SyntaxError is thrown on parse error) + :rtype: TypeParserResult + :Example: + + >>> platform.parse_types_from_source('int foo;\\nint bar(int x);\\nstruct bas{int x,y;};\\n') + ({types: {'bas': <type: struct bas>}, variables: {'foo': <type: int32_t>}, functions:{'bar': + <type: int32_t(int32_t x)>}}, '') + >>> + """ + + if filename is None: + filename = "input" + dir_buf = (ctypes.c_char_p * len(include_dirs))() + for i in xrange(0, len(include_dirs)): + dir_buf[i] = str(include_dirs[i]) + parse = core.BNTypeParserResult() + errors = ctypes.c_char_p() + result = core.BNParseTypesFromSource(self.handle, source, filename, parse, errors, dir_buf, + len(include_dirs), auto_type_source) + error_str = errors.value + core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) + if not result: + raise SyntaxError(error_str) + type_dict = {} + variables = {} + functions = {} + for i in xrange(0, parse.typeCount): + name = types.QualifiedName._from_core_struct(parse.types[i].name) + type_dict[name] = types.Type(core.BNNewTypeReference(parse.types[i].type), platform = self) + for i in xrange(0, parse.variableCount): + name = types.QualifiedName._from_core_struct(parse.variables[i].name) + variables[name] = types.Type(core.BNNewTypeReference(parse.variables[i].type), platform = self) + for i in xrange(0, parse.functionCount): + name = types.QualifiedName._from_core_struct(parse.functions[i].name) + functions[name] = types.Type(core.BNNewTypeReference(parse.functions[i].type), platform = self) + core.BNFreeTypeParserResult(parse) + return types.TypeParserResult(type_dict, variables, functions) + + def parse_types_from_source_file(self, filename, include_dirs=[], auto_type_source=None): + """ + ``parse_types_from_source_file`` parses the source file ``filename`` and any needed headers searching for them in + the optional list of directories provided in ``include_dirs``. + + :param str filename: filename of file to be parsed + :param list(str) include_dirs: optional list of string filename include directories + :param str auto_type_source: optional source of types if used for automatically generated types + :return: :py:class:`TypeParserResult` (a SyntaxError is thrown on parse error) + :rtype: TypeParserResult + :Example: + + >>> file = "/Users/binja/tmp.c" + >>> open(file).read() + 'int foo;\\nint bar(int x);\\nstruct bas{int x,y;};\\n' + >>> platform.parse_types_from_source_file(file) + ({types: {'bas': <type: struct bas>}, variables: {'foo': <type: int32_t>}, functions: + {'bar': <type: int32_t(int32_t x)>}}, '') + >>> + """ + dir_buf = (ctypes.c_char_p * len(include_dirs))() + for i in xrange(0, len(include_dirs)): + dir_buf[i] = str(include_dirs[i]) + parse = core.BNTypeParserResult() + errors = ctypes.c_char_p() + result = core.BNParseTypesFromSourceFile(self.handle, filename, parse, errors, dir_buf, + len(include_dirs), auto_type_source) + error_str = errors.value + core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) + if not result: + raise SyntaxError(error_str) + type_dict = {} + variables = {} + functions = {} + for i in xrange(0, parse.typeCount): + name = types.QualifiedName._from_core_struct(parse.types[i].name) + type_dict[name] = types.Type(core.BNNewTypeReference(parse.types[i].type), platform = self) + for i in xrange(0, parse.variableCount): + name = types.QualifiedName._from_core_struct(parse.variables[i].name) + variables[name] = types.Type(core.BNNewTypeReference(parse.variables[i].type), platform = self) + for i in xrange(0, parse.functionCount): + name = types.QualifiedName._from_core_struct(parse.functions[i].name) + functions[name] = types.Type(core.BNNewTypeReference(parse.functions[i].type), platform = self) + core.BNFreeTypeParserResult(parse) + return types.TypeParserResult(type_dict, variables, functions) diff --git a/python/pluginmanager.py b/python/pluginmanager.py index c0f70260..6896d699 100644 --- a/python/pluginmanager.py +++ b/python/pluginmanager.py @@ -144,6 +144,12 @@ class Repository(object): def __repr__(self): return "<{} - {}/{}>".format(self.path, self.remote_reference, self.local_reference) + def __getitem__(self, plugin_path): + for plugin in self.plugins: + if plugin_path == plugin.path: + return plugin + raise KeyError() + @property def url(self): """String url of the git repository where the plugin repository's are stored""" @@ -155,6 +161,11 @@ class Repository(object): return core.BNRepositoryGetRepoPath(self.handle) @property + def full_path(self): + """String full path the repository""" + return core.BNRepositoryGetPluginsPath(self.handle) + + @property def local_reference(self): """String for the local git reference (ie 'master')""" return core.BNRepositoryGetLocalReference(self.handle) @@ -190,6 +201,12 @@ class RepositoryManager(object): def __init__(self, handle=None): self.handle = core.BNGetRepositoryManager() + def __getitem__(self, repo_path): + for repo in self.repositories: + if repo_path == repo.path: + return repo + raise KeyError() + def check_for_updates(self): """Check for updates for all managed Repository objects""" return core.BNRepositoryManagerCheckForUpdates(self.handle) diff --git a/python/scriptingprovider.py b/python/scriptingprovider.py index 94616d59..ed9688b9 100644 --- a/python/scriptingprovider.py +++ b/python/scriptingprovider.py @@ -335,7 +335,6 @@ class _PythonScriptingInstanceOutput(object): self.buffer = "" self.encoding = 'UTF-8' self.errors = None - self.isatty = False self.mode = 'w' self.name = 'PythonScriptingInstanceOutput' self.newlines = None @@ -349,6 +348,9 @@ class _PythonScriptingInstanceOutput(object): def flush(self): pass + def isatty(self): + return False + def next(self): raise IOError("File not open for reading") @@ -412,6 +414,9 @@ class _PythonScriptingInstanceInput(object): def __init__(self, orig): self.orig = orig + def isatty(self): + return False + def read(self, size): interpreter = None if "value" in dir(PythonScriptingInstance._interpreter): diff --git a/python/setting.py b/python/setting.py new file mode 100644 index 00000000..d58c8955 --- /dev/null +++ b/python/setting.py @@ -0,0 +1,141 @@ +# Copyright (c) 2015-2017 Vector 35 LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + +import ctypes + +# Binary Ninja components +import _binaryninjacore as core + + +class Setting(object): + def __init__(self, plugin_name="core"): + self.plugin_name = plugin_name + + def get_bool(self, name, default_value=False): + return core.BNSettingGetBool(self.plugin_name, name, default_value) + + def get_integer(self, name, default_value=0): + return core.BNSettingGetInteger(self.plugin_name, name, default_value) + + def get_string(self, name, default_value=""): + return core.BNSettingGetString(self.plugin_name, name, default_value) + + def get_integer_list(self, name, default_value=[]): + length = ctypes.c_ulonglong() + length.value = len(default_value) + default_list = (ctypes.c_longlong * len(default_value))() + for i in range(len(default_value)): + default_list[i] = default_value[i] + result = core.BNSettingGetIntegerList(self.plugin_name, name, default_list, ctypes.byref(length)) + out_list = [] + for i in xrange(length.value): + out_list.append(result[i]) + core.BNFreeSettingIntegerList(result) + return out_list + + def get_string_list(self, name, default_value=[]): + length = ctypes.c_ulonglong() + length.value = len(default_value) + default_list = (ctypes.c_char_p * len(default_value))() + for i in range(len(default_value)): + default_list[i] = default_value[i] + result = core.BNSettingGetStringList(self.plugin_name, name, default_list, ctypes.byref(length)) + out_list = [] + for i in xrange(length.value): + out_list.append(result[i]) + core.BNFreeStringList(result, length) + return out_list + + def get_double(self, name, default_value=0.0): + return core.BNSettingGetDouble(self.plugin_name, name, default_value) + + def is_bool(self, name): + return core.BNSettingIsBool(self.plugin_name, name) + + def is_integer(self, name): + return core.BNSettingIsInteger(self.plugin_name, name) + + def is_string(self, name): + return core.BNSettingIsString(self.plugin_name, name) + + def is_string_list(self, name): + return core.BNSettingIsStringList(self.plugin_name, name) + + def is_integer_list(self, name): + return core.BNSettingIsIntegerList(self.plugin_name, name) + + def is_double(self, name): + return core.BNSettingIsDouble(self.plugin_name, name) + + def is_present(self, name): + return core.BNSettingIsPresent(self.plugin_name, name) + + def set_bool(self, name, value, auto_flush=True): + return core.BNSettingSetBool(self.plugin_name, name, value, auto_flush) + + def set_integer(self, name, value, auto_flush=True): + return core.BNSettingSetInteger(self.plugin_name, name, value, auto_flush) + + def set_string(self, name, value, auto_flush=True): + return core.BNSettingSetString(self.plugin_name, name, value, auto_flush) + + def set_integer_list(self, name, value, auto_flush=True): + length = ctypes.c_ulonglong() + length.value = len(value) + default_list = (ctypes.c_longlong * len(value))() + for i in xrange(len(value)): + default_list[i] = value[i] + + return core.BNSettingSetIntegerList(self.plugin_name, name, default_list, length, auto_flush) + + def set_string_list(self, name, value, auto_flush=True): + length = ctypes.c_ulonglong() + length.value = len(value) + default_list = (ctypes.c_char_p * len(value))() + for i in xrange(len(value)): + default_list[i] = str(value[i]) + + return core.BNSettingSetStringList(self.plugin_name, name, default_list, length, auto_flush) + + def set_double(self, name, value, auto_flush=True): + return core.BNSettingSetDouble(self.plugin_name, name, value, auto_flush) + + def set(self, name, value, auto_flush=True): + if isinstance(value, bool): + return self.set_bool(name, value, auto_flush) + elif isinstance(value, int): + return self.set_integer(name, value, auto_flush) + elif isinstance(value, str): + return self.set_string(name, value, auto_flush) + elif isinstance(value, list) and len(value) == 0: + return self.set_integer_list(name, value, auto_flush) + elif isinstance(value, list) and len(value) > 0 and isinstance(value[0], int): + return self.set_integer_list(name, value, auto_flush) + elif isinstance(value, list) and len(value) > 0 and isinstance(value[0], str): + return self.set_string_list(name, value, auto_flush) + elif isinstance(value, float): + return self.set_double(name, value, auto_flush) + raise ValueError("value is not one of (int, bool, float, str, [int], [str]) types") + + def remove_setting_group(self, auto_flush=True): + 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 diff --git a/python/types.py b/python/types.py index f8e416f4..2557db2c 100644 --- a/python/types.py +++ b/python/types.py @@ -18,11 +18,13 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. +max_confidence = 255 + import ctypes # Binary Ninja components import _binaryninjacore as core -from enums import SymbolType, TypeClass, NamedTypeReferenceClass, InstructionTextTokenType, StructureType +from enums import SymbolType, TypeClass, NamedTypeReferenceClass, InstructionTextTokenType, StructureType, ReferenceType, VariableSourceType import callingconvention import function @@ -197,9 +199,23 @@ class Symbol(object): raise AttributeError("attribute '%s' is read only" % name) +class FunctionParameter(object): + def __init__(self, param_type, name = "", location = None): + self.type = param_type + self.name = name + self.location = location + + def __repr__(self): + if (self.location is not None) and (self.location.name != self.name): + return "%s %s%s @ %s" % (self.type.get_string_before_name(), self.name, self.type.get_string_after_name(), self.location.name) + return "%s %s%s" % (self.type.get_string_before_name(), self.name, self.type.get_string_after_name()) + + class Type(object): - def __init__(self, handle): + def __init__(self, handle, platform = None, confidence = max_confidence): self.handle = handle + self.confidence = confidence + self.platform = platform def __del__(self): core.BNFreeType(self.handle) @@ -232,12 +248,14 @@ class Type(object): @property def signed(self): """Wether type is signed (read-only)""" - return core.BNIsTypeSigned(self.handle) + result = core.BNIsTypeSigned(self.handle) + return BoolWithConfidence(result.value, confidence = result.confidence) @property def const(self): """Whether type is const (read-only)""" - return core.BNIsTypeConst(self.handle) + result = core.BNIsTypeConst(self.handle) + return BoolWithConfidence(result.value, confidence = result.confidence) @property def modified(self): @@ -248,33 +266,33 @@ class Type(object): def target(self): """Target (read-only)""" result = core.BNGetChildType(self.handle) - if result is None: + if not result.type: return None - return Type(result) + return Type(result.type, platform = self.platform, confidence = result.confidence) @property def element_type(self): """Target (read-only)""" result = core.BNGetChildType(self.handle) - if result is None: + if not result.type: return None - return Type(result) + return Type(result.type, platform = self.platform, confidence = result.confidence) @property def return_value(self): """Return value (read-only)""" result = core.BNGetChildType(self.handle) - if result is None: + if not result.type: return None - return Type(result) + return Type(result.type, platform = self.platform, confidence = result.confidence) @property def calling_convention(self): """Calling convention (read-only)""" result = core.BNGetTypeCallingConvention(self.handle) - if result is None: + if not result.convention: return None - return callingconvention.CallingConvention(None, result) + return callingconvention.CallingConvention(None, handle = result.convention, confidence = result.confidence) @property def parameters(self): @@ -283,19 +301,32 @@ class Type(object): params = core.BNGetTypeParameters(self.handle, count) result = [] for i in xrange(0, count.value): - result.append((Type(core.BNNewTypeReference(params[i].type)), params[i].name)) + param_type = Type(core.BNNewTypeReference(params[i].type), platform = self.platform, confidence = params[i].typeConfidence) + if params[i].defaultLocation: + param_location = None + else: + name = params[i].name + if (params[i].location.type == VariableSourceType.RegisterVariableSourceType) and (self.platform is not None): + name = self.platform.arch.get_reg_name(params[i].location.storage) + elif params[i].location.type == VariableSourceType.StackVariableSourceType: + name = "arg_%x" % params[i].location.storage + param_location = function.Variable(None, params[i].location.type, params[i].location.index, + params[i].location.storage, name, param_type) + result.append(FunctionParameter(param_type, params[i].name, param_location)) core.BNFreeTypeParameterList(params, count.value) return result @property def has_variable_arguments(self): """Whether type has variable arguments (read-only)""" - return core.BNTypeHasVariableArguments(self.handle) + result = core.BNTypeHasVariableArguments(self.handle) + return BoolWithConfidence(result.value, confidence = result.confidence) @property def can_return(self): """Whether type can return (read-only)""" - return core.BNFunctionTypeCanReturn(self.handle) + result = core.BNFunctionTypeCanReturn(self.handle) + return BoolWithConfidence(result.value, confidence = result.confidence) @property def structure(self): @@ -326,23 +357,51 @@ class Type(object): """Type count (read-only)""" return core.BNGetTypeElementCount(self.handle) + @property + def offset(self): + """Offset into structure (read-only)""" + return core.BNGetTypeOffset(self.handle) + + @property + def stack_adjustment(self): + """Stack adjustment for function (read-only)""" + result = core.BNGetTypeStackAdjustment(self.handle) + return SizeWithConfidence(result.value, confidence = result.confidence) + def __str__(self): - return core.BNGetTypeString(self.handle) + platform = None + if self.platform is not None: + platform = self.platform.handle + return core.BNGetTypeString(self.handle, platform) def __repr__(self): + if self.confidence < max_confidence: + return "<type: %s, %d%% confidence>" % (str(self), (self.confidence * 100) / max_confidence) return "<type: %s>" % str(self) def get_string_before_name(self): - return core.BNGetTypeStringBeforeName(self.handle) + platform = None + if self.platform is not None: + platform = self.platform.handle + return core.BNGetTypeStringBeforeName(self.handle, platform) def get_string_after_name(self): - return core.BNGetTypeStringAfterName(self.handle) + platform = None + if self.platform is not None: + platform = self.platform.handle + return core.BNGetTypeStringAfterName(self.handle, platform) @property def tokens(self): """Type string as a list of tokens (read-only)""" + return self.get_tokens() + + def get_tokens(self, base_confidence = max_confidence): count = ctypes.c_ulonglong() - tokens = core.BNGetTypeTokens(self.handle, count) + platform = None + if self.platform is not None: + platform = self.platform.handle + tokens = core.BNGetTypeTokens(self.handle, platform, base_confidence, count) result = [] for i in xrange(0, count.value): token_type = InstructionTextTokenType(tokens[i].type) @@ -351,14 +410,18 @@ class Type(object): size = tokens[i].size operand = tokens[i].operand context = tokens[i].context + confidence = tokens[i].confidence address = tokens[i].address - result.append(function.InstructionTextToken(token_type, text, value, size, operand, context, address)) + result.append(function.InstructionTextToken(token_type, text, value, size, operand, context, address, confidence)) core.BNFreeTokenList(tokens, count.value) return result - def get_tokens_before_name(self): + def get_tokens_before_name(self, base_confidence = max_confidence): count = ctypes.c_ulonglong() - tokens = core.BNGetTypeTokensBeforeName(self.handle, count) + platform = None + if self.platform is not None: + platform = self.platform.handle + tokens = core.BNGetTypeTokensBeforeName(self.handle, platform, base_confidence, count) result = [] for i in xrange(0, count.value): token_type = InstructionTextTokenType(tokens[i].type) @@ -367,14 +430,18 @@ class Type(object): size = tokens[i].size operand = tokens[i].operand context = tokens[i].context + confidence = tokens[i].confidence address = tokens[i].address - result.append(function.InstructionTextToken(token_type, text, value, size, operand, context, address)) + result.append(function.InstructionTextToken(token_type, text, value, size, operand, context, address, confidence)) core.BNFreeTokenList(tokens, count.value) return result - def get_tokens_after_name(self): + def get_tokens_after_name(self, base_confidence = max_confidence): count = ctypes.c_ulonglong() - tokens = core.BNGetTypeTokensAfterName(self.handle, count) + platform = None + if self.platform is not None: + platform = self.platform.handle + tokens = core.BNGetTypeTokensAfterName(self.handle, platform, base_confidence, count) result = [] for i in xrange(0, count.value): token_type = InstructionTextTokenType(tokens[i].type) @@ -383,8 +450,9 @@ class Type(object): size = tokens[i].size operand = tokens[i].operand context = tokens[i].context + confidence = tokens[i].confidence address = tokens[i].address - result.append(function.InstructionTextToken(token_type, text, value, size, operand, context, address)) + result.append(function.InstructionTextToken(token_type, text, value, size, operand, context, address, confidence)) core.BNFreeTokenList(tokens, count.value) return result @@ -397,14 +465,23 @@ class Type(object): return Type(core.BNCreateBoolType()) @classmethod - def int(self, width, sign = True, altname=""): + def int(self, width, sign = None, altname=""): """ ``int`` class method for creating an int Type. :param int width: width of the integer in bytes :param bool sign: optional variable representing signedness """ - return Type(core.BNCreateIntegerType(width, sign, altname)) + if sign is None: + sign = BoolWithConfidence(True, confidence = 0) + elif not isinstance(sign, BoolWithConfidence): + sign = BoolWithConfidence(sign) + + sign_conf = core.BNBoolWithConfidence() + sign_conf.value = sign.value + sign_conf.confidence = sign.confidence + + return Type(core.BNCreateIntegerType(width, sign_conf, altname)) @classmethod def float(self, width): @@ -444,15 +521,43 @@ class Type(object): return Type(core.BNCreateEnumerationType(e.handle, width)) @classmethod - def pointer(self, arch, t, const=False): - return Type(core.BNCreatePointerType(arch.handle, t.handle, const)) + def pointer(self, arch, t, const=None, volatile=None, ref_type=None): + if const is None: + const = BoolWithConfidence(False, confidence = 0) + elif not isinstance(const, BoolWithConfidence): + const = BoolWithConfidence(const) + + if volatile is None: + volatile = BoolWithConfidence(False, confidence = 0) + elif not isinstance(volatile, BoolWithConfidence): + volatile = BoolWithConfidence(volatile) + + if ref_type is None: + ref_type = ReferenceType.PointerReferenceType + + type_conf = core.BNTypeWithConfidence() + type_conf.type = t.handle + type_conf.confidence = t.confidence + + const_conf = core.BNBoolWithConfidence() + const_conf.value = const.value + const_conf.confidence = const.confidence + + volatile_conf = core.BNBoolWithConfidence() + volatile_conf.value = volatile.value + volatile_conf.confidence = volatile.confidence + + return Type(core.BNCreatePointerType(arch.handle, type_conf, const_conf, volatile_conf, ref_type)) @classmethod def array(self, t, count): - return Type(core.BNCreateArrayType(t.handle, count)) + type_conf = core.BNTypeWithConfidence() + type_conf.type = t.handle + type_conf.confidence = t.confidence + return Type(core.BNCreateArrayType(type_conf, count)) @classmethod - def function(self, ret, params, calling_convention=None, variable_arguments=False): + def function(self, ret, params, calling_convention=None, variable_arguments=None, stack_adjust=None): """ ``function`` class method for creating an function Type. @@ -461,18 +566,62 @@ class Type(object): :param CallingConvention calling_convention: optional argument for function calling convention :param bool variable_arguments: optional argument for functions that have a variable number of arguments """ - param_buf = (core.BNNameAndType * len(params))() + param_buf = (core.BNFunctionParameter * len(params))() for i in xrange(0, len(params)): if isinstance(params[i], Type): param_buf[i].name = "" param_buf[i].type = params[i].handle + param_buf[i].typeConfidence = params[i].confidence + param_buf[i].defaultLocation = True + elif isinstance(params[i], FunctionParameter): + param_buf[i].name = params[i].name + param_buf[i].type = params[i].type.handle + param_buf[i].typeConfidence = params[i].type.confidence + if params[i].location is None: + param_buf[i].defaultLocation = True + else: + param_buf[i].defaultLocation = False + param_buf[i].location.type = params[i].location.type + param_buf[i].location.index = params[i].location.index + param_buf[i].location.storage = params[i].location.storage else: param_buf[i].name = params[i][1] - param_buf[i].type = params[i][0] - if calling_convention is not None: - calling_convention = calling_convention.handle - return Type(core.BNCreateFunctionType(ret.handle, calling_convention, param_buf, len(params), - variable_arguments)) + param_buf[i].type = params[i][0].handle + param_buf[i].typeConfidence = params[i][0].confidence + param_buf[i].defaultLocation = True + + ret_conf = core.BNTypeWithConfidence() + ret_conf.type = ret.handle + ret_conf.confidence = ret.confidence + + conv_conf = core.BNCallingConventionWithConfidence() + if calling_convention is None: + conv_conf.convention = None + conv_conf.confidence = 0 + else: + conv_conf.convention = calling_convention.handle + conv_conf.confidence = calling_convention.confidence + + if variable_arguments is None: + variable_arguments = BoolWithConfidence(False, confidence = 0) + elif not isinstance(variable_arguments, BoolWithConfidence): + variable_arguments = BoolWithConfidence(variable_arguments) + + vararg_conf = core.BNBoolWithConfidence() + vararg_conf.value = variable_arguments.value + vararg_conf.confidence = variable_arguments.confidence + + if stack_adjust is None: + stack_adjust = SizeWithConfidence(0, confidence = 0) + elif not isinstance(stack_adjust, SizeWithConfidence): + stack_adjust = SizeWithConfidence(stack_adjust) + + stack_adjust_conf = core.BNSizeWithConfidence() + stack_adjust_conf.value = stack_adjust.value + stack_adjust_conf.confidence = stack_adjust.confidence + + return Type(core.BNCreateFunctionType(ret_conf, conv_conf, param_buf, len(params), + vararg_conf, stack_adjust_conf)) @classmethod def generate_auto_type_id(self, source, name): @@ -488,6 +637,9 @@ class Type(object): def get_auto_demanged_type_id_source(self): return core.BNGetAutoDemangledTypeIdSource() + def with_confidence(self, confidence): + return Type(handle = core.BNNewTypeReference(self.handle), platform = self.platform, confidence = confidence) + def __setattr__(self, name, value): try: object.__setattr__(self, name, value) @@ -495,6 +647,73 @@ class Type(object): raise AttributeError("attribute '%s' is read only" % name) +class BoolWithConfidence(object): + def __init__(self, value, confidence = max_confidence): + self.value = value + self.confidence = confidence + + def __str__(self): + return str(self.value) + + def __repr__(self): + return repr(self.value) + + def __bool__(self): + return self.value + + def __nonzero__(self): + return self.value + + +class SizeWithConfidence(object): + def __init__(self, value, confidence = max_confidence): + self.value = value + self.confidence = confidence + + def __str__(self): + return str(self.value) + + def __repr__(self): + return repr(self.value) + + def __int__(self): + return self.value + + +class RegisterSet(object): + def __init__(self, reg_list, confidence = max_confidence): + self.regs = reg_list + self.confidence = confidence + + def __repr__(self): + return repr(self.regs) + + def __iter__(self): + for reg in self.regs: + yield reg + + def __getitem__(self, idx): + return self.regs[idx] + + def __len__(self): + return len(self.regs) + + def with_confidence(self, confidence): + return RegisterSet(list(self.regs), confidence = confidence) + + +class ReferenceTypeWithConfidence(object): + def __init__(self, value, confidence = max_confidence): + self.value = value + self.confidence = confidence + + def __str__(self): + return str(self.value) + + def __repr__(self): + return repr(self.value) + + class NamedTypeReference(object): def __init__(self, type_class = NamedTypeReferenceClass.UnknownNamedTypeClass, type_id = None, name = None, handle = None): if handle is None: @@ -611,7 +830,7 @@ class Structure(object): members = core.BNGetStructureMembers(self.handle, count) result = [] for i in xrange(0, count.value): - result.append(StructureMember(Type(core.BNNewTypeReference(members[i].type)), + result.append(StructureMember(Type(core.BNNewTypeReference(members[i].type), confidence = members[i].typeConfidence), members[i].name, members[i].offset)) core.BNFreeStructureMemberList(members, count.value) return result @@ -664,16 +883,25 @@ class Structure(object): return "<struct: size %#x>" % self.width def append(self, t, name = ""): - core.BNAddStructureMember(self.handle, t.handle, name) + tc = core.BNTypeWithConfidence() + tc.type = t.handle + tc.confidence = t.confidence + core.BNAddStructureMember(self.handle, tc, name) def insert(self, offset, t, name = ""): - core.BNAddStructureMemberAtOffset(self.handle, t.handle, name, offset) + tc = core.BNTypeWithConfidence() + tc.type = t.handle + tc.confidence = t.confidence + core.BNAddStructureMemberAtOffset(self.handle, tc, name, offset) def remove(self, i): core.BNRemoveStructureMember(self.handle, i) def replace(self, i, t, name = ""): - core.BNReplaceStructureMember(self.handle, i, t.handle, name) + tc = core.BNTypeWithConfidence() + tc.type = t.handle + tc.confidence = t.confidence + core.BNReplaceStructureMember(self.handle, i, tc, name) class EnumerationMember(object): @@ -746,7 +974,7 @@ class TypeParserResult(object): self.functions = functions def __repr__(self): - return "{types: %s, variables: %s, functions: %s}" % (self.types, self.variables, self.functions) + return "<types: %s, variables: %s, functions: %s>" % (self.types, self.variables, self.functions) def preprocess_source(source, filename=None, include_dirs=[]): |
