diff options
| author | Peter LaFosse <peter@vector35.com> | 2017-03-05 13:12:01 -0500 |
|---|---|---|
| committer | Peter LaFosse <peter@vector35.com> | 2017-03-05 13:12:01 -0500 |
| commit | cf5997848b8819725315bd2c8dd8a04c70da3b63 (patch) | |
| tree | 24d9360e3ccfaee361aca4d345072c142c386a41 /python | |
| parent | a0132eed82d28d4408a2475847e1804a157b3dc1 (diff) | |
| parent | 27f1271083efb09efc6405f91be893ab38dad9a0 (diff) | |
Merginging with dev
Diffstat (limited to 'python')
| -rw-r--r-- | python/__init__.py | 28 | ||||
| -rw-r--r-- | python/architecture.py | 68 | ||||
| -rw-r--r-- | python/basicblock.py | 137 | ||||
| -rw-r--r-- | python/binaryview.py | 249 | ||||
| -rw-r--r-- | python/callingconvention.py | 10 | ||||
| -rw-r--r-- | python/demangle.py | 4 | ||||
| -rwxr-xr-x | python/examples/export_svg.py | 9 | ||||
| -rw-r--r-- | python/examples/version_switcher.py | 34 | ||||
| -rw-r--r-- | python/filemetadata.py | 10 | ||||
| -rw-r--r-- | python/function.py | 82 | ||||
| -rw-r--r-- | python/generator.cpp | 56 | ||||
| -rw-r--r-- | python/lowlevelil.py | 14 | ||||
| -rw-r--r-- | python/platform.py | 101 | ||||
| -rw-r--r-- | python/plugin.py | 20 | ||||
| -rw-r--r-- | python/transform.py | 10 | ||||
| -rw-r--r-- | python/types.py | 348 | ||||
| -rw-r--r-- | python/update.py | 24 |
17 files changed, 1035 insertions, 169 deletions
diff --git a/python/__init__.py b/python/__init__.py index 5df7c7f1..f028bc5b 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -49,9 +49,25 @@ from .pluginmanager import * def shutdown(): + """ + ``shutdown`` cleanly shuts down the core, stopping all workers and closing all log files. + """ core.BNShutdown() +def get_unique_identifier(): + return core.BNGetUniqueIdentifierString() + + +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 + """ + return core.BNGetInstallDirectory() + + class _DestructionCallbackHandler(object): def __init__(self): self._cb = core.BNObjectDestructionCallbacks() @@ -77,4 +93,16 @@ bundled_plugin_path = core.BNGetBundledPluginDirectory() user_plugin_path = core.BNGetUserPluginDirectory() core_version = core.BNGetVersionString() +'''Core version''' + core_build_id = core.BNGetBuildId() +'''Build ID''' + +core_product = core.BNGetProduct() +'''Product string from the license file''' + +core_product_type = core.BNGetProductType() +'''Product type from the license file''' + +core_license_count = core.BNGetLicenseCount() +'''License count from the license file''' diff --git a/python/architecture.py b/python/architecture.py index f1440ca3..39e5e72d 100644 --- a/python/architecture.py +++ b/python/architecture.py @@ -333,6 +333,16 @@ class Architecture(object): self._pending_reg_lists = {} self._pending_token_lists = {} + def __eq__(self, value): + if not isinstance(value, Architecture): + return False + return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents) + + def __ne__(self, value): + if not isinstance(value, Architecture): + return True + return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) + @property def full_width_regs(self): """List of full width register strings (read-only)""" @@ -364,7 +374,7 @@ class Architecture(object): def __setattr__(self, name, value): if ((name == "name") or (name == "endianness") or (name == "address_size") or - (name == "default_int_size") or (name == "regs") or (name == "get_max_instruction_length")): + (name == "default_int_size") or (name == "regs") or (name == "get_max_instruction_length")): raise AttributeError("attribute '%s' is read only" % name) else: try: @@ -467,6 +477,8 @@ class Architecture(object): token_buf[i].value = tokens[i].value token_buf[i].size = tokens[i].size token_buf[i].operand = tokens[i].operand + token_buf[i].context = tokens[i].context + token_buf[i].address = tokens[i].address result[0] = token_buf ptr = ctypes.cast(token_buf, ctypes.c_void_p) self._pending_token_lists[ptr.value] = (ptr.value, token_buf) @@ -1148,7 +1160,9 @@ class Architecture(object): value = tokens[i].value size = tokens[i].size operand = tokens[i].operand - result.append(function.InstructionTextToken(token_type, text, value, size, operand)) + context = tokens[i].context + address = tokens[i].address + result.append(function.InstructionTextToken(token_type, text, value, size, operand, context, address)) core.BNFreeInstructionText(tokens, count.value) return result, length.value @@ -1160,8 +1174,8 @@ class Architecture(object): def get_instruction_low_level_il(self, data, addr, il): """ - ``get_instruction_low_level_il`` appends LowLevelILExpr objects for the instruction at the given virtual - address ``addr`` with data ``data``. + ``get_instruction_low_level_il`` appends LowLevelILExpr objects to ``il`` for the instruction at the given + virtual address ``addr`` with data ``data``. :param str data: max_instruction_length bytes from the binary at virtual address ``addr`` :param int addr: virtual address of bytes in ``data`` @@ -1581,7 +1595,7 @@ class Architecture(object): """ core.BNSetBinaryViewTypeArchitectureConstant(self.handle, type_name, const_name, value) - def parse_types_from_source(self, source, filename=None, include_dirs=[]): + 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``. @@ -1589,8 +1603,9 @@ class Architecture(object): :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 - :return: a tuple of py:class:`TypeParserResult` and error string - :rtype: tuple(TypeParserResult,str) + :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') @@ -1606,32 +1621,37 @@ class Architecture(object): 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)) + 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: - return (None, error_str) + raise SyntaxError(error_str) type_dict = {} variables = {} functions = {} for i in xrange(0, parse.typeCount): - types[parse.types[i].name] = types.Type(core.BNNewTypeReference(parse.types[i].type)) + 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): - variables[parse.variables[i].name] = types.Type(core.BNNewTypeReference(parse.variables[i].type)) + 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): - functions[parse.functions[i].name] = types.Type(core.BNNewTypeReference(parse.functions[i].type)) + 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), error_str) + return types.TypeParserResult(type_dict, variables, functions) - def parse_types_from_source_file(self, filename, include_dirs=[]): + 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 - :return: a tuple of py:class:`TypeParserResult` and error string - :rtype: tuple(TypeParserResult, str) + :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" @@ -1647,22 +1667,26 @@ class Architecture(object): 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)) + 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: - return (None, error_str) + raise SyntaxError(error_str) type_dict = {} variables = {} functions = {} for i in xrange(0, parse.typeCount): - type_dict[parse.types[i].name] = types.Type(core.BNNewTypeReference(parse.types[i].type)) + 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): - variables[parse.variables[i].name] = types.Type(core.BNNewTypeReference(parse.variables[i].type)) + 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): - functions[parse.functions[i].name] = types.Type(core.BNNewTypeReference(parse.functions[i].type)) + 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), error_str) + return types.TypeParserResult(type_dict, variables, functions) def register_calling_convention(self, cc): """ diff --git a/python/basicblock.py b/python/basicblock.py index ae9889fc..9f256aa7 100644 --- a/python/basicblock.py +++ b/python/basicblock.py @@ -29,19 +29,23 @@ import function class BasicBlockEdge(object): - def __init__(self, branch_type, target, arch): + def __init__(self, branch_type, source, target): self.type = branch_type - if self.type != BranchType.UnresolvedBranch: - self.target = target - self.arch = arch + self.source = source + self.target = target def __repr__(self): if self.type == BranchType.UnresolvedBranch: return "<%s>" % BranchType(self.type).name - elif self.arch: - return "<%s: %s@%#x>" % (self.type, self.arch.name, self.target) + elif self.target.arch: + return "<%s: %s@%#x>" % (BranchType(self.type).name, self.target.arch.name, self.target.start) else: - return "<%s: %#x>" % (self.type, self.target) + return "<%s: %#x>" % (BranchType(self.type).name, self.target.start) + + @property + def back_edge(self): + """Whether the edge is a back edge (end of a loop)""" + return self.target in self.source.dominators class BasicBlock(object): @@ -52,6 +56,16 @@ class BasicBlock(object): def __del__(self): core.BNFreeBasicBlock(self.handle) + def __eq__(self, value): + if not isinstance(value, BasicBlock): + return False + return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents) + + def __ne__(self, value): + if not isinstance(value, BasicBlock): + return True + return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) + @property def function(self): """Basic block function (read-only)""" @@ -84,20 +98,40 @@ class BasicBlock(object): return core.BNGetBasicBlockLength(self.handle) @property + def index(self): + """Basic block index in list of blocks for the function (read-only)""" + return core.BNGetBasicBlockIndex(self.handle) + + @property def outgoing_edges(self): """List of basic block outgoing edges (read-only)""" count = ctypes.c_ulonglong(0) edges = core.BNGetBasicBlockOutgoingEdges(self.handle, count) result = [] for i in xrange(0, count.value): - branch_type = edges[i].type - target = edges[i].target - if edges[i].arch: - arch = architecture.Architecture(edges[i].arch) + branch_type = BranchType(edges[i].type) + if edges[i].target: + target = BasicBlock(self.view, core.BNNewBasicBlockReference(edges[i].target)) else: - arch = None - result.append(BasicBlockEdge(branch_type, target, arch)) - core.BNFreeBasicBlockOutgoingEdgeList(edges) + target = None + result.append(BasicBlockEdge(branch_type, self, target)) + core.BNFreeBasicBlockEdgeList(edges, count.value) + return result + + @property + def incoming_edges(self): + """List of basic block incoming edges (read-only)""" + count = ctypes.c_ulonglong(0) + edges = core.BNGetBasicBlockIncomingEdges(self.handle, count) + result = [] + for i in xrange(0, count.value): + branch_type = BranchType(edges[i].type) + if edges[i].target: + target = BasicBlock(self.view, core.BNNewBasicBlockReference(edges[i].target)) + else: + target = None + result.append(BasicBlockEdge(branch_type, self, target)) + core.BNFreeBasicBlockEdgeList(edges, count.value) return result @property @@ -106,6 +140,58 @@ class BasicBlock(object): return core.BNBasicBlockHasUndeterminedOutgoingEdges(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]))) + core.BNFreeBasicBlockList(blocks, count.value) + return result + + @property + def strict_dominators(self): + """List of strict dominators for this basic block (read-only)""" + count = ctypes.c_ulonglong() + blocks = core.BNGetBasicBlockStrictDominators(self.handle, count) + result = [] + for i in xrange(0, count.value): + result.append(BasicBlock(self.view, core.BNNewBasicBlockReference(blocks[i]))) + core.BNFreeBasicBlockList(blocks, count.value) + return result + + @property + def immediate_dominator(self): + """Immediate dominator of this basic block (read-only)""" + result = core.BNGetBasicBlockImmediateDominator(self.handle) + if not result: + return None + return BasicBlock(self.view, result) + + @property + def dominator_tree_children(self): + """List of child blocks in the dominator tree for this basic block (read-only)""" + count = ctypes.c_ulonglong() + blocks = core.BNGetBasicBlockDominatorTreeChildren(self.handle, count) + result = [] + for i in xrange(0, count.value): + result.append(BasicBlock(self.view, core.BNNewBasicBlockReference(blocks[i]))) + core.BNFreeBasicBlockList(blocks, count.value) + return result + + @property + def dominance_frontier(self): + """Dominance frontier for this basic block (read-only)""" + count = ctypes.c_ulonglong() + blocks = core.BNGetBasicBlockDominanceFrontier(self.handle, count) + result = [] + for i in xrange(0, count.value): + result.append(BasicBlock(self.view, core.BNNewBasicBlockReference(blocks[i]))) + core.BNFreeBasicBlockList(blocks, count.value) + return result + + @property def annotations(self): """List of automatic annotations for the start of this block (read-only)""" return self.function.get_block_annotations(self.arch, self.start) @@ -144,6 +230,21 @@ class BasicBlock(object): def highlight(self, value): self.set_user_highlight(value) + @classmethod + def get_iterated_dominance_frontier(self, blocks): + if len(blocks) == 0: + return [] + block_set = (ctypes.POINTER(core.BNBasicBlock) * len(blocks))() + for i in xrange(len(blocks)): + block_set[i] = blocks[i].handle + count = ctypes.c_ulonglong() + out_blocks = core.BNGetBasicBlockIteratedDominanceFrontier(block_set, len(blocks), count) + result = [] + for i in xrange(0, count.value): + result.append(BasicBlock(blocks[0].view, core.BNNewBasicBlockReference(out_blocks[i]))) + core.BNFreeBasicBlockList(out_blocks, count.value) + return result + def __setattr__(self, name, value): try: object.__setattr__(self, name, value) @@ -200,7 +301,9 @@ class BasicBlock(object): value = lines[i].tokens[j].value size = lines[i].tokens[j].size operand = lines[i].tokens[j].operand - tokens.append(function.InstructionTextToken(token_type, text, value, size, operand)) + context = lines[i].tokens[j].context + address = lines[i].tokens[j].address + tokens.append(function.InstructionTextToken(token_type, text, value, size, operand, context, address)) result.append(function.DisassemblyTextLine(addr, tokens)) core.BNFreeDisassemblyTextLines(lines, count.value) return result @@ -215,6 +318,8 @@ class BasicBlock(object): """ if not isinstance(color, HighlightStandardColor) and not isinstance(color, highlight.HighlightColor): raise ValueError("Specified color is not one of HighlightStandardColor, highlight.HighlightColor") + if isinstance(color, HighlightStandardColor): + color = highlight.HighlightColor(color) core.BNSetAutoBasicBlockHighlight(self.handle, color._get_core_struct()) def set_user_highlight(self, color): @@ -229,4 +334,6 @@ class BasicBlock(object): """ if not isinstance(color, HighlightStandardColor) and not isinstance(color, highlight.HighlightColor): raise ValueError("Specified color is not one of HighlightStandardColor, highlight.HighlightColor") + if isinstance(color, HighlightStandardColor): + color = highlight.HighlightColor(color) core.BNSetUserBasicBlockHighlight(self.handle, color._get_core_struct()) diff --git a/python/binaryview.py b/python/binaryview.py index af7faa32..dd37be69 100644 --- a/python/binaryview.py +++ b/python/binaryview.py @@ -78,6 +78,12 @@ class BinaryDataNotification(object): def string_removed(self, view, string_type, offset, length): pass + def type_defined(self, view, name, type): + pass + + def type_undefined(self, view, name, type): + pass + class StringReference(object): def __init__(self, string_type, start, length): @@ -157,6 +163,8 @@ class BinaryDataNotificationCallbacks(object): self._cb.dataVariableUpdated = self._cb.dataVariableUpdated.__class__(self._data_var_updated) self._cb.stringFound = self._cb.stringFound.__class__(self._string_found) self._cb.stringRemoved = self._cb.stringRemoved.__class__(self._string_removed) + self._cb.typeDefined = self._cb.typeDefined.__class__(self._type_defined) + self._cb.typeUndefined = self._cb.typeUndefined.__class__(self._type_undefined) def _register(self): core.BNRegisterDataNotification(self.view.handle, self._cb) @@ -239,6 +247,20 @@ class BinaryDataNotificationCallbacks(object): except: log.log_error(traceback.format_exc()) + def _type_defined(self, ctxt, name, type_obj): + try: + qualified_name = types.QualifiedName._from_core_struct(name[0]) + self.notify.type_defined(self.view, qualified_name, types.Type(core.BNNewTypeReference(type_obj))) + except: + log.log_error(traceback.format_exc()) + + def _type_undefined(self, ctxt, name, type_obj): + try: + qualified_name = types.QualifiedName._from_core_struct(name[0]) + self.notify.type_undefined(self.view, qualified_name, types.Type(core.BNNewTypeReference(type_obj))) + except: + log.log_error(traceback.format_exc()) + class _BinaryViewTypeMetaclass(type): @property @@ -277,6 +299,16 @@ class BinaryViewType(object): def __init__(self, handle): self.handle = core.handle_of_type(handle, core.BNBinaryViewType) + def __eq__(self, value): + if not isinstance(value, BinaryViewType): + return False + return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents) + + def __ne__(self, value): + if not isinstance(value, BinaryViewType): + return True + return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) + @property def name(self): """BinaryView name (read-only)""" @@ -326,7 +358,11 @@ class BinaryViewType(object): return None for available in view.available_view_types: if available.name != "Raw": - bv = cls[available.name].open(filename) + if filename.endswith(".bndb"): + bv = view.get_view_of_type(available.name) + else: + bv = cls[available.name].open(filename) + if update_analysis: bv.update_analysis_and_wait() return bv @@ -521,6 +557,16 @@ class BinaryView(object): self.notifications = {} self.next_address = None # Do NOT try to access view before init() is called, use placeholder + def __eq__(self, value): + if not isinstance(value, BinaryView): + return False + return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents) + + def __ne__(self, value): + if not isinstance(value, BinaryView): + return True + return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) + @classmethod def register(cls): startup._init_plugins() @@ -754,7 +800,7 @@ class BinaryView(object): syms = core.BNGetSymbols(self.handle, count) result = {} for i in xrange(0, count.value): - sym = function.Symbol(None, None, None, handle=core.BNNewSymbolReference(syms[i])) + sym = types.Symbol(None, None, None, handle=core.BNNewSymbolReference(syms[i])) result[sym.raw_name] = sym core.BNFreeSymbolList(syms, count.value) return result @@ -821,7 +867,8 @@ class BinaryView(object): type_list = core.BNGetAnalysisTypeList(self.handle, count) result = {} for i in xrange(0, count.value): - result[type_list[i].name] = types.Type(core.BNNewTypeReference(type_list[i].type)) + name = types.QualifiedName._from_core_struct(type_list[i].name) + result[name] = types.Type(core.BNNewTypeReference(type_list[i].type)) core.BNFreeTypeList(type_list, count.value) return result @@ -1863,7 +1910,7 @@ class BinaryView(object): var = core.BNDataVariable() if not core.BNGetDataVariableAtAddress(self.handle, addr, var): return None - return DataVariable(var.address, type.Type(var.type), var.autoDiscovered) + return DataVariable(var.address, types.Type(var.type), var.autoDiscovered) def get_function_at(self, addr, plat=None): """ @@ -1929,7 +1976,7 @@ class BinaryView(object): def get_basic_blocks_starting_at(self, addr): """ - ``get_basic_blocks_at`` get a list of :py:Class:`BasicBlock` objects which start at the provided virtual address. + ``get_basic_blocks_starting_at`` get a list of :py:Class:`BasicBlock` objects which start at the provided virtual address. :param int addr: virtual address of BasicBlock desired :return: a list of :py:Class:`BasicBlock` objects @@ -2727,7 +2774,9 @@ class BinaryView(object): value = lines[i].contents.tokens[j].value size = lines[i].contents.tokens[j].size operand = lines[i].contents.tokens[j].operand - tokens.append(function.InstructionTextToken(token_type, text, value, size, operand)) + context = lines[i].contents.tokens[j].context + address = lines[i].contents.tokens[j].address + tokens.append(function.InstructionTextToken(token_type, text, value, size, operand, context, address)) contents = function.DisassemblyTextLine(addr, tokens) result.append(lineardisassembly.LinearDisassemblyLine(lines[i].type, func, block, lines[i].lineOffset, contents)) @@ -2827,51 +2876,117 @@ class BinaryView(object): ``parse_type_string`` converts `C-style` string into a :py:Class:`Type`. :param str text: `C-style` string of type to create - :return: A tuple of a :py:Class:`Type` and string type name - :rtype: tuple(Type, str) + :return: A tuple of a :py:Class:`Type` and type name + :rtype: tuple(Type, QualifiedName) :Example: >>> bv.parse_type_string("int foo") (<type: int32_t>, 'foo') >>> """ - result = core.BNNameAndType() + result = core.BNQualifiedNameAndType() errors = ctypes.c_char_p() if not core.BNParseTypeString(self.handle, text, result, errors): 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)) - name = result.name - core.BNFreeNameAndType(result) + name = types.QualifiedName._from_core_struct(result.name) + core.BNFreeQualifiedNameAndType(result) return type_obj, name def get_type_by_name(self, name): """ ``get_type_by_name`` returns the defined type whose name corresponds with the provided ``name`` - :param str name: Type name to lookup + :param QualifiedName name: Type name to lookup :return: A :py:Class:`Type` or None if the type does not exist :rtype: Type or None :Example: >>> type, name = bv.parse_type_string("int foo") - >>> bv.define_type(name, type) + >>> bv.define_user_type(name, type) >>> bv.get_type_by_name(name) <type: int32_t> >>> """ + name = types.QualifiedName(name)._get_core_struct() obj = core.BNGetAnalysisTypeByName(self.handle, name) if not obj: return None return types.Type(obj) + def get_type_by_id(self, id): + """ + ``get_type_by_id`` returns the defined type whose unique identifier corresponds with the provided ``id`` + + :param str id: Unique identifier to lookup + :return: A :py:Class:`Type` or None if the type does not exist + :rtype: Type or None + :Example: + + >>> type, name = bv.parse_type_string("int foo") + >>> type_id = Type.generate_auto_type_id("source", name) + >>> bv.define_type(type_id, name, type) + >>> bv.get_type_by_id(type_id) + <type: int32_t> + >>> + """ + obj = core.BNGetAnalysisTypeById(self.handle, id) + if not obj: + return None + return types.Type(obj) + + def get_type_name_by_id(self, id): + """ + ``get_type_name_by_id`` returns the defined type name whose unique identifier corresponds with the provided ``id`` + + :param str id: Unique identifier to lookup + :return: A QualifiedName or None if the type does not exist + :rtype: QualifiedName or None + :Example: + + >>> type, name = bv.parse_type_string("int foo") + >>> type_id = Type.generate_auto_type_id("source", name) + >>> bv.define_type(type_id, name, type) + 'foo' + >>> bv.get_type_name_by_id(type_id) + 'foo' + >>> + """ + name = core.BNGetAnalysisTypeNameById(self.handle, id) + result = types.QualifiedName._from_core_struct(name) + core.BNFreeQualifiedName(name) + if len(result) == 0: + return None + return result + + def get_type_id(self, name): + """ + ``get_type_id`` returns the unique indentifier of the defined type whose name corresponds with the + provided ``name`` + + :param QualifiedName name: Type name to lookup + :return: The unique identifier of the type + :rtype: str + :Example: + + >>> type, name = bv.parse_type_string("int foo") + >>> type_id = Type.generate_auto_type_id("source", name) + >>> registered_name = bv.define_type(type_id, name, type) + >>> bv.get_type_id(registered_name) == type_id + True + >>> + """ + name = types.QualifiedName(name)._get_core_struct() + return core.BNGetAnalysisTypeId(self.handle, name) + def is_type_auto_defined(self, name): """ ``is_type_auto_defined`` queries the user type list of name. If name is not in the *user* type list then the name is considered an *auto* type. - :param str name: Name of type to query + :param QualifiedName name: Name of type to query :return: True if the type is not a *user* type. False if the type is a *user* type. :Example: >>> bv.is_type_auto_defined("foo") @@ -2881,31 +2996,38 @@ class BinaryView(object): False >>> """ + name = types.QualifiedName(name)._get_core_struct() return core.BNIsAnalysisTypeAutoDefined(self.handle, name) - def define_type(self, name, type_obj): + def define_type(self, type_id, default_name, type_obj): """ ``define_type`` registers a :py:Class:`Type` ``type_obj`` of the given ``name`` in the global list of types for - the current :py:Class:`BinaryView`. + the current :py:Class:`BinaryView`. This method should only be used for automatically generated types. - :param str name: Name of the type to be registered + :param str type_id: Unique identifier for the automatically generated type + :param QualifiedName default_name: Name of the type to be registered :param Type type_obj: Type object to be registered - :rtype: None + :return: Registered name of the type. May not be the same as the requested name if the user has renamed types. + :rtype: QualifiedName :Example: >>> type, name = bv.parse_type_string("int foo") - >>> bv.define_type(name, type) - >>> bv.get_type_by_name(name) + >>> registered_name = bv.define_type(Type.generate_auto_type_id("source", name), name, type) + >>> bv.get_type_by_name(registered_name) <type: int32_t> """ - core.BNDefineAnalysisType(self.handle, name, type_obj.handle) + name = types.QualifiedName(default_name)._get_core_struct() + reg_name = core.BNDefineAnalysisType(self.handle, type_id, name, type_obj.handle) + result = types.QualifiedName._from_core_struct(reg_name) + core.BNFreeQualifiedName(reg_name) + return result def define_user_type(self, name, type_obj): """ ``define_user_type`` registers a :py:Class:`Type` ``type_obj`` of the given ``name`` in the global list of user types for the current :py:Class:`BinaryView`. - :param str name: Name of the user type to be registered + :param QualifiedName name: Name of the user type to be registered :param Type type_obj: Type object to be registered :rtype: None :Example: @@ -2915,45 +3037,86 @@ class BinaryView(object): >>> bv.get_type_by_name(name) <type: int32_t> """ + name = types.QualifiedName(name)._get_core_struct() core.BNDefineUserAnalysisType(self.handle, name, type_obj.handle) - def undefine_type(self, name): + def undefine_type(self, type_id): """ ``undefine_type`` removes a :py:Class:`Type` from the global list of types for the current :py:Class:`BinaryView` - :param str name: Name of type to be undefined + :param str type_id: Unique identifier of type to be undefined :rtype: None :Example: >>> type, name = bv.parse_type_string("int foo") - >>> bv.define_type(name, type) + >>> type_id = Type.generate_auto_type_id("source", name) + >>> bv.define_type(type_id, name, type) >>> bv.get_type_by_name(name) <type: int32_t> - >>> bv.undefine_type(name) + >>> bv.undefine_type(type_id) >>> bv.get_type_by_name(name) >>> """ - core.BNUndefineAnalysisType(self.handle, name) + core.BNUndefineAnalysisType(self.handle, type_id) def undefine_user_type(self, name): """ ``undefine_user_type`` removes a :py:Class:`Type` from the global list of user types for the current :py:Class:`BinaryView` - :param str name: Name of user type to be undefined + :param QualifiedName name: Name of user type to be undefined :rtype: None :Example: >>> type, name = bv.parse_type_string("int foo") - >>> bv.define_type(name, type) + >>> bv.define_user_type(name, type) >>> bv.get_type_by_name(name) <type: int32_t> - >>> bv.undefine_type(name) + >>> bv.undefine_user_type(name) >>> bv.get_type_by_name(name) >>> """ + name = types.QualifiedName(name)._get_core_struct() core.BNUndefineUserAnalysisType(self.handle, name) + def rename_type(self, old_name, new_name): + """ + ``rename_type`` renames a type in the global list of types for the current :py:Class:`BinaryView` + + :param QualifiedName old_name: Existing name of type to be renamed + :param QualifiedName new_name: New name of type to be renamed + :rtype: None + :Example: + + >>> type, name = bv.parse_type_string("int foo") + >>> bv.define_user_type(name, type) + >>> bv.get_type_by_name("foo") + <type: int32_t> + >>> bv.rename_type("foo", "bar") + >>> bv.get_type_by_name("bar") + <type: int32_t> + >>> + """ + old_name = types.QualifiedName(old_name)._get_core_struct() + new_name = types.QualifiedName(new_name)._get_core_struct() + core.BNRenameAnalysisType(self.handle, old_name, new_name) + + def register_platform_types(self, platform): + """ + ``register_platform_types`` ensures that the platform-specific types for a :py:Class:`Platform` are available + for the current :py:Class:`BinaryView`. This is automatically performed when adding a new function or setting + the default platform. + + :param Platform platform: Platform containing types to be registered + :rtype: None + :Example: + + >>> platform = Platform["linux-x86"] + >>> bv.register_platform_types(platform) + >>> + """ + core.BNRegisterPlatformTypes(self.handle, platform.handle) + def find_next_data(self, start, data, flags = 0): """ ``find_next_data`` searchs for the bytes in data starting at the virtual address ``start`` either, case-sensitive, @@ -3021,6 +3184,12 @@ class BinaryView(object): segment.flags) return result + def get_address_for_data_offset(self, offset): + address = ctypes.c_ulonglong() + if not core.BNGetAddressForDataOffset(self.handle, offset, address): + 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, @@ -3106,6 +3275,16 @@ class BinaryReader(object): def __del__(self): core.BNFreeBinaryReader(self.handle) + def __eq__(self, value): + if not isinstance(value, BinaryReader): + return False + return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents) + + def __ne__(self, value): + if not isinstance(value, BinaryReader): + return True + return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) + @property def endianness(self): """ @@ -3413,6 +3592,16 @@ class BinaryWriter(object): def __del__(self): core.BNFreeBinaryWriter(self.handle) + def __eq__(self, value): + if not isinstance(value, BinaryWriter): + return False + return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents) + + def __ne__(self, value): + if not isinstance(value, BinaryWriter): + return True + return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) + @property def endianness(self): """ diff --git a/python/callingconvention.py b/python/callingconvention.py index 5f4adeab..04ba711f 100644 --- a/python/callingconvention.py +++ b/python/callingconvention.py @@ -112,6 +112,16 @@ class CallingConvention(object): def __del__(self): core.BNFreeCallingConvention(self.handle) + def __eq__(self, value): + if not isinstance(value, CallingConvention): + return False + return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents) + + def __ne__(self, value): + if not isinstance(value, CallingConvention): + return True + return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) + def _get_caller_saved_regs(self, ctxt, count): try: regs = self.__class__.caller_saved_regs diff --git a/python/demangle.py b/python/demangle.py index 520cfbaf..ed38674a 100644 --- a/python/demangle.py +++ b/python/demangle.py @@ -63,7 +63,7 @@ def demangle_ms(arch, mangled_name): if core.BNDemangleMS(arch.handle, mangled_name, ctypes.byref(handle), ctypes.byref(outName), ctypes.byref(outSize)): for i in xrange(outSize.value): names.append(outName[i]) - core.BNFreeDemangledName(outName.value, outSize.value) + core.BNFreeDemangledName(ctypes.byref(outName), outSize.value) return (types.Type(handle), names) return (None, mangled_name) @@ -76,7 +76,7 @@ def demangle_gnu3(arch, mangled_name): if core.BNDemangleGNU3(arch.handle, mangled_name, ctypes.byref(handle), ctypes.byref(outName), ctypes.byref(outSize)): for i in xrange(outSize.value): names.append(outName[i]) - core.BNFreeDemangledName(outName.value, outSize.value) + core.BNFreeDemangledName(ctypes.byref(outName), outSize.value) if not handle: return (None, names) return (types.Type(handle), names) diff --git a/python/examples/export_svg.py b/python/examples/export_svg.py index 89bc41a1..95e2d62d 100755 --- a/python/examples/export_svg.py +++ b/python/examples/export_svg.py @@ -80,6 +80,10 @@ def render_svg(function): fill: none; stroke-width: 1px; } + .back_edge { + fill: none; + stroke-width: 2px; + } .UnconditionalBranch, .IndirectBranch { stroke: rgb(128, 198, 233); color: rgb(128, 198, 233); @@ -197,7 +201,10 @@ def render_svg(function): points += str(x * widthconst) + "," + str(y * heightconst) + " " x, y = edge.points[-1] points += str(x * widthconst) + "," + str(y * heightconst + 0) + " " - edges += ' <polyline class="edge {type}" points="{points}" marker-end="url(#arrow-{type})"/>\n'.format(type=BranchType(edge.type).name, points=points) + if edge.back_edge: + edges += ' <polyline class="back_edge {type}" points="{points}" marker-end="url(#arrow-{type})"/>\n'.format(type=BranchType(edge.type).name, points=points) + else: + edges += ' <polyline class="edge {type}" points="{points}" marker-end="url(#arrow-{type})"/>\n'.format(type=BranchType(edge.type).name, points=points) output += ' ' + edges + '\n' output += ' </g>\n' output += '</svg></html>' diff --git a/python/examples/version_switcher.py b/python/examples/version_switcher.py index bc4f576c..9d5bbf05 100644 --- a/python/examples/version_switcher.py +++ b/python/examples/version_switcher.py @@ -20,10 +20,12 @@ # IN THE SOFTWARE. import sys -import binaryninja + +from binaryninja.update import UpdateChannel, are_auto_updates_enabled, set_auto_updates_enabled, is_update_installation_pending, install_pending_update +from binaryninja import core_version import datetime -chandefault = binaryninja.UpdateChannel.list[0].name +chandefault = UpdateChannel.list[0].name channel = None versions = [] @@ -31,17 +33,17 @@ versions = [] def load_channel(newchannel): global channel global versions - if (channel is None and newchannel == channel.name): + if (channel is not None and newchannel == channel.name): print "Same channel, not updating." else: try: print "Loading channel %s" % newchannel - channel = binaryninja.UpdateChannel[newchannel] + channel = UpdateChannel[newchannel] print "Loading versions..." versions = channel.versions except Exception: print "%s is not a valid channel name. Defaulting to " % chandefault - channel = binaryninja.UpdateChannel[chandefault] + channel = UpdateChannel[chandefault] def select(version): @@ -66,16 +68,20 @@ def select(version): print "Requesting update to latest version." else: print "Requesting update to prior version." - if binaryninja.are_auto_updates_enabled(): + if are_auto_updates_enabled(): print "Disabling automatic updates." - binaryninja.set_auto_updates_enabled(False) - if (version.version == binaryninja.core_version): + set_auto_updates_enabled(False) + if (version.version == core_version): print "Already running %s" % version.version else: print "version.version %s" % version.version - print "binaryninja.core_version %s" % binaryninja.core_version - print "Updating..." + print "core_version %s" % core_version + print "Downloading..." print version.update() + print "Installing..." + if is_update_installation_pending: + #note that the GUI will be launched after update but should still do the upgrade headless + install_pending_update() # forward updating won't work without reloading sys.exit() else: @@ -86,7 +92,7 @@ def list_channels(): done = False print "\tSelect channel:\n" while not done: - channel_list = binaryninja.UpdateChannel.list + channel_list = UpdateChannel.list for index, item in enumerate(channel_list): print "\t%d)\t%s" % (index + 1, item.name) print "\t%d)\t%s" % (len(channel_list) + 1, "Main Menu") @@ -104,7 +110,7 @@ def list_channels(): def toggle_updates(): - binaryninja.set_auto_updates_enabled(not binaryninja.are_auto_updates_enabled()) + set_auto_updates_enabled(not are_auto_updates_enabled()) def main(): @@ -114,8 +120,8 @@ def main(): while not done: print "\n\tBinary Ninja Version Switcher" print "\t\tCurrent Channel:\t%s" % channel.name - print "\t\tCurrent Version:\t%s" % binaryninja.core_version - print "\t\tAuto-Updates On:\t%s\n" % binaryninja.are_auto_updates_enabled() + print "\t\tCurrent Version:\t%s" % core_version + print "\t\tAuto-Updates On:\t%s\n" % are_auto_updates_enabled() for index, version in enumerate(versions): date = datetime.datetime.fromtimestamp(version.time).strftime('%c') print "\t%d)\t%s (%s)" % (index + 1, version.version, date) diff --git a/python/filemetadata.py b/python/filemetadata.py index f6593405..b489d5bb 100644 --- a/python/filemetadata.py +++ b/python/filemetadata.py @@ -93,6 +93,16 @@ class FileMetadata(object): core.BNSetFileMetadataNavigationHandler(self.handle, None) core.BNFreeFileMetadata(self.handle) + def __eq__(self, value): + if not isinstance(value, FileMetadata): + return False + return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents) + + def __ne__(self, value): + if not isinstance(value, FileMetadata): + return True + return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) + @classmethod def _unregister(cls, f): handle = ctypes.cast(f, ctypes.c_void_p) diff --git a/python/function.py b/python/function.py index 7d03585c..86c93bf7 100644 --- a/python/function.py +++ b/python/function.py @@ -26,7 +26,7 @@ import ctypes import _binaryninjacore as core from enums import (FunctionGraphType, BranchType, SymbolType, InstructionTextTokenType, HighlightStandardColor, HighlightColorStyle, RegisterValueType, ImplicitRegisterExtend, - DisassemblyOption, IntegerDisplayType) + DisassemblyOption, IntegerDisplayType, InstructionTextTokenContext) import architecture import highlight import associateddatastore @@ -177,6 +177,16 @@ class Function(object): core.BNReleaseAdvancedFunctionAnalysisDataMultiple(self.handle, self._advanced_analysis_requests) core.BNFreeFunction(self.handle) + def __eq__(self, value): + if not isinstance(value, Function): + return False + return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents) + + def __ne__(self, value): + if not isinstance(value, Function): + return True + return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) + @classmethod def _unregister(cls, func): handle = ctypes.cast(func, ctypes.c_void_p) @@ -669,7 +679,9 @@ class Function(object): value = lines[i].tokens[j].value size = lines[i].tokens[j].size operand = lines[i].tokens[j].operand - tokens.append(InstructionTextToken(token_type, text, value, size, operand)) + context = lines[i].tokens[j].context + address = lines[i].tokens[j].address + tokens.append(InstructionTextToken(token_type, text, value, size, operand, context, address)) result.append(tokens) core.BNFreeInstructionTextLines(lines, count.value) return result @@ -764,7 +776,9 @@ class Function(object): """ if arch is None: arch = self.arch - if not isinstance(color, highlight.HighlightColor): + if not isinstance(color, HighlightStandardColor) and not isinstance(color, highlight.HighlightColor): + raise ValueError("Specified color is not one of HighlightStandardColor, highlight.HighlightColor") + if isinstance(color, HighlightStandardColor): color = highlight.HighlightColor(color = color) core.BNSetAutoInstructionHighlight(self.handle, arch.handle, addr, color._get_core_struct()) @@ -784,6 +798,8 @@ class Function(object): arch = self.arch if not isinstance(color, HighlightStandardColor) and not isinstance(color, highlight.HighlightColor): raise ValueError("Specified color is not one of HighlightStandardColor, highlight.HighlightColor") + if isinstance(color, HighlightStandardColor): + color = highlight.HighlightColor(color) core.BNSetUserInstructionHighlight(self.handle, arch.handle, addr, color._get_core_struct()) @@ -831,16 +847,19 @@ class DisassemblyTextLine(object): class FunctionGraphEdge(object): - def __init__(self, branch_type, arch, target, points): + def __init__(self, branch_type, source, target, points): self.type = BranchType(branch_type) - self.arch = arch + self.source = source self.target = target self.points = points def __repr__(self): - if self.arch: - return "<%s: %s@%#x>" % (self.type.name, self.arch.name, self.target) - return "<%s: %#x>" % (self.type, self.target) + return "<%s: %s>" % (self.type.name, repr(self.target)) + + @property + def back_edge(self): + """Whether the edge is a back edge (end of a loop)""" + return self.target in self.source.basic_block.dominators class FunctionGraphBlock(object): @@ -850,6 +869,16 @@ class FunctionGraphBlock(object): def __del__(self): core.BNFreeFunctionGraphBlock(self.handle) + def __eq__(self, value): + if not isinstance(value, FunctionGraphBlock): + return False + return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents) + + def __ne__(self, value): + if not isinstance(value, FunctionGraphBlock): + return True + return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) + @property def basic_block(self): """Basic block associated with this part of the function graph (read-only)""" @@ -916,7 +945,9 @@ class FunctionGraphBlock(object): value = lines[i].tokens[j].value size = lines[i].tokens[j].size operand = lines[i].tokens[j].operand - tokens.append(InstructionTextToken(token_type, text, value, size, operand)) + context = lines[i].tokens[j].context + address = lines[i].tokens[j].address + tokens.append(InstructionTextToken(token_type, text, value, size, operand, context, address)) result.append(DisassemblyTextLine(addr, tokens)) core.BNFreeDisassemblyTextLines(lines, count.value) return result @@ -930,13 +961,19 @@ class FunctionGraphBlock(object): for i in xrange(0, count.value): branch_type = BranchType(edges[i].type) target = edges[i].target - arch = None - if edges[i].arch is not None: - arch = architecture.Architecture(edges[i].arch) + if target: + func = core.BNGetBasicBlockFunction(target) + if func is None: + core.BNFreeBasicBlock(target) + target = None + else: + target = basicblock.BasicBlock(binaryview.BinaryView(handle = core.BNGetFunctionData(func)), + core.BNNewBasicBlockReference(target)) + core.BNFreeFunction(func) points = [] for j in xrange(0, edges[i].pointCount): points.append((edges[i].points[j].x, edges[i].points[j].y)) - result.append(FunctionGraphEdge(branch_type, arch, target, points)) + result.append(FunctionGraphEdge(branch_type, self, target, points)) core.BNFreeFunctionGraphBlockOutgoingEdgeList(edges, count.value) return result @@ -966,7 +1003,9 @@ class FunctionGraphBlock(object): value = lines[i].tokens[j].value size = lines[i].tokens[j].size operand = lines[i].tokens[j].operand - tokens.append(InstructionTextToken(token_type, text, value, size, operand)) + context = lines[i].tokens[j].context + address = lines[i].tokens[j].address + tokens.append(InstructionTextToken(token_type, text, value, size, operand, context, address)) yield DisassemblyTextLine(addr, tokens) finally: core.BNFreeDisassemblyTextLines(lines, count.value) @@ -1020,6 +1059,16 @@ class FunctionGraph(object): self.abort() core.BNFreeFunctionGraph(self.handle) + def __eq__(self, value): + if not isinstance(value, FunctionGraph): + return False + return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents) + + def __ne__(self, value): + if not isinstance(value, FunctionGraph): + return True + return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) + @property def function(self): """Function for a function graph (read-only)""" @@ -1237,12 +1286,15 @@ class InstructionTextToken(object): ========================== ============================================ """ - def __init__(self, token_type, text, value = 0, size = 0, operand = 0xffffffff): + def __init__(self, token_type, text, value = 0, size = 0, operand = 0xffffffff, + context = InstructionTextTokenContext.NoTokenContext, address = 0): self.type = InstructionTextTokenType(token_type) self.text = text self.value = value self.size = size self.operand = operand + self.context = InstructionTextTokenContext(context) + self.address = address def __str__(self): return self.text diff --git a/python/generator.cpp b/python/generator.cpp index d3725b6d..1c0e7b16 100644 --- a/python/generator.cpp +++ b/python/generator.cpp @@ -97,17 +97,19 @@ void OutputType(FILE* out, Type* type, bool isReturnType = false, bool isCallbac else fprintf(out, "ctypes.c_double"); break; - case StructureTypeClass: - fprintf(out, "%s", type->GetQualifiedName(type->GetStructure()->GetName()).c_str()); - break; - case EnumerationTypeClass: - { - string name = type->GetQualifiedName(type->GetEnumeration()->GetName()); - if (name.size() > 2 && name.substr(0, 2) == "BN") - name = name.substr(2); - fprintf(out, "%sEnum", name.c_str()); + case NamedTypeReferenceClass: + if (type->GetNamedTypeReference()->GetTypeClass() == EnumNamedTypeClass) + { + string name = type->GetNamedTypeReference()->GetName().GetString(); + if (name.size() > 2 && name.substr(0, 2) == "BN") + name = name.substr(2); + fprintf(out, "%sEnum", name.c_str()); + } + else + { + fprintf(out, "%s", type->GetNamedTypeReference()->GetName().GetString().c_str()); + } break; - } case PointerTypeClass: if (isCallback || (type->GetChildType()->GetClass() == VoidTypeClass)) { @@ -161,7 +163,7 @@ int main(int argc, char* argv[]) Architecture::Register(new GeneratorArchitecture()); // Parse API header to get type and function information - map<string, Ref<Type>> types, vars, funcs; + 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()); @@ -195,14 +197,17 @@ int main(int argc, char* argv[]) fprintf(out, "# Type definitions\n"); for (auto& i : types) { + string name; + if (i.first.size() != 1) + continue; + name = i.first[0]; if (i.second->GetClass() == StructureTypeClass) { - fprintf(out, "class %s(ctypes.Structure):\n", i.first.c_str()); + fprintf(out, "class %s(ctypes.Structure):\n", name.c_str()); fprintf(out, "\tpass\n"); } else if (i.second->GetClass() == EnumerationTypeClass) { - string name = i.first; if (name.size() > 2 && name.substr(0, 2) == "BN") name = name.substr(2); @@ -217,7 +222,7 @@ int main(int argc, char* argv[]) else if ((i.second->GetClass() == BoolTypeClass) || (i.second->GetClass() == IntegerTypeClass) || (i.second->GetClass() == FloatTypeClass) || (i.second->GetClass() == ArrayTypeClass)) { - fprintf(out, "%s = ", i.first.c_str()); + fprintf(out, "%s = ", name.c_str()); OutputType(out, i.second); fprintf(out, "\n"); } @@ -227,9 +232,13 @@ int main(int argc, char* argv[]) fprintf(out, "\n# Structure definitions\n"); for (auto& i : types) { + string name; + if (i.first.size() != 1) + continue; + name = i.first[0]; if ((i.second->GetClass() == StructureTypeClass) && (i.second->GetStructure()->GetMembers().size() != 0)) { - fprintf(out, "%s._fields_ = [\n", i.first.c_str()); + fprintf(out, "%s._fields_ = [\n", name.c_str()); for (auto& j : i.second->GetStructure()->GetMembers()) { fprintf(out, "\t\t(\"%s\", ", j.name.c_str()); @@ -243,6 +252,11 @@ int main(int argc, char* argv[]) fprintf(out, "\n# Function definitions\n"); for (auto& i : funcs) { + string name; + if (i.first.size() != 1) + continue; + name = i.first[0]; + // Check for a string result, these will be automatically wrapped to free the string // memory and return a Python string bool stringResult = (i.second->GetChildType()->GetClass() == PointerTypeClass) && @@ -251,7 +265,7 @@ int main(int argc, char* argv[]) // Pointer returns will be automatically wrapped to return None on null pointer bool pointerResult = (i.second->GetChildType()->GetClass() == PointerTypeClass); bool callbackConvention = false; - if (i.first == "BNAllocString") + if (name == "BNAllocString") { // Don't perform automatic wrapping of string allocation, and return a void // pointer so that callback functions (which is the only valid use of BNAllocString) @@ -260,11 +274,11 @@ int main(int argc, char* argv[]) callbackConvention = true; } - string funcName = i.first; + string funcName = name; if (stringResult || pointerResult) funcName = string("_") + funcName; - fprintf(out, "%s = core.%s\n", funcName.c_str(), i.first.c_str()); + fprintf(out, "%s = core.%s\n", funcName.c_str(), name.c_str()); fprintf(out, "%s.restype = ", funcName.c_str()); OutputType(out, i.second->GetChildType(), true, callbackConvention); fprintf(out, "\n"); @@ -274,7 +288,7 @@ int main(int argc, char* argv[]) for (auto& j : i.second->GetParameters()) { fprintf(out, "\t\t"); - if (i.first == "BNFreeString") + if (name == "BNFreeString") { // BNFreeString expects a pointer to a string allocated by the core, so do not use // a c_char_p here, as that would be allocated by the Python runtime. This can @@ -293,7 +307,7 @@ int main(int argc, char* argv[]) if (stringResult) { // Emit wrapper to get Python string and free native memory - fprintf(out, "def %s(*args):\n", i.first.c_str()); + fprintf(out, "def %s(*args):\n", name.c_str()); fprintf(out, "\tresult = %s(*args)\n", funcName.c_str()); fprintf(out, "\tstring = ctypes.cast(result, ctypes.c_char_p).value\n"); fprintf(out, "\tBNFreeString(result)\n"); @@ -302,7 +316,7 @@ int main(int argc, char* argv[]) else if (pointerResult) { // Emit wrapper to return None on null pointer - fprintf(out, "def %s(*args):\n", i.first.c_str()); + fprintf(out, "def %s(*args):\n", name.c_str()); fprintf(out, "\tresult = %s(*args)\n", funcName.c_str()); fprintf(out, "\tif not result:\n"); fprintf(out, "\t\treturn None\n"); diff --git a/python/lowlevelil.py b/python/lowlevelil.py index 419e8513..c80fcd0d 100644 --- a/python/lowlevelil.py +++ b/python/lowlevelil.py @@ -187,7 +187,9 @@ class LowLevelILInstruction(object): value = tokens[i].value size = tokens[i].size operand = tokens[i].operand - result.append(function.InstructionTextToken(token_type, text, value, size, operand)) + context = tokens[i].context + address = tokens[i].address + result.append(function.InstructionTextToken(token_type, text, value, size, operand, context, address)) core.BNFreeInstructionText(tokens, count.value) return result @@ -251,6 +253,16 @@ class LowLevelILFunction(object): def __del__(self): core.BNFreeLowLevelILFunction(self.handle) + def __eq__(self, value): + if not isinstance(value, LowLevelILFunction): + return False + return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents) + + def __ne__(self, value): + if not isinstance(value, LowLevelILFunction): + return True + return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) + @property def current_address(self): """Current IL Address (read/write)""" diff --git a/python/platform.py b/python/platform.py index 04dce587..139b7d5e 100644 --- a/python/platform.py +++ b/python/platform.py @@ -25,6 +25,7 @@ import _binaryninjacore as core import startup import architecture import callingconvention +import types class _PlatformMetaClass(type): @@ -109,6 +110,16 @@ class Platform(object): def __del__(self): core.BNFreePlatform(self.handle) + def __eq__(self, value): + if not isinstance(value, Platform): + return False + return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents) + + def __ne__(self, value): + if not isinstance(value, Platform): + return True + return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) + @property def default_calling_convention(self): """ @@ -215,6 +226,55 @@ class Platform(object): core.BNFreeCallingConventionList(cc, count.value) return result + @property + def types(self): + """List of platform-specific types (read-only)""" + count = ctypes.c_ulonglong(0) + type_list = core.BNGetPlatformTypes(self.handle, count) + 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)) + core.BNFreeTypeList(type_list, count.value) + return result + + @property + def variables(self): + """List of platform-specific variable definitions (read-only)""" + count = ctypes.c_ulonglong(0) + type_list = core.BNGetPlatformVariables(self.handle, count) + 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)) + core.BNFreeTypeList(type_list, count.value) + return result + + @property + def functions(self): + """List of platform-specific function definitions (read-only)""" + count = ctypes.c_ulonglong(0) + type_list = core.BNGetPlatformFunctions(self.handle, count) + 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)) + core.BNFreeTypeList(type_list, count.value) + return result + + @property + def system_calls(self): + """List of system calls for this platform (read-only)""" + count = ctypes.c_ulonglong(0) + call_list = core.BNGetPlatformSystemCalls(self.handle, count) + 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)) + result[call_list[i].number] = (name, t) + core.BNFreeSystemCallList(call_list, count.value) + return result + def __setattr__(self, name, value): try: object.__setattr__(self, name, value) @@ -259,3 +319,44 @@ class Platform(object): new_addr.value = addr result = core.BNGetAssociatedPlatformByAddress(self.handle, new_addr) return Platform(None, handle = result), new_addr.value + + def get_type_by_name(self, name): + name = types.QualifiedName(name)._get_core_struct() + obj = core.BNGetPlatformTypeByName(self.handle, name) + if not obj: + return None + return types.Type(obj) + + 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) + + 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) + + def get_system_call_name(self, number): + return core.BNGetPlatformSystemCallName(self.handle, number) + + def get_system_call_type(self, number): + obj = core.BNGetPlatformSystemCallType(self.handle, number) + if not obj: + return None + return types.Type(obj) + + def generate_auto_platform_type_id(self, name): + name = types.QualifiedName(name)._get_core_struct() + return core.BNGenerateAutoPlatformTypeId(self.handle, name) + + def generate_auto_platform_type_ref(self, type_class, name): + type_id = self.generate_auto_platform_type_id(name) + return types.NamedTypeReference(type_class, type_id, name) + + def get_auto_platform_type_id_source(self): + return core.BNGetAutoPlatformTypeIdSource(self.handle) diff --git a/python/plugin.py b/python/plugin.py index 9a4da487..2632b5a9 100644 --- a/python/plugin.py +++ b/python/plugin.py @@ -24,7 +24,7 @@ import threading # Binary Ninja components import _binaryninjacore as core -from enums import LowLevelILOperation +from enums import PluginCommandType import startup import filemetadata import binaryview @@ -78,7 +78,7 @@ class PluginCommand(object): ctypes.memmove(ctypes.byref(self.command), ctypes.byref(cmd), ctypes.sizeof(core.BNPluginCommand)) self.name = str(cmd.name) self.description = str(cmd.description) - self.type = LowLevelILOperation(cmd.type) + self.type = PluginCommandType(cmd.type) @classmethod def _default_action(cls, view, action): @@ -210,21 +210,21 @@ class PluginCommand(object): def is_valid(self, context): if context.view is None: return False - if self.command.type == LowLevelILOperation.DefaultPluginCommand: + if self.command.type == PluginCommandType.DefaultPluginCommand: if not self.command.defaultIsValid: return True return self.command.defaultIsValid(self.command.context, context.view.handle) - elif self.command.type == LowLevelILOperation.AddressPluginCommand: + elif self.command.type == PluginCommandType.AddressPluginCommand: if not self.command.addressIsValid: return True return self.command.addressIsValid(self.command.context, context.view.handle, context.address) - elif self.command.type == LowLevelILOperation.RangePluginCommand: + elif self.command.type == PluginCommandType.RangePluginCommand: if context.length == 0: return False if not self.command.rangeIsValid: return True return self.command.rangeIsValid(self.command.context, context.view.handle, context.address, context.length) - elif self.command.type == LowLevelILOperation.FunctionPluginCommand: + elif self.command.type == PluginCommandType.FunctionPluginCommand: if context.function is None: return False if not self.command.functionIsValid: @@ -235,13 +235,13 @@ class PluginCommand(object): def execute(self, context): if not self.is_valid(context): return - if self.command.type == LowLevelILOperation.DefaultPluginCommand: + if self.command.type == PluginCommandType.DefaultPluginCommand: self.command.defaultCommand(self.command.context, context.view.handle) - elif self.command.type == LowLevelILOperation.AddressPluginCommand: + elif self.command.type == PluginCommandType.AddressPluginCommand: self.command.addressCommand(self.command.context, context.view.handle, context.address) - elif self.command.type == LowLevelILOperation.RangePluginCommand: + elif self.command.type == PluginCommandType.RangePluginCommand: self.command.rangeCommand(self.command.context, context.view.handle, context.address, context.length) - elif self.command.type == LowLevelILOperation.FunctionPluginCommand: + elif self.command.type == PluginCommandType.FunctionPluginCommand: self.command.functionCommand(self.command.context, context.view.handle, context.function.handle) def __repr__(self): diff --git a/python/transform.py b/python/transform.py index 0c003738..40382c69 100644 --- a/python/transform.py +++ b/python/transform.py @@ -131,6 +131,16 @@ class Transform(object): def __repr__(self): return "<transform: %s>" % self.name + def __eq__(self, value): + if not isinstance(value, Transform): + return False + return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents) + + def __ne__(self, value): + if not isinstance(value, Transform): + return True + return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) + def _get_parameters(self, ctxt, count): try: count[0] = len(self.parameters) diff --git a/python/types.py b/python/types.py index 8582e4e0..ebaa36fc 100644 --- a/python/types.py +++ b/python/types.py @@ -22,9 +22,92 @@ import ctypes # Binary Ninja components import _binaryninjacore as core -from enums import SymbolType, TypeClass +from enums import SymbolType, TypeClass, NamedTypeReferenceClass, InstructionTextTokenType import callingconvention -import demangle +import function + + +class QualifiedName(object): + def __init__(self, name = []): + if isinstance(name, str): + self.name = [name] + elif isinstance(name, QualifiedName): + self.name = name.name + else: + self.name = name + + def __str__(self): + return "::".join(self.name) + + def __repr__(self): + return repr(str(self)) + + def __len__(self): + return len(self.name) + + def __hash__(self): + return hash(str(self)) + + def __eq__(self, other): + if isinstance(other, str): + return str(self) == other + elif isinstance(other, list): + return self.name == other + elif isinstance(other, QualifiedName): + return self.name == other.name + return False + + def __ne__(self, other): + return not (self == other) + + def __lt__(self, other): + if isinstance(other, QualifiedName): + return self.name < other.name + return False + + def __le__(self, other): + if isinstance(other, QualifiedName): + return self.name <= other.name + return False + + def __gt__(self, other): + if isinstance(other, QualifiedName): + return self.name > other.name + return False + + def __ge__(self, other): + if isinstance(other, QualifiedName): + return self.name >= other.name + return False + + def __cmp__(self, other): + if self == other: + return 0 + if self < other: + return -1 + return 1 + + def __getitem__(self, key): + return self.name[key] + + def __iter__(self): + return iter(self.name) + + def _get_core_struct(self): + result = core.BNQualifiedName() + name_list = (ctypes.c_char_p * len(self.name))() + for i in xrange(0, len(self.name)): + name_list[i] = self.name[i] + result.name = name_list + result.nameCount = len(self.name) + return result + + @classmethod + def _from_core_struct(cls, name): + result = [] + for i in xrange(0, name.nameCount): + result.append(name.name[i]) + return QualifiedName(result) class Symbol(object): @@ -56,6 +139,16 @@ class Symbol(object): def __del__(self): core.BNFreeSymbol(self.handle) + def __eq__(self, value): + if not isinstance(value, Symbol): + return False + return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents) + + def __ne__(self, value): + if not isinstance(value, Symbol): + return True + return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) + @property def type(self): """Symbol type (read-only)""" @@ -111,6 +204,16 @@ class Type(object): def __del__(self): core.BNFreeType(self.handle) + def __eq__(self, value): + if not isinstance(value, Type): + return False + return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents) + + def __ne__(self, value): + if not isinstance(value, Type): + return True + return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) + @property def type_class(self): """Type class (read-only)""" @@ -210,6 +313,14 @@ class Type(object): return None return Enumeration(result) + @property + def named_type_reference(self): + """Reference to a named type (read-only)""" + result = core.BNGetTypeNamedTypeReference(self.handle) + if result is None: + return None + return NamedTypeReference(handle = result) + @property def count(self): """Type count (read-only)""" @@ -227,6 +338,56 @@ class Type(object): def get_string_after_name(self): return core.BNGetTypeStringAfterName(self.handle) + @property + def tokens(self): + """Type string as a list of tokens (read-only)""" + count = ctypes.c_ulonglong() + tokens = core.BNGetTypeTokens(self.handle, count) + result = [] + for i in xrange(0, count.value): + token_type = InstructionTextTokenType(tokens[i].type) + text = tokens[i].text + value = tokens[i].value + size = tokens[i].size + operand = tokens[i].operand + context = tokens[i].context + address = tokens[i].address + result.append(function.InstructionTextToken(token_type, text, value, size, operand, context, address)) + core.BNFreeTokenList(tokens, count.value) + return result + + def get_tokens_before_name(self): + count = ctypes.c_ulonglong() + tokens = core.BNGetTypeTokensBeforeName(self.handle, count) + result = [] + for i in xrange(0, count.value): + token_type = InstructionTextTokenType(tokens[i].type) + text = tokens[i].text + value = tokens[i].value + size = tokens[i].size + operand = tokens[i].operand + context = tokens[i].context + address = tokens[i].address + result.append(function.InstructionTextToken(token_type, text, value, size, operand, context, address)) + core.BNFreeTokenList(tokens, count.value) + return result + + def get_tokens_after_name(self): + count = ctypes.c_ulonglong() + tokens = core.BNGetTypeTokensAfterName(self.handle, count) + result = [] + for i in xrange(0, count.value): + token_type = InstructionTextTokenType(tokens[i].type) + text = tokens[i].text + value = tokens[i].value + size = tokens[i].size + operand = tokens[i].operand + context = tokens[i].context + address = tokens[i].address + result.append(function.InstructionTextToken(token_type, text, value, size, operand, context, address)) + core.BNFreeTokenList(tokens, count.value) + return result + @classmethod def void(cls): return Type(core.BNCreateVoidType()) @@ -237,6 +398,12 @@ class Type(object): @classmethod def int(self, width, sign = True, 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)) @classmethod @@ -248,8 +415,27 @@ class Type(object): return Type(core.BNCreateStructureType(structure_type.handle)) @classmethod - def unknown_type(self, unknown_type): - return Type(core.BNCreateUnknownType(unknown_type.handle)) + def named_type(self, named_type, width = 0, align = 1): + return Type(core.BNCreateNamedTypeReference(named_type.handle, width, align)) + + @classmethod + def named_type_from_type_and_id(self, type_id, name, t): + name = QualifiedName(name)._get_core_struct() + if t is not None: + t = t.handle + return Type(core.BNCreateNamedTypeReferenceFromTypeAndId(type_id, name, t)) + + @classmethod + def named_type_from_type(self, name, t): + name = QualifiedName(name)._get_core_struct() + if t is not None: + t = t.handle + return Type(core.BNCreateNamedTypeReferenceFromTypeAndId("", name, t)) + + @classmethod + def named_type_from_registered_type(self, view, name): + name = QualifiedName(name)._get_core_struct() + return Type(core.BNCreateNamedTypeReferenceFromType(view.handle, name)) @classmethod def enumeration_type(self, arch, e, width=None): @@ -267,6 +453,14 @@ class Type(object): @classmethod def function(self, ret, params, calling_convention=None, variable_arguments=False): + """ + ``function`` class method for creating an function Type. + + :param Type ret: width of the integer in bytes + :param list(Type) params: list of parameter Types + :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))() for i in xrange(0, len(params)): if isinstance(params[i], Type): @@ -280,6 +474,20 @@ class Type(object): return Type(core.BNCreateFunctionType(ret.handle, calling_convention, param_buf, len(params), variable_arguments)) + @classmethod + def generate_auto_type_id(self, source, name): + name = QualifiedName(name)._get_core_struct() + return core.BNGenerateAutoTypeId(source, name) + + @classmethod + def generate_auto_demangled_type_id(self, name): + name = QualifiedName(name)._get_core_struct() + return core.BNGenerateAutoDemangledTypeId(name) + + @classmethod + def get_auto_demanged_type_id_source(self): + return core.BNGetAutoDemangledTypeIdSource() + def __setattr__(self, name, value): try: object.__setattr__(self, name, value) @@ -287,28 +495,80 @@ class Type(object): raise AttributeError("attribute '%s' is read only" % name) -class UnknownType(object): - def __init__(self, handle=None): +class NamedTypeReference(object): + def __init__(self, type_class = NamedTypeReferenceClass.UnknownNamedTypeClass, type_id = None, name = None, handle = None): if handle is None: - self.handle = core.BNCreateUnknownType() + self.handle = core.BNCreateNamedType() + core.BNSetTypeReferenceClass(self.handle, type_class) + if type_id is not None: + core.BNSetTypeReferenceId(self.handle, type_id) + if name is not None: + name = QualifiedName(name)._get_core_struct() + core.BNSetTypeReferenceName(self.handle, name) else: self.handle = handle def __del__(self): - core.BNFreeUnknownType(self.handle) + core.BNFreeNamedTypeReference(self.handle) + + def __eq__(self, value): + if not isinstance(value, NamedTypeReference): + return False + return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents) + + def __ne__(self, value): + if not isinstance(value, NamedTypeReference): + return True + return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) + + @property + def type_class(self): + return NamedTypeReferenceClass(core.BNGetTypeReferenceClass(self.handle)) + + @type_class.setter + def type_class(self, value): + core.BNSetTypeReferenceClass(self.handle, value) + + @property + def type_id(self): + return core.BNGetTypeReferenceId(self.handle) + + @type_id.setter + def type_id(self, value): + core.BNSetTypeReferenceId(self.handle, value) @property def name(self): - count = ctypes.c_ulonglong() - nameList = core.BNGetUnknownTypeName(self.handle, count) - result = [] - for i in xrange(count.value): - result.append(nameList[i]) - return demangle.get_qualified_name(result) + name = core.BNGetTypeReferenceName(self.handle) + result = QualifiedName._from_core_struct(name) + core.BNFreeQualifiedName(name) + return result @name.setter def name(self, value): - core.BNSetUnknownTypeName(self.handle, value) + value = QualifiedName(value)._get_core_struct() + core.BNSetTypeReferenceName(self.handle, value) + + def __repr__(self): + if self.type_class == NamedTypeReferenceClass.TypedefNamedTypeClass: + return "<named type: typedef %s>" % str(self.name) + if self.type_class == NamedTypeReferenceClass.StructNamedTypeClass: + return "<named type: struct %s>" % str(self.name) + if self.type_class == NamedTypeReferenceClass.UnionNamedTypeClass: + return "<named type: union %s>" % str(self.name) + if self.type_class == NamedTypeReferenceClass.EnumNamedTypeClass: + return "<named type: enum %s>" % str(self.name) + return "<named type: unknown %s>" % str(self.name) + + @classmethod + def generate_auto_type_ref(self, type_class, source, name): + type_id = Type.generate_auto_type_id(source, name) + return NamedTypeReference(type_class, type_id, name) + + @classmethod + def generate_auto_demangled_type_ref(self, type_class, name): + type_id = Type.generate_auto_demangled_type_id(name) + return NamedTypeReference(type_class, type_id, name) class StructureMember(object): @@ -334,18 +594,15 @@ class Structure(object): def __del__(self): core.BNFreeStructure(self.handle) - @property - def name(self): - count = ctypes.c_ulonglong() - nameList = core.BNGetStructureName(self.handle, count) - result = [] - for i in xrange(count.value): - result.append(nameList[i]) - return demangle.get_qualified_name(result) + def __eq__(self, value): + if not isinstance(value, Structure): + return False + return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents) - @name.setter - def name(self, value): - core.BNSetStructureName(self.handle, value) + def __ne__(self, value): + if not isinstance(value, Structure): + return True + return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) @property def members(self): @@ -361,14 +618,22 @@ class Structure(object): @property def width(self): - """Structure width (read-only)""" + """Structure width""" return core.BNGetStructureWidth(self.handle) + @width.setter + def width(self, new_width): + core.BNSetStructureWidth(self.handle, new_width) + @property def alignment(self): - """Structure alignment (read-only)""" + """Structure alignment""" return core.BNGetStructureAlignment(self.handle) + @alignment.setter + def alignment(self, align): + core.BNSetStructureAlignment(self.handle, align) + @property def packed(self): return core.BNIsStructurePacked(self.handle) @@ -392,8 +657,6 @@ class Structure(object): raise AttributeError("attribute '%s' is read only" % name) def __repr__(self): - if len(self.name) > 0: - return "<struct: %s>" % self.name return "<struct: size %#x>" % self.width def append(self, t, name = ""): @@ -405,6 +668,9 @@ class Structure(object): def remove(self, i): core.BNRemoveStructureMember(self.handle, i) + def replace(self, i, t, name = ""): + core.BNReplaceStructureMember(self.handle, i, t.handle, name) + class EnumerationMember(object): def __init__(self, name, value, default): @@ -426,13 +692,15 @@ class Enumeration(object): def __del__(self): core.BNFreeEnumeration(self.handle) - @property - def name(self): - return core.BNGetEnumerationName(self.handle) + def __eq__(self, value): + if not isinstance(value, Enumeration): + return False + return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents) - @name.setter - def name(self, value): - core.BNSetEnumerationName(self.handle, value) + def __ne__(self, value): + if not isinstance(value, Enumeration): + return True + return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) @property def members(self): @@ -452,8 +720,6 @@ class Enumeration(object): raise AttributeError("attribute '%s' is read only" % name) def __repr__(self): - if len(self.name) > 0: - return "<enum: %s>" % self.name return "<enum: %s>" % repr(self.members) def append(self, name, value = None): @@ -462,6 +728,12 @@ class Enumeration(object): else: core.BNAddEnumerationMemberWithValue(self.handle, name, value) + def remove(self, i): + core.BNRemoveEnumerationMember(self.handle, i) + + def replace(self, i, name, value): + core.BNReplaceEnumerationMember(self.handle, i, name, value) + class TypeParserResult(object): def __init__(self, types, variables, functions): diff --git a/python/update.py b/python/update.py index 7e2bd4ef..6417e4a0 100644 --- a/python/update.py +++ b/python/update.py @@ -238,5 +238,29 @@ def get_time_since_last_update_check(): return core.BNGetTimeSinceLastUpdateCheck() +def is_update_installation_pending(): + """ + ``is_update_installation_pending`` whether an update has been downloaded and is waiting installation + + :return: boolean True if an update is pending, false if no update is pending + :rtype: bool + """ + return core.BNIsUpdateInstallationPending() + + +def install_pending_update(): + """ + ``install_pending_update`` installs any pending updates + + :rtype: None + """ + errors = ctypes.c_char_p() + core.BNInstallPendingUpdate(errors) + if errors: + error_str = errors.value + core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) + raise IOError(error_str) + + def updates_checked(): core.BNUpdatesChecked() |
