diff options
| author | plafosse <peter@vector35.com> | 2016-10-28 20:16:37 -0400 |
|---|---|---|
| committer | plafosse <peter@vector35.com> | 2016-10-28 20:16:37 -0400 |
| commit | 5e4cca1f1796bec109adacdb049d9e34c17656eb (patch) | |
| tree | 68d9effb24f5fae58c1016d992f9af57c18024cc /python/architecture.py | |
| parent | 253fc58d774f74edd47724eaec0197fc41561112 (diff) | |
Refactor python api into separate files and add Enumeration support. Also fixed bugs found with pyflakes
Diffstat (limited to 'python/architecture.py')
| -rw-r--r-- | python/architecture.py | 1661 |
1 files changed, 1661 insertions, 0 deletions
diff --git a/python/architecture.py b/python/architecture.py new file mode 100644 index 00000000..11aa4757 --- /dev/null +++ b/python/architecture.py @@ -0,0 +1,1661 @@ +# Copyright (c) 2015-2016 Vector 35 LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + +import traceback +import ctypes +import abc + +# Binary Ninja components +import _binaryninjacore as core +from _binaryninjacore import BNEndianness + +import startup +import function +import lowlevelil +import callingconvention +import platform +import log +import databuffer +import bntype + + +class _ArchitectureMetaClass(type): + @property + def list(self): + startup._init_plugins() + count = ctypes.c_ulonglong() + archs = core.BNGetArchitectureList(count) + result = [] + for i in xrange(0, count.value): + result.append(Architecture(archs[i])) + core.BNFreeArchitectureList(archs) + return result + + def __iter__(self): + startup._init_plugins() + count = ctypes.c_ulonglong() + archs = core.BNGetArchitectureList(count) + try: + for i in xrange(0, count.value): + yield Architecture(archs[i]) + finally: + core.BNFreeArchitectureList(archs) + + def __getitem__(cls, name): + startup._init_plugins() + arch = core.BNGetArchitectureByName(name) + if arch is None: + raise KeyError("'%s' is not a valid architecture" % str(name)) + return Architecture(arch) + + def register(cls): + startup._init_plugins() + if cls.name is None: + raise ValueError("architecture 'name' is not defined") + arch = cls() + cls._registered_cb = arch._cb + arch.handle = core.BNRegisterArchitecture(cls.name, arch._cb) + + def __setattr__(self, name, value): + try: + type.__setattr__(self, name, value) + except AttributeError: + raise AttributeError("attribute '%s' is read only" % name) + + +class Architecture(object): + """ + ``class Architecture`` is the parent class for all CPU architectures. Subclasses of Architecture implement assembly, + disassembly, IL lifting, and patching. + + ``class Architecture`` has a ``__metaclass__`` with the additional methods ``register``, and supports + iteration:: + + >>> #List the architectures + >>> list(Architecture) + [<arch: aarch64>, <arch: armv7>, <arch: armv7eb>, <arch: mipsel32>, <arch: mips32>, <arch: powerpc>, + <arch: x86>, <arch: x86_64>] + >>> #Register a new Architecture + >>> class MyArch(Architecture): + ... name = "MyArch" + ... + >>> MyArch.register() + >>> list(Architecture) + [<arch: aarch64>, <arch: armv7>, <arch: armv7eb>, <arch: mipsel32>, <arch: mips32>, <arch: powerpc>, + <arch: x86>, <arch: x86_64>, <arch: MyArch>] + >>> + + For the purposes of this documentation the variable ``arch`` will be used in the following context :: + + >>> from binaryninja import * + >>> arch = Architecture['x86'] + """ + name = None + endianness = BNEndianness.LittleEndian + address_size = 8 + default_int_size = 4 + max_instr_length = 16 + opcode_display_length = 8 + regs = {} + stack_pointer = None + link_reg = None + flags = [] + flag_write_types = [] + flag_roles = {} + flags_required_for_flag_condition = {} + flags_written_by_flag_write_type = {} + __metaclass__ = _ArchitectureMetaClass + next_address = 0 + + def __init__(self, handle=None): + if handle is not None: + self.handle = core.handle_of_type(handle, core.BNArchitecture) + self.__dict__["name"] = core.BNGetArchitectureName(self.handle) + self.__dict__["endianness"] = core.BNEndianness(core.BNGetArchitectureEndianness(self.handle)).name + self.__dict__["address_size"] = core.BNGetArchitectureAddressSize(self.handle) + self.__dict__["default_int_size"] = core.BNGetArchitectureDefaultIntegerSize(self.handle) + self.__dict__["max_instr_length"] = core.BNGetArchitectureMaxInstructionLength(self.handle) + self.__dict__["opcode_display_length"] = core.BNGetArchitectureOpcodeDisplayLength(self.handle) + self.__dict__["stack_pointer"] = core.BNGetArchitectureRegisterName(self.handle, + core.BNGetArchitectureStackPointerRegister(self.handle)) + self.__dict__["link_reg"] = core.BNGetArchitectureRegisterName(self.handle, + core.BNGetArchitectureLinkRegister(self.handle)) + + count = ctypes.c_ulonglong() + regs = core.BNGetAllArchitectureRegisters(self.handle, count) + self.__dict__["regs"] = {} + for i in xrange(0, count.value): + name = core.BNGetArchitectureRegisterName(self.handle, regs[i]) + info = core.BNGetArchitectureRegisterInfo(self.handle, regs[i]) + full_width_reg = core.BNGetArchitectureRegisterName(self.handle, info.fullWidthRegister) + self.regs[name] = function.RegisterInfo(full_width_reg, info.size, info.offset, + core.BNImplicitRegisterExtend(info.extend).name, regs[i]) + core.BNFreeRegisterList(regs) + + count = ctypes.c_ulonglong() + flags = core.BNGetAllArchitectureFlags(self.handle, count) + self._flags = {} + self._flags_by_index = {} + self.__dict__["flags"] = [] + for i in xrange(0, count.value): + name = core.BNGetArchitectureFlagName(self.handle, flags[i]) + self._flags[name] = flags[i] + self._flags_by_index[flags[i]] = name + self.flags.append(name) + core.BNFreeRegisterList(flags) + + count = ctypes.c_ulonglong() + types = core.BNGetAllArchitectureFlagWriteTypes(self.handle, count) + self._flag_write_types = {} + self._flag_write_types_by_index = {} + self.__dict__["flag_write_types"] = [] + for i in xrange(0, count.value): + name = core.BNGetArchitectureFlagWriteTypeName(self.handle, types[i]) + self._flag_write_types[name] = types[i] + self._flag_write_types_by_index[types[i]] = name + self.flag_write_types.append(name) + core.BNFreeRegisterList(types) + + self._flag_roles = {} + self.__dict__["flag_roles"] = {} + for flag in self.__dict__["flags"]: + role = core.BNGetArchitectureFlagRole(self.handle, self._flags[flag]) + self.__dict__["flag_roles"][flag] = role + self._flag_roles[self._flags[flag]] = role + + self._flags_required_for_flag_condition = {} + self.__dict__["flags_required_for_flag_condition"] = {} + for cond in core.BNLowLevelILFlagCondition: + count = ctypes.c_ulonglong() + flags = core.BNGetArchitectureFlagsRequiredForFlagCondition(self.handle, cond, count) + flag_indexes = [] + flag_names = [] + for i in xrange(0, count.value): + flag_indexes.append(flags[i]) + flag_names.append(self._flags_by_index[flags[i]]) + core.BNFreeRegisterList(flags) + self._flags_required_for_flag_condition[cond] = flag_indexes + self.__dict__["flags_required_for_flag_condition"][cond] = flag_names + + self._flags_written_by_flag_write_type = {} + self.__dict__["flags_written_by_flag_write_type"] = {} + for write_type in self.flag_write_types: + count = ctypes.c_ulonglong() + flags = core.BNGetArchitectureFlagsWrittenByFlagWriteType(self.handle, + self._flag_write_types[write_type], count) + flag_indexes = [] + flag_names = [] + for i in xrange(0, count.value): + flag_indexes.append(flags[i]) + flag_names.append(self._flags_by_index[flags[i]]) + 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 + else: + startup._init_plugins() + + if self.__class__.opcode_display_length > self.__class__.max_instr_length: + self.__class__.opcode_display_length = self.__class__.max_instr_length + + self._cb = core.BNCustomArchitecture() + self._cb.context = 0 + self._cb.init = self._cb.init.__class__(self._init) + self._cb.getEndianness = self._cb.getEndianness.__class__(self._get_endianness) + self._cb.getAddressSize = self._cb.getAddressSize.__class__(self._get_address_size) + self._cb.getDefaultIntegerSize = self._cb.getDefaultIntegerSize.__class__(self._get_default_integer_size) + self._cb.getMaxInstructionLength = self._cb.getMaxInstructionLength.__class__(self._get_max_instruction_length) + self._cb.getOpcodeDisplayLength = self._cb.getOpcodeDisplayLength.__class__(self._get_opcode_display_length) + self._cb.getInstructionInfo = self._cb.getInstructionInfo.__class__(self._get_instruction_info) + self._cb.getInstructionText = self._cb.getInstructionText.__class__(self._get_instruction_text) + self._cb.freeInstructionText = self._cb.freeInstructionText.__class__(self._free_instruction_text) + self._cb.getInstructionLowLevelIL = self._cb.getInstructionLowLevelIL.__class__( + self._get_instruction_low_level_il) + self._cb.getRegisterName = self._cb.getRegisterName.__class__(self._get_register_name) + self._cb.getFlagName = self._cb.getFlagName.__class__(self._get_flag_name) + self._cb.getFlagWriteTypeName = self._cb.getFlagWriteTypeName.__class__(self._get_flag_write_type_name) + self._cb.getFullWidthRegisters = self._cb.getFullWidthRegisters.__class__(self._get_full_width_registers) + self._cb.getAllRegisters = self._cb.getAllRegisters.__class__(self._get_all_registers) + self._cb.getAllFlags = self._cb.getAllRegisters.__class__(self._get_all_flags) + self._cb.getAllFlagWriteTypes = self._cb.getAllRegisters.__class__(self._get_all_flag_write_types) + self._cb.getFlagRole = self._cb.getFlagRole.__class__(self._get_flag_role) + self._cb.getFlagsRequiredForFlagCondition = self._cb.getFlagsRequiredForFlagCondition.__class__( + self._get_flags_required_for_flag_condition) + self._cb.getFlagsWrittenByFlagWriteType = self._cb.getFlagsWrittenByFlagWriteType.__class__( + self._get_flags_written_by_flag_write_type) + self._cb.getFlagWriteLowLevelIL = self._cb.getFlagWriteLowLevelIL.__class__( + self._get_flag_write_low_level_il) + self._cb.getFlagConditionLowLevelIL = self._cb.getFlagConditionLowLevelIL.__class__( + self._get_flag_condition_low_level_il) + self._cb.freeRegisterList = self._cb.freeRegisterList.__class__(self._free_register_list) + self._cb.getRegisterInfo = self._cb.getRegisterInfo.__class__(self._get_register_info) + 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.assemble = self._cb.assemble.__class__(self._assemble) + self._cb.isNeverBranchPatchAvailable = self._cb.isNeverBranchPatchAvailable.__class__( + self._is_never_branch_patch_available) + self._cb.isAlwaysBranchPatchAvailable = self._cb.isAlwaysBranchPatchAvailable.__class__( + self._is_always_branch_patch_available) + self._cb.isInvertBranchPatchAvailable = self._cb.isInvertBranchPatchAvailable.__class__( + self._is_invert_branch_patch_available) + self._cb.isSkipAndReturnZeroPatchAvailable = self._cb.isSkipAndReturnZeroPatchAvailable.__class__( + self._is_skip_and_return_zero_patch_available) + self._cb.isSkipAndReturnValuePatchAvailable = self._cb.isSkipAndReturnValuePatchAvailable.__class__( + self._is_skip_and_return_value_patch_available) + self._cb.convertToNop = self._cb.convertToNop.__class__(self._convert_to_nop) + self._cb.alwaysBranch = self._cb.alwaysBranch.__class__(self._always_branch) + self._cb.invertBranch = self._cb.invertBranch.__class__(self._invert_branch) + self._cb.skipAndReturnValue = self._cb.skipAndReturnValue.__class__(self._skip_and_return_value) + + self._all_regs = {} + self._full_width_regs = {} + self._regs_by_index = {} + self.__dict__["regs"] = self.__class__.regs + reg_index = 0 + for reg in self.regs: + info = self.regs[reg] + if reg not in self._all_regs: + self._all_regs[reg] = reg_index + self._regs_by_index[reg_index] = reg + self.regs[reg].index = reg_index + reg_index += 1 + if info.full_width_reg not in self._all_regs: + self._all_regs[info.full_width_reg] = reg_index + self._regs_by_index[reg_index] = info.full_width_reg + self.regs[info.full_width_reg].index = reg_index + reg_index += 1 + if info.full_width_reg not in self._full_width_regs: + self._full_width_regs[info.full_width_reg] = self._all_regs[info.full_width_reg] + + self._flags = {} + self._flags_by_index = {} + self.__dict__["flags"] = self.__class__.flags + flag_index = 0 + for flag in self.__class__.flags: + if flag not in self._flags: + self._flags[flag] = flag_index + self._flags_by_index[flag_index] = flag + flag_index += 1 + + self._flag_write_types = {} + self._flag_write_types_by_index = {} + self.__dict__["flag_write_types"] = self.__class__.flag_write_types + write_type_index = 0 + for write_type in self.__class__.flag_write_types: + if write_type not in self._flag_write_types: + self._flag_write_types[write_type] = write_type_index + self._flag_write_types_by_index[write_type_index] = write_type + write_type_index += 1 + + self._flag_roles = {} + self.__dict__["flag_roles"] = self.__class__.flag_roles + for flag in self.__class__.flag_roles: + role = self.__class__.flag_roles[flag] + if isinstance(role, str): + role = core.BNFlagRole[role] + self._flag_roles[self._flags[flag]] = role + + self._flags_required_for_flag_condition = {} + self.__dict__["flags_required_for_flag_condition"] = self.__class__.flags_required_for_flag_condition + for cond in self.__class__.flags_required_for_flag_condition: + flags = [] + for flag in self.__class__.flags_required_for_flag_condition[cond]: + flags.append(self._flags[flag]) + self._flags_required_for_flag_condition[cond] = flags + + self._flags_written_by_flag_write_type = {} + self.__dict__["flags_written_by_flag_write_type"] = self.__class__.flags_written_by_flag_write_type + for write_type in self.__class__.flags_written_by_flag_write_type: + flags = [] + for flag in self.__class__.flags_written_by_flag_write_type[write_type]: + flags.append(self._flags[flag]) + self._flags_written_by_flag_write_type[self._flag_write_types[write_type]] = flags + + self._pending_reg_lists = {} + self._pending_token_lists = {} + + @property + def full_width_regs(self): + """List of full width register strings (read-only)""" + count = ctypes.c_ulonglong() + regs = core.BNGetFullWidthArchitectureRegisters(self.handle, count) + result = [] + for i in xrange(0, count.value): + result.append(core.BNGetArchitectureRegisterName(self.handle, regs[i])) + core.BNFreeRegisterList(regs) + return result + + @property + def calling_conventions(self): + """Dict of CallingConvention objects (read-only)""" + count = ctypes.c_ulonglong() + cc = core.BNGetArchitectureCallingConventions(self.handle, count) + result = {} + for i in xrange(0, count.value): + obj = callingconvention.CallingConvention(None, core.BNNewCallingConventionReference(cc[i])) + result[obj.name] = obj + core.BNFreeCallingConventionList(cc, count) + return result + + @property + def standalone_platform(self): + """Architecture standalone platform (read-only)""" + pl = core.BNGetArchitectureStandalonePlatform(self.handle) + return platform.Platform(self, pl) + + 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")): + raise AttributeError("attribute '%s' is read only" % name) + else: + try: + object.__setattr__(self, name, value) + except AttributeError: + raise AttributeError("attribute '%s' is read only" % name) + + def __repr__(self): + return "<arch: %s>" % self.name + + def _init(self, ctxt, handle): + self.handle = handle + + def _get_endianness(self, ctxt): + try: + return self.__class__.endianness + except: + log.log_error(traceback.format_exc()) + return core.BNEndianness.LittleEndian + + def _get_address_size(self, ctxt): + try: + return self.__class__.address_size + except: + log.log_error(traceback.format_exc()) + return 8 + + def _get_default_integer_size(self, ctxt): + try: + return self.__class__.default_int_size + except: + log.log_error(traceback.format_exc()) + return 4 + + def _get_max_instruction_length(self, ctxt): + try: + return self.__class__.max_instr_length + except: + log.log_error(traceback.format_exc()) + return 16 + + def _get_opcode_display_length(self, ctxt): + try: + return self.__class__.opcode_display_length + except: + log.log_error(traceback.format_exc()) + return 8 + + def _get_instruction_info(self, ctxt, data, addr, max_len, result): + try: + buf = ctypes.create_string_buffer(max_len) + ctypes.memmove(buf, data, max_len) + info = self.perform_get_instruction_info(buf.raw, addr) + if info is None: + return False + result[0].length = info.length + result[0].branchDelay = info.branch_delay + result[0].branchCount = len(info.branches) + for i in xrange(0, len(info.branches)): + if isinstance(info.branches[i].type, str): + result[0].branchType[i] = core.BNBranchType[info.branches[i].type] + else: + result[0].branchType[i] = info.branches[i].type + result[0].branchTarget[i] = info.branches[i].target + if info.branches[i].arch is None: + result[0].branchArch[i] = None + else: + result[0].branchArch[i] = info.branches[i].arch.handle + return True + except (KeyError, OSError): + log.log_error(traceback.format_exc()) + return False + + def _get_instruction_text(self, ctxt, data, addr, length, result, count): + try: + buf = ctypes.create_string_buffer(length[0]) + ctypes.memmove(buf, data, length[0]) + info = self.perform_get_instruction_text(buf.raw, addr) + if info is None: + return False + tokens = info[0] + length[0] = info[1] + count[0] = len(tokens) + token_buf = (core.BNInstructionTextToken * len(tokens))() + for i in xrange(0, len(tokens)): + if isinstance(tokens[i].type, str): + token_buf[i].type = core.BNInstructionTextTokenType[tokens[i].type] + else: + token_buf[i].type = tokens[i].type + token_buf[i].text = tokens[i].text + token_buf[i].value = tokens[i].value + token_buf[i].size = tokens[i].size + token_buf[i].operand = tokens[i].operand + result[0] = token_buf + ptr = ctypes.cast(token_buf, ctypes.c_void_p) + self._pending_token_lists[ptr.value] = (ptr.value, token_buf) + return True + except (KeyError, OSError): + log.log_error(traceback.format_exc()) + return False + + def _free_instruction_text(self, tokens, count): + try: + buf = ctypes.cast(tokens, ctypes.c_void_p) + if buf.value not in self._pending_token_lists: + raise ValueError("freeing token list that wasn't allocated") + del self._pending_token_lists[buf.value] + except KeyError: + log.log_error(traceback.format_exc()) + + def _get_instruction_low_level_il(self, ctxt, data, addr, length, il): + try: + buf = ctypes.create_string_buffer(length[0]) + ctypes.memmove(buf, data, length[0]) + result = self.perform_get_instruction_low_level_il(buf.raw, addr, + lowlevelil.LowLevelILFunction(self, core.BNNewLowLevelILFunctionReference(il))) + if result is None: + return False + length[0] = result + return True + except OSError: + log.log_error(traceback.format_exc()) + return False + + def _get_register_name(self, ctxt, reg): + try: + if reg in self._regs_by_index: + return core.BNAllocString(self._regs_by_index[reg]) + return core.BNAllocString("") + except (KeyError, OSError): + log.log_error(traceback.format_exc()) + return core.BNAllocString("") + + def _get_flag_name(self, ctxt, flag): + try: + if flag in self._flags_by_index: + return core.BNAllocString(self._flags_by_index[flag]) + return core.BNAllocString("") + except (KeyError, OSError): + log.log_error(traceback.format_exc()) + return core.BNAllocString("") + + def _get_flag_write_type_name(self, ctxt, write_type): + try: + if write_type in self._flag_write_types_by_index: + return core.BNAllocString(self._flag_write_types_by_index[write_type]) + return core.BNAllocString("") + except (KeyError, OSError): + log.log_error(traceback.format_exc()) + return core.BNAllocString("") + + def _get_full_width_registers(self, ctxt, count): + try: + regs = self._full_width_regs.values() + count[0] = len(regs) + reg_buf = (ctypes.c_uint * len(regs))() + for i in xrange(0, len(regs)): + reg_buf[i] = 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 _get_all_registers(self, ctxt, count): + try: + regs = self._regs_by_index.keys() + count[0] = len(regs) + reg_buf = (ctypes.c_uint * len(regs))() + for i in xrange(0, len(regs)): + reg_buf[i] = 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 _get_all_flags(self, ctxt, count): + try: + flags = self._flags_by_index.keys() + count[0] = len(flags) + flag_buf = (ctypes.c_uint * len(flags))() + for i in xrange(0, len(flags)): + flag_buf[i] = flags[i] + result = ctypes.cast(flag_buf, ctypes.c_void_p) + self._pending_reg_lists[result.value] = (result, flag_buf) + return result.value + except KeyError: + log.log_error(traceback.format_exc()) + count[0] = 0 + return None + + def _get_all_flag_write_types(self, ctxt, count): + try: + types = self._flag_write_types_by_index.keys() + count[0] = len(types) + type_buf = (ctypes.c_uint * len(types))() + for i in xrange(0, len(types)): + type_buf[i] = types[i] + result = ctypes.cast(type_buf, ctypes.c_void_p) + self._pending_reg_lists[result.value] = (result, type_buf) + return result.value + except KeyError: + log.log_error(traceback.format_exc()) + count[0] = 0 + return None + + def _get_flag_role(self, ctxt, flag): + try: + if flag in self._flag_roles: + return self._flag_roles[flag] + return core.BNFlagRole.SpecialFlagRole + except KeyError: + log.log_error(traceback.format_exc()) + return None + + def _get_flags_required_for_flag_condition(self, ctxt, cond, count): + try: + if cond in self._flags_required_for_flag_condition: + flags = self._flags_required_for_flag_condition[cond] + else: + flags = [] + count[0] = len(flags) + flag_buf = (ctypes.c_uint * len(flags))() + for i in xrange(0, len(flags)): + flag_buf[i] = flags[i] + result = ctypes.cast(flag_buf, ctypes.c_void_p) + self._pending_reg_lists[result.value] = (result, flag_buf) + return result.value + except KeyError: + log.log_error(traceback.format_exc()) + count[0] = 0 + return None + + def _get_flags_written_by_flag_write_type(self, ctxt, write_type, count): + try: + if write_type in self._flags_written_by_flag_write_type: + flags = self._flags_written_by_flag_write_type[write_type] + else: + flags = [] + count[0] = len(flags) + flag_buf = (ctypes.c_uint * len(flags))() + for i in xrange(0, len(flags)): + flag_buf[i] = flags[i] + result = ctypes.cast(flag_buf, ctypes.c_void_p) + self._pending_reg_lists[result.value] = (result, flag_buf) + return result.value + except (KeyError, OSError): + log.log_error(traceback.format_exc()) + count[0] = 0 + return None + + def _get_flag_write_low_level_il(self, ctxt, op, size, write_type, flag, operands, operand_count, il): + try: + write_type_name = None + if write_type != 0: + write_type_name = self._flag_write_types_by_index[write_type] + flag_name = self._flags_by_index[flag] + operand_list = [] + for i in xrange(operand_count): + if operands[i].constant: + operand_list.append(("const", operands[i].value)) + elif lowlevelil.LLIL_REG_IS_TEMP(operands[i].reg): + operand_list.append(("reg", operands[i].reg)) + else: + operand_list.append(("reg", self._regs_by_index[operands[i].reg])) + return self.perform_get_flag_write_low_level_il(op, size, write_type_name, flag_name, operand_list, + lowlevelil.LowLevelILFunction(self, core.BNNewLowLevelILFunctionReference(il))).index + except (KeyError, OSError): + log.log_error(traceback.format_exc()) + return False + + def _get_flag_condition_low_level_il(self, ctxt, cond, il): + try: + return self.perform_get_flag_condition_low_level_il(cond, + lowlevelil.LowLevelILFunction(self, core.BNNewLowLevelILFunctionReference(il))).index + except OSError: + log.log_error(traceback.format_exc()) + return 0 + + def _free_register_list(self, ctxt, regs): + try: + buf = ctypes.cast(regs, ctypes.c_void_p) + if buf.value not in self._pending_reg_lists: + raise ValueError("freeing register list that wasn't allocated") + del self._pending_reg_lists[buf.value] + except (ValueError, KeyError): + log.log_error(traceback.format_exc()) + + def _get_register_info(self, ctxt, reg, result): + try: + if reg not in self._regs_by_index: + result[0].fullWidthRegister = 0 + result[0].offset = 0 + result[0].size = 0 + result[0].extend = core.BNImplicitRegisterExtend.NoExtend + return + info = self.__class__.regs[self._regs_by_index[reg]] + result[0].fullWidthRegister = self._all_regs[info.full_width_reg] + result[0].offset = info.offset + result[0].size = info.size + if isinstance(info.extend, str): + result[0].extend = core.BNImplicitRegisterExtend[info.extend] + else: + result[0].extend = info.extend + except KeyError: + log.log_error(traceback.format_exc()) + result[0].fullWidthRegister = 0 + result[0].offset = 0 + result[0].size = 0 + result[0].extend = core.BNImplicitRegisterExtend.NoExtend + + def _get_stack_pointer_register(self, ctxt): + try: + return self._all_regs[self.__class__.stack_pointer] + except KeyError: + log.log_error(traceback.format_exc()) + return 0 + + def _get_link_register(self, ctxt): + try: + if self.__class__.link_reg is None: + return 0xffffffff + return self._all_regs[self.__class__.link_reg] + except KeyError: + log.log_error(traceback.format_exc()) + return 0 + + def _assemble(self, ctxt, code, addr, result, errors): + try: + data, error_str = self.perform_assemble(code, addr) + errors[0] = core.BNAllocString(str(error_str)) + if data is None: + return False + data = str(data) + buf = ctypes.create_string_buffer(len(data)) + ctypes.memmove(buf, data, len(data)) + core.BNSetDataBufferContents(result, buf, len(data)) + return True + except: + log.log_error(traceback.format_exc()) + errors[0] = core.BNAllocString("Unhandled exception during assembly.\n") + return False + + def _is_never_branch_patch_available(self, ctxt, data, addr, length): + try: + buf = ctypes.create_string_buffer(length) + ctypes.memmove(buf, data, length) + return self.perform_is_never_branch_patch_available(buf.raw, addr) + except: + log.log_error(traceback.format_exc()) + return False + + def _is_always_branch_patch_available(self, ctxt, data, addr, length): + try: + buf = ctypes.create_string_buffer(length) + ctypes.memmove(buf, data, length) + return self.perform_is_always_branch_patch_available(buf.raw, addr) + except: + log.log_error(traceback.format_exc()) + return False + + def _is_invert_branch_patch_available(self, ctxt, data, addr, length): + try: + buf = ctypes.create_string_buffer(length) + ctypes.memmove(buf, data, length) + return self.perform_is_invert_branch_patch_available(buf.raw, addr) + except: + log.log_error(traceback.format_exc()) + return False + + def _is_skip_and_return_zero_patch_available(self, ctxt, data, addr, length): + try: + buf = ctypes.create_string_buffer(length) + ctypes.memmove(buf, data, length) + return self.perform_is_skip_and_return_zero_patch_available(buf.raw, addr) + except: + log.log_error(traceback.format_exc()) + return False + + def _is_skip_and_return_value_patch_available(self, ctxt, data, addr, length): + try: + buf = ctypes.create_string_buffer(length) + ctypes.memmove(buf, data, length) + return self.perform_is_skip_and_return_value_patch_available(buf.raw, addr) + except: + log.log_error(traceback.format_exc()) + return False + + def _convert_to_nop(self, ctxt, data, addr, length): + try: + buf = ctypes.create_string_buffer(length) + ctypes.memmove(buf, data, length) + result = self.perform_convert_to_nop(buf.raw, addr) + if result is None: + return False + result = str(result) + if len(result) > length: + result = result[0:length] + ctypes.memmove(data, result, len(result)) + return True + except: + log.log_error(traceback.format_exc()) + return False + + def _always_branch(self, ctxt, data, addr, length): + try: + buf = ctypes.create_string_buffer(length) + ctypes.memmove(buf, data, length) + result = self.perform_always_branch(buf.raw, addr) + if result is None: + return False + result = str(result) + if len(result) > length: + result = result[0:length] + ctypes.memmove(data, result, len(result)) + return True + except: + log.log_error(traceback.format_exc()) + return False + + def _invert_branch(self, ctxt, data, addr, length): + try: + buf = ctypes.create_string_buffer(length) + ctypes.memmove(buf, data, length) + result = self.perform_invert_branch(buf.raw, addr) + if result is None: + return False + result = str(result) + if len(result) > length: + result = result[0:length] + ctypes.memmove(data, result, len(result)) + return True + except: + log.log_error(traceback.format_exc()) + return False + + def _skip_and_return_value(self, ctxt, data, addr, length, value): + try: + buf = ctypes.create_string_buffer(length) + ctypes.memmove(buf, data, length) + result = self.perform_skip_and_return_value(buf.raw, addr, value) + if result is None: + return False + result = str(result) + if len(result) > length: + result = result[0:length] + ctypes.memmove(data, result, len(result)) + return True + except: + log.log_error(traceback.format_exc()) + return False + + @abc.abstractmethod + def perform_get_instruction_info(self, data, addr): + """ + ``perform_get_instruction_info`` implements a method which interpretes the bytes passed in ``data`` as an + :py:Class:`InstructionInfo` object. The InstructionInfo object should have the length of the current instruction. + If the instruction is a branch instruction the method should add a branch of the proper type: + + ===================== =================================================== + BranchType Description + ===================== =================================================== + UnconditionalBranch Branch will always be taken + FalseBranch False branch condition + TrueBranch True branch condition + CallDestination Branch is a call instruction (Branch with Link) + FunctionReturn Branch returns from a function + SystemCall System call instruction + IndirectBranch Branch destination is a memory address or register + UnresolvedBranch Call instruction that isn't + ===================== =================================================== + + :param str data: bytes to decode + :param int addr: virtual address of the byte to be decoded + :return: a :py:class:`InstructionInfo` object containing the length and branche types for the given instruction + :rtype: InstructionInfo + """ + raise NotImplementedError + + @abc.abstractmethod + def perform_get_instruction_text(self, data, addr): + """ + ``perform_get_instruction_text`` implements a method which interpretes the bytes passed in ``data`` as a + list of :py:class:`InstructionTextToken` objects. + + :param str data: bytes to decode + :param int addr: virtual address of the byte to be decoded + :return: a tuple of list(InstructionTextToken) and length of instruction decoded + :rtype: tuple(list(InstructionTextToken), int) + """ + raise NotImplementedError + + @abc.abstractmethod + def perform_get_instruction_low_level_il(self, data, addr, il): + """ + ``perform_get_instruction_low_level_il`` implements a method to interpret the bytes passed in ``data`` to + low-level IL instructions. The il instructions must be appended to the :py:class:`LowLevelILFunction`. + + .. note:: Architecture subclasses should implement this method. + + :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 + """ + raise NotImplementedError + + @abc.abstractmethod + def perform_get_flag_write_low_level_il(self, op, size, write_type, flag, operands, il): + """ + .. note:: Architecture subclasses should implement this method. + .. warning:: This method should never be called directly. + + :param LowLevelILOperation op: + :param int size: + :param int write_type: + :param int flag: + :param list(int_or_str): + :param LowLevelILFunction il: + :rtype: LowLevelILExpr + """ + return il.unimplemented() + + @abc.abstractmethod + def perform_get_flag_condition_low_level_il(self, cond, il): + """ + .. note:: Architecture subclasses should implement this method. + .. warning:: This method should never be called directly. + + :param LowLevelILFlagCondition cond: + :param LowLevelILFunction il: + :rtype: LowLevelILExpr + """ + return il.unimplemented() + + @abc.abstractmethod + def perform_assemble(self, code, addr): + """ + ``perform_assemble`` implements a method to convert the string of assembly instructions ``code`` loaded at + virtual address ``addr`` to the byte representation of those instructions. This can be done by simply shelling + out to an assembler like yasm or llvm-mc, since this method isn't performance sensitive. + + .. note:: Architecture subclasses should implement this method. + .. note :: It is important that the assembler used accepts a syntax identical to the one emitted by the \ + disassembler. This will prevent confusing the user. + .. warning:: This method should never be called directly. + + :param str code: string representation of the instructions to be assembled + :param int addr: virtual address that the instructions will be loaded at + :return: the bytes for the assembled instructions or error string + :rtype: (a tuple of instructions and empty string) or (or None and error string) + """ + return None, "Architecture does not implement an assembler.\n" + + @abc.abstractmethod + def perform_is_never_branch_patch_available(self, data, addr): + """ + ``perform_is_never_branch_patch_available`` implements a check to determine if the instruction represented by + the bytes contained in ``data`` at address addr is a branch instruction that can be made to never branch. + + .. note:: Architecture subclasses should implement this method. + .. warning:: This method should never be called directly. + + :param str data: bytes to be checked + :param int addr: the virtual address of the instruction to be patched + :return: True if the instruction can be patched, False otherwise + :rtype: bool + """ + return False + + @abc.abstractmethod + def perform_is_always_branch_patch_available(self, data, addr): + """ + ``perform_is_always_branch_patch_available`` implements a check to determine if the instruction represented by + the bytes contained in ``data`` at address addr is a conditional branch that can be made unconditional. + + .. note:: Architecture subclasses should implement this method. + .. warning:: This method should never be called directly. + + :param str data: bytes to be checked + :param int addr: the virtual address of the instruction to be patched + :return: True if the instruction can be patched, False otherwise + :rtype: bool + """ + return False + + @abc.abstractmethod + def perform_is_invert_branch_patch_available(self, data, addr): + """ + ``perform_is_invert_branch_patch_available`` implements a check to determine if the instruction represented by + the bytes contained in ``data`` at address addr is a conditional branch which can be inverted. + + .. note:: Architecture subclasses should implement this method. + .. warning:: This method should never be called directly. + + :param int addr: the virtual address of the instruction to be patched + :return: True if the instruction can be patched, False otherwise + :rtype: bool + """ + return False + + @abc.abstractmethod + def perform_is_skip_and_return_zero_patch_available(self, data, addr): + """ + ``perform_is_skip_and_return_zero_patch_available`` implements a check to determine if the instruction represented by + the bytes contained in ``data`` at address addr is a *call-like* instruction which can made into instructions + that are equivilent to "return 0". For example if ``data`` was the x86 instruction ``call eax`` which could be + converted into ``xor eax,eax`` thus this function would return True. + + .. note:: Architecture subclasses should implement this method. + .. warning:: This method should never be called directly. + + :param str data: bytes to be checked + :param int addr: the virtual address of the instruction to be patched + :return: True if the instruction can be patched, False otherwise + :rtype: bool + """ + return False + + @abc.abstractmethod + def perform_is_skip_and_return_value_patch_available(self, data, addr): + """ + ``perform_is_skip_and_return_value_patch_available`` implements a check to determine if the instruction represented by + the bytes contained in ``data`` at address addr is a *call-like* instruction which can made into instructions + that are equivilent to "return 0". For example if ``data`` was the x86 instruction ``call 0xdeadbeef`` which could be + converted into ``mov eax, 42`` thus this function would return True. + + .. note:: Architecture subclasses should implement this method. + .. warning:: This method should never be called directly. + + :param str data: bytes to be checked + :param int addr: the virtual address of the instruction to be patched + :return: True if the instruction can be patched, False otherwise + :rtype: bool + """ + return False + + @abc.abstractmethod + def perform_convert_to_nop(self, data, addr): + """ + ``perform_convert_to_nop`` implements a method which returns a nop sequence of len(data) bytes long. + + .. note:: Architecture subclasses should implement this method. + .. warning:: This method should never be called directly. + + :param str data: bytes at virtual address ``addr`` + :param int addr: the virtual address of the instruction to be patched + :return: nop sequence of same length as ``data`` or None + :rtype: str or None + """ + return None + + @abc.abstractmethod + def perform_always_branch(self, data, addr): + """ + ``perform_always_branch`` implements a method which converts the branch represented by the bytes in ``data`` to + at ``addr`` to an unconditional branch. + + .. note:: Architecture subclasses should implement this method. + .. warning:: This method should never be called directly. + + :param str data: bytes to be checked + :param int addr: the virtual address of the instruction to be patched + :return: The bytes of the replacement unconditional branch instruction + :rtype: str + """ + return None + + @abc.abstractmethod + def perform_invert_branch(self, data, addr): + """ + ``perform_invert_branch`` implements a method which inverts the branch represented by the bytes in ``data`` to + at ``addr``. + + .. note:: Architecture subclasses should implement this method. + .. warning:: This method should never be called directly. + + :param str data: bytes to be checked + :param int addr: the virtual address of the instruction to be patched + :return: The bytes of the replacement unconditional branch instruction + :rtype: str + """ + return None + + @abc.abstractmethod + def perform_skip_and_return_value(self, data, addr, value): + """ + ``perform_skip_and_return_value`` implements a method which converts a *call-like* instruction represented by + the bytes in ``data`` at ``addr`` to one or more instructions that are equivilent to a function returning a + value. + + .. note:: Architecture subclasses should implement this method. + .. warning:: This method should never be called directly. + + :param str data: bytes to be checked + :param int addr: the virtual address of the instruction to be patched + :param int value: value to be returned + :return: The bytes of the replacement unconditional branch instruction + :rtype: str + """ + return None + + def get_instruction_info(self, data, addr): + """ + ``get_instruction_info`` returns an InstructionInfo object for the instruction at the given virtual address + ``addr`` with data ``data``. + + .. note :: The instruction info object should always set the InstructionInfo.length to the instruction length, \ + and the branches of the proper types shoulde be added if the instruction is a branch. + + :param str data: max_instruction_length bytes from the binary at virtual address ``addr`` + :param int addr: virtual address of bytes in ``data`` + :return: the InstructionInfo for the current instruction + :rtype: InstructionInfo + """ + info = core.BNInstructionInfo() + data = str(data) + buf = (ctypes.c_ubyte * len(data))() + ctypes.memmove(buf, data, len(data)) + if not core.BNGetInstructionInfo(self.handle, buf, addr, len(data), info): + return None + result = function.InstructionInfo() + result.length = info.length + result.branch_delay = info.branchDelay + for i in xrange(0, info.branchCount): + branch_type = core.BNBranchType(info.branchType[i]).name + target = info.branchTarget[i] + if info.branchArch[i]: + arch = Architecture(info.branchArch[i]) + else: + arch = None + result.add_branch(branch_type, target, arch) + return result + + def get_instruction_text(self, data, addr): + """ + ``get_instruction_text`` returns a list of InstructionTextToken objects 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`` + :return: an InstructionTextToken list for the current instruction + :rtype: list(InstructionTextToken) + """ + data = str(data) + count = ctypes.c_ulonglong() + length = ctypes.c_ulonglong() + length.value = len(data) + buf = (ctypes.c_ubyte * len(data))() + ctypes.memmove(buf, data, len(data)) + tokens = ctypes.POINTER(core.BNInstructionTextToken)() + if not core.BNGetInstructionText(self.handle, buf, addr, length, tokens, count): + return None, 0 + result = [] + for i in xrange(0, count.value): + token_type = core.BNInstructionTextTokenType(tokens[i].type).name + text = tokens[i].text + value = tokens[i].value + size = tokens[i].size + operand = tokens[i].operand + result.append(function.InstructionTextToken(token_type, text, value, size, operand)) + core.BNFreeInstructionText(tokens, count.value) + return result, length.value + + 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``. + + :param str data: max_instruction_length bytes from the binary at virtual address ``addr`` + :param int addr: virtual address of bytes in ``data`` + :param LowLevelILFunction il: The function the current instruction belongs to + :return: the length of the current instruction + :rtype: int + """ + data = str(data) + length = ctypes.c_ulonglong() + length.value = len(data) + buf = (ctypes.c_ubyte * len(data))() + ctypes.memmove(buf, data, len(data)) + core.BNGetInstructionLowLevelIL(self.handle, buf, addr, length, il.handle) + return length.value + + def get_reg_name(self, reg): + """ + ``get_reg_name`` gets a register name from a register number. + + :param int reg: register number + :return: the corresponding register string + :rtype: str + """ + return core.BNGetArchitectureRegisterName(self.handle, reg) + + def get_flag_name(self, flag): + """ + ``get_flag_name`` gets a flag name from a flag number. + + :param int reg: register number + :return: the corresponding register string + :rtype: str + """ + return core.BNGetArchitectureFlagName(self.handle, flag) + + def get_flag_write_type_name(self, write_type): + """ + ``get_flag_write_type_name`` gets the flag write type name for the given flag. + + :param int write_type: flag + :return: flag write type name + :rtype: str + """ + return core.BNGetArchitectureFlagWriteTypeName(self.handle, write_type) + + def get_flag_by_name(self, flag): + """ + ``get_flag_by_name`` get flag name for flag index. + + :param int flag: flag index + :return: flag name for flag index + :rtype: str + """ + return self._flags[flag] + + def get_flag_write_type_by_name(self, write_type): + """ + ``get_flag_write_type_by_name`` gets the flag write type name for the flage write type. + + :param int write_type: flag write type + :return: flag write type + :rtype: str + """ + return self._flag_write_types[write_type] + + def get_flag_write_low_level_il(self, op, size, write_type, operands, il): + """ + :param LowLevelILOperation op: + :param int size: + :param str write_type: + :param list(str or int) operands: a list of either items that are either string register names or constant \ + integer values + :param LowLevelILFunction il: + :rtype: LowLevelILExpr + """ + operand_list = (core.BNRegisterOrConstant * len(operands))() + for i in xrange(len(operands)): + if isinstance(operands[i], str): + operand_list[i].constant = False + operand_list[i].reg = self._flags[operands[i]] + else: + operand_list[i].constant = True + operand_list[i].value = operands[i] + return lowlevelil.LowLevelILExpr(core.BNGetArchitectureFlagWriteLowLevelIL(self.handle, op, size, + self._flag_write_types[write_type], operand_list, len(operand_list), il.handle)) + + def get_default_flag_write_low_level_il(self, op, size, write_type, operands, il): + """ + :param LowLevelILOperation op: + :param int size: + :param str write_type: + :param list(str or int) operands: a list of either items that are either string register names or constant \ + integer values + :param LowLevelILFunction il: + :rtype: LowLevelILExpr index + """ + operand_list = (core.BNRegisterOrConstant * len(operands))() + for i in xrange(len(operands)): + if isinstance(operands[i], str): + operand_list[i].constant = False + operand_list[i].reg = self._flags[operands[i]] + else: + operand_list[i].constant = True + operand_list[i].value = operands[i] + return lowlevelil.LowLevelILExpr(core.BNGetDefaultArchitectureFlagWriteLowLevelIL(self.handle, op, size, + self._flag_write_types[write_type], operand_list, len(operand_list), il.handle)) + + def get_flag_condition_low_level_il(self, cond, il): + """ + :param LowLevelILFlagCondition cond: + :param LowLevelILFunction il: + :rtype: LowLevelILExpr + """ + return lowlevelil.LowLevelILExpr(core.BNGetArchitectureFlagConditionLowLevelIL(self.handle, cond, il.handle)) + + def get_modified_regs_on_write(self, reg): + """ + ``get_modified_regs_on_write`` returns a list of register names that are modified when ``reg`` is written. + + :param str reg: string register name + :return: list of register names + :rtype: list(str) + """ + reg = core.BNGetArchitectureRegisterByName(self.handle, str(reg)) + count = ctypes.c_ulonglong() + regs = core.BNGetModifiedArchitectureRegistersOnWrite(self.handle, reg, count) + result = [] + for i in xrange(0, count.value): + result.append(core.BNGetArchitectureRegisterName(self.handle, regs[i])) + core.BNFreeRegisterList(regs) + return result + + def assemble(self, code, addr=0): + """ + ``assemble`` converts the string of assembly instructions ``code`` loaded at virtual address ``addr`` to the + byte representation of those instructions. + + :param str code: string representation of the instructions to be assembled + :param int addr: virtual address that the instructions will be loaded at + :return: the bytes for the assembled instructions or error string + :rtype: (a tuple of instructions and empty string) or (or None and error string) + :Example: + + >>> arch.assemble("je 10") + ('\\x0f\\x84\\x04\\x00\\x00\\x00', '') + >>> + """ + result = databuffer.DataBuffer() + errors = ctypes.c_char_p() + if not core.BNAssemble(self.handle, code, addr, result.handle, errors): + return None, errors.value + return str(result), errors.value + + def is_never_branch_patch_available(self, data, addr): + """ + ``is_never_branch_patch_available`` determines if the instruction ``data`` at ``addr`` can be made to **never branch**. + + :param str data: bytes for the instruction to be checked + :param int addr: the virtual address of the instruction to be patched + :return: True if the instruction can be patched, False otherwise + :rtype: bool + :Example: + + >>> arch.is_never_branch_patch_available(arch.assemble("je 10")[0], 0) + True + >>> arch.is_never_branch_patch_available(arch.assemble("nop")[0], 0) + False + >>> + """ + data = str(data) + buf = (ctypes.c_ubyte * len(data))() + ctypes.memmove(buf, data, len(data)) + return core.BNIsArchitectureNeverBranchPatchAvailable(self.handle, buf, addr, len(data)) + + def is_always_branch_patch_available(self, data, addr): + """ + ``is_always_branch_patch_available`` determines if the instruction ``data`` at ``addr`` can be made to + **always branch**. + + :param str data: bytes for the instruction to be checked + :param int addr: the virtual address of the instruction to be patched + :return: True if the instruction can be patched, False otherwise + :rtype: bool + :Example: + + >>> arch.is_always_branch_patch_available(arch.assemble("je 10")[0], 0) + True + >>> arch.is_always_branch_patch_available(arch.assemble("nop")[0], 0) + False + >>> + """ + data = str(data) + buf = (ctypes.c_ubyte * len(data))() + ctypes.memmove(buf, data, len(data)) + return core.BNIsArchitectureAlwaysBranchPatchAvailable(self.handle, buf, addr, len(data)) + + def is_invert_branch_patch_available(self, data, addr): + """ + ``is_always_branch_patch_available`` determines if the instruction ``data`` at ``addr`` can be inverted. + + :param str data: bytes for the instruction to be checked + :param int addr: the virtual address of the instruction to be patched + :return: True if the instruction can be patched, False otherwise + :rtype: bool + :Example: + + >>> arch.is_invert_branch_patch_available(arch.assemble("je 10")[0], 0) + True + >>> arch.is_invert_branch_patch_available(arch.assemble("nop")[0], 0) + False + >>> + """ + data = str(data) + buf = (ctypes.c_ubyte * len(data))() + ctypes.memmove(buf, data, len(data)) + return core.BNIsArchitectureInvertBranchPatchAvailable(self.handle, buf, addr, len(data)) + + def is_skip_and_return_zero_patch_available(self, data, addr): + """ + ``is_skip_and_return_zero_patch_available`` determines if the instruction ``data`` at ``addr`` is a *call-like* + instruction that can be made into an instruction *returns zero*. + + :param str data: bytes for the instruction to be checked + :param int addr: the virtual address of the instruction to be patched + :return: True if the instruction can be patched, False otherwise + :rtype: bool + :Example: + + >>> arch.is_skip_and_return_zero_patch_available(arch.assemble("call 0")[0], 0) + True + >>> arch.is_skip_and_return_zero_patch_available(arch.assemble("call eax")[0], 0) + True + >>> arch.is_skip_and_return_zero_patch_available(arch.assemble("jmp eax")[0], 0) + False + >>> + """ + data = str(data) + buf = (ctypes.c_ubyte * len(data))() + ctypes.memmove(buf, data, len(data)) + return core.BNIsArchitectureSkipAndReturnZeroPatchAvailable(self.handle, buf, addr, len(data)) + + def is_skip_and_return_value_patch_available(self, data, addr): + """ + ``is_skip_and_return_zero_patch_available`` determines if the instruction ``data`` at ``addr`` is a *call-like* + instruction that can be made into an instruction *returns a value*. + + :param str data: bytes for the instruction to be checked + :param int addr: the virtual address of the instruction to be patched + :return: True if the instruction can be patched, False otherwise + :rtype: bool + :Example: + + >>> arch.is_skip_and_return_zero_patch_available(arch.assemble("call 0")[0], 0) + True + >>> arch.is_skip_and_return_zero_patch_available(arch.assemble("jmp eax")[0], 0) + False + >>> + """ + data = str(data) + buf = (ctypes.c_ubyte * len(data))() + ctypes.memmove(buf, data, len(data)) + return core.BNIsArchitectureSkipAndReturnValuePatchAvailable(self.handle, buf, addr, len(data)) + + def convert_to_nop(self, data, addr): + """ + ``convert_to_nop`` reads the instruction(s) in ``data`` at virtual address ``addr`` and returns a string of nop + instructions of the same length as data. + + :param str data: bytes for the instruction to be converted + :param int addr: the virtual address of the instruction to be patched + :return: string containing len(data) worth of no-operation instructions + :rtype: str + :Example: + + >>> arch.convert_to_nop("\\x00\\x00", 0) + '\\x90\\x90' + >>> + """ + data = str(data) + buf = (ctypes.c_ubyte * len(data))() + ctypes.memmove(buf, data, len(data)) + if not core.BNArchitectureConvertToNop(self.handle, buf, addr, len(data)): + return None + result = ctypes.create_string_buffer(len(data)) + ctypes.memmove(result, buf, len(data)) + return result.raw + + def always_branch(self, data, addr): + """ + ``always_branch`` reads the instruction(s) in ``data`` at virtual address ``addr`` and returns a string of bytes + of the same length which always branches. + + :param str data: bytes for the instruction to be converted + :param int addr: the virtual address of the instruction to be patched + :return: string containing len(data) which always branches to the same location as the provided instruction + :rtype: str + :Example: + + >>> bytes = arch.always_branch(arch.assemble("je 10")[0], 0) + >>> arch.get_instruction_text(bytes, 0) + (['nop '], 1L) + >>> arch.get_instruction_text(bytes[1:], 0) + (['jmp ', '0x9'], 5L) + >>> + """ + data = str(data) + buf = (ctypes.c_ubyte * len(data))() + ctypes.memmove(buf, data, len(data)) + if not core.BNArchitectureAlwaysBranch(self.handle, buf, addr, len(data)): + return None + result = ctypes.create_string_buffer(len(data)) + ctypes.memmove(result, buf, len(data)) + return result.raw + + def invert_branch(self, data, addr): + """ + ``invert_branch`` reads the instruction(s) in ``data`` at virtual address ``addr`` and returns a string of bytes + of the same length which inverts the branch of provided instruction. + + :param str data: bytes for the instruction to be converted + :param int addr: the virtual address of the instruction to be patched + :return: string containing len(data) which always branches to the same location as the provided instruction + :rtype: str + :Example: + + >>> arch.get_instruction_text(arch.invert_branch(arch.assemble("je 10")[0], 0), 0) + (['jne ', '0xa'], 6L) + >>> arch.get_instruction_text(arch.invert_branch(arch.assemble("jo 10")[0], 0), 0) + (['jno ', '0xa'], 6L) + >>> arch.get_instruction_text(arch.invert_branch(arch.assemble("jge 10")[0], 0), 0) + (['jl ', '0xa'], 6L) + >>> + """ + data = str(data) + buf = (ctypes.c_ubyte * len(data))() + ctypes.memmove(buf, data, len(data)) + if not core.BNArchitectureInvertBranch(self.handle, buf, addr, len(data)): + return None + result = ctypes.create_string_buffer(len(data)) + ctypes.memmove(result, buf, len(data)) + return result.raw + + def skip_and_return_value(self, data, addr, value): + """ + ``skip_and_return_value`` reads the instruction(s) in ``data`` at virtual address ``addr`` and returns a string of + bytes of the same length which doesn't call and instead *return a value*. + + :param str data: bytes for the instruction to be converted + :param int addr: the virtual address of the instruction to be patched + :return: string containing len(data) which always branches to the same location as the provided instruction + :rtype: str + :Example: + + >>> arch.get_instruction_text(arch.skip_and_return_value(arch.assemble("call 10")[0], 0, 0), 0) + (['mov ', 'eax', ', ', '0x0'], 5L) + >>> + """ + data = str(data) + buf = (ctypes.c_ubyte * len(data))() + ctypes.memmove(buf, data, len(data)) + if not core.BNArchitectureSkipAndReturnValue(self.handle, buf, addr, len(data), value): + return None + result = ctypes.create_string_buffer(len(data)) + ctypes.memmove(result, buf, len(data)) + return result.raw + + def is_view_type_constant_defined(self, type_name, const_name): + """ + + :param str type_name: the BinaryView type name of the constant to query + :param str const_name: the constant name to query + :rtype: None + :Example: + + >>> arch.set_view_type_constant("ELF", "R_COPY", ELF_RELOC_COPY) + >>> arch.is_view_type_constant_defined("ELF", "R_COPY") + True + >>> arch.is_view_type_constant_defined("ELF", "NOT_THERE") + False + >>> + """ + return core.BNIsBinaryViewTypeArchitectureConstantDefined(self.handle, type_name, const_name) + + def get_view_type_constant(self, type_name, const_name, default_value=0): + """ + ``get_view_type_constant`` retrieves the view type constant for the given type_name and const_name. + + :param str type_name: the BinaryView type name of the constant to be retrieved + :param str const_name: the constant name to retrieved + :param int value: optional default value if the type_name is not present. default value is zero. + :return: The BinaryView type constant or the default_value if not found + :rtype: int + :Example: + + >>> ELF_RELOC_COPY = 5 + >>> arch.set_view_type_constant("ELF", "R_COPY", ELF_RELOC_COPY) + >>> arch.get_view_type_constant("ELF", "R_COPY") + 5L + >>> arch.get_view_type_constant("ELF", "NOT_HERE", 100) + 100L + """ + return core.BNGetBinaryViewTypeArchitectureConstant(self.handle, type_name, const_name, default_value) + + def set_view_type_constant(self, type_name, const_name, value): + """ + ``set_view_type_constant`` creates a new binaryview type constant. + + :param str type_name: the BinaryView type name of the constant to be registered + :param str const_name: the constant name to register + :param int value: the value of the constant + :rtype: None + :Example: + + >>> ELF_RELOC_COPY = 5 + >>> arch.set_view_type_constant("ELF", "R_COPY", ELF_RELOC_COPY) + >>> + """ + core.BNSetBinaryViewTypeArchitectureConstant(self.handle, type_name, const_name, value) + + def parse_types_from_source(self, source, filename=None, include_dirs=[]): + """ + ``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 + :return: a tuple of py:class:`TypeParserResult` and error string + :rtype: tuple(TypeParserResult,str) + :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)) + error_str = errors.value + core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) + if not result: + return (None, error_str) + types = {} + variables = {} + functions = {} + for i in xrange(0, parse.typeCount): + types[parse.types[i].name] = bntype.Type(core.BNNewTypeReference(parse.types[i].type)) + for i in xrange(0, parse.variableCount): + variables[parse.variables[i].name] = bntype.Type(core.BNNewTypeReference(parse.variables[i].type)) + for i in xrange(0, parse.functionCount): + functions[parse.functions[i].name] = bntype.Type(core.BNNewTypeReference(parse.functions[i].type)) + core.BNFreeTypeParserResult(parse) + return (bntype.TypeParserResult(types, variables, functions), error_str) + + def parse_types_from_source_file(self, filename, include_dirs=[]): + """ + ``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) + :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)) + error_str = errors.value + core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) + if not result: + return (None, error_str) + types = {} + variables = {} + functions = {} + for i in xrange(0, parse.typeCount): + types[parse.types[i].name] = bntype.Type(core.BNNewTypeReference(parse.types[i].type)) + for i in xrange(0, parse.variableCount): + variables[parse.variables[i].name] = bntype.Type(core.BNNewTypeReference(parse.variables[i].type)) + for i in xrange(0, parse.functionCount): + functions[parse.functions[i].name] = bntype.Type(core.BNNewTypeReference(parse.functions[i].type)) + core.BNFreeTypeParserResult(parse) + return (bntype.TypeParserResult(types, variables, functions), error_str) + + def register_calling_convention(self, cc): + """ + ``register_calling_convention`` registers a new calling convention for the Architecture. + + :param CallingConvention cc: CallingConvention object to be registered + :rtype: None + """ + core.BNRegisterCallingConvention(self.handle, cc.handle) + + +class ReferenceSource(object): + def __init__(self, func, arch, addr): + self.function = func + self.arch = arch + self.address = addr + + def __repr__(self): + if self.arch: + return "<ref: %s@%#x>" % (self.arch.name, self.address) + else: + return "<ref: %#x>" % self.address |
