From 277b18a8f7809bc3eef61cecf81cb7859b1308bd Mon Sep 17 00:00:00 2001 From: Rusty Wagner Date: Mon, 10 Oct 2016 16:46:21 -0400 Subject: Rename data to session_data, as this API is per-session and not stored in the db --- python/examples/angr_plugin.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'python/examples') diff --git a/python/examples/angr_plugin.py b/python/examples/angr_plugin.py index 5d567511..c6e6f87d 100644 --- a/python/examples/angr_plugin.py +++ b/python/examples/angr_plugin.py @@ -21,8 +21,8 @@ import os logging.disable(logging.WARNING) # Create sets in the BinaryView's data field to store the desired path for each view -BinaryView.set_default_data("angr_find", set()) -BinaryView.set_default_data("angr_avoid", set()) +BinaryView.set_default_session_data("angr_find", set()) +BinaryView.set_default_session_data("angr_avoid", set()) def escaped_output(str): return '\n'.join([s.encode("string_escape") for s in str.split('\n')]) @@ -89,7 +89,7 @@ def find_instr(bv, addr): block.function.set_auto_instr_highlight(block.arch, addr, GreenHighlightColor) # Add the instruction to the list associated with the current view - bv.data.angr_find.add(addr) + bv.session_data.angr_find.add(addr) def avoid_instr(bv, addr): # Highlight the instruction in red @@ -99,17 +99,17 @@ def avoid_instr(bv, addr): block.function.set_auto_instr_highlight(block.arch, addr, RedHighlightColor) # Add the instruction to the list associated with the current view - bv.data.angr_avoid.add(addr) + bv.session_data.angr_avoid.add(addr) def solve(bv): - if len(bv.data.angr_find) == 0: + if len(bv.session_data.angr_find) == 0: show_message_box("Angr Solve", "You have not specified a goal instruction.\n\n" + "Please right click on the goal instruction and select \"Find Path to This Instruction\" to " + "continue.", OKButtonSet, ErrorIcon) return # Start a solver thread for the path associated with the view - s = Solver(bv.data.angr_find, bv.data.angr_avoid, bv) + s = Solver(bv.session_data.angr_find, bv.session_data.angr_avoid, bv) s.start() # Register commands for the user to interact with the plugin -- cgit v1.3.1 From 1f90dae1d3ba958041862d39fff74e0dcdd9cf63 Mon Sep 17 00:00:00 2001 From: Rusty Wagner Date: Mon, 26 Feb 2018 22:37:19 -0500 Subject: Architecture plugins no longer need to override the perform_* methods (you can now override get_instruction_info, not perform_get_instruction_info). The perform_* methods are now deprecated but will still function as expected. Added architecture hooks to Python API using this new style. --- python/architecture.py | 1763 +++++++++++++++++++++++++----------------- python/basicblock.py | 2 +- python/binaryview.py | 6 +- python/callingconvention.py | 2 +- python/examples/arch_hook.py | 16 + python/examples/nes.py | 28 +- python/function.py | 8 +- python/platform.py | 2 +- 8 files changed, 1091 insertions(+), 736 deletions(-) create mode 100644 python/examples/arch_hook.py (limited to 'python/examples') diff --git a/python/architecture.py b/python/architecture.py index 2b52ea35..781edcfd 100644 --- a/python/architecture.py +++ b/python/architecture.py @@ -44,7 +44,7 @@ class _ArchitectureMetaClass(type): archs = core.BNGetArchitectureList(count) result = [] for i in xrange(0, count.value): - result.append(Architecture(archs[i])) + result.append(CoreArchitecture(archs[i])) core.BNFreeArchitectureList(archs) return result @@ -54,7 +54,7 @@ class _ArchitectureMetaClass(type): archs = core.BNGetArchitectureList(count) try: for i in xrange(0, count.value): - yield Architecture(archs[i]) + yield CoreArchitecture(archs[i]) finally: core.BNFreeArchitectureList(archs) @@ -63,7 +63,7 @@ class _ArchitectureMetaClass(type): arch = core.BNGetArchitectureByName(name) if arch is None: raise KeyError("'%s' is not a valid architecture" % str(name)) - return Architecture(arch) + return CoreArchitecture(arch) def register(cls): startup._init_plugins() @@ -133,438 +133,253 @@ class Architecture(object): __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"] = Endianness(core.BNGetArchitectureEndianness(self.handle)) - self.__dict__["address_size"] = core.BNGetArchitectureAddressSize(self.handle) - self.__dict__["default_int_size"] = core.BNGetArchitectureDefaultIntegerSize(self.handle) - self.__dict__["instr_alignment"] = core.BNGetArchitectureInstructionAlignment(self.handle) - self.__dict__["max_instr_length"] = core.BNGetArchitectureMaxInstructionLength(self.handle) - self.__dict__["opcode_display_length"] = core.BNGetArchitectureOpcodeDisplayLength(self.handle) - self.__dict__["stack_pointer"] = core.BNGetArchitectureRegisterName(self.handle, - 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, - ImplicitRegisterExtend(info.extend), 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() - write_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, write_types[i]) - self._flag_write_types[name] = write_types[i] - self._flag_write_types_by_index[write_types[i]] = name - self.flag_write_types.append(name) - core.BNFreeRegisterList(write_types) - - count = ctypes.c_ulonglong() - sem_classes = core.BNGetAllArchitectureSemanticFlagClasses(self.handle, count) - self._semantic_flag_classes = {} - self._semantic_flag_classes_by_index = {} - self.__dict__["semantic_flag_classes"] = [] - for i in xrange(0, count.value): - name = core.BNGetArchitectureSemanticFlagClassName(self.handle, sem_classes[i]) - self._semantic_flag_classes[name] = sem_classes[i] - self._semantic_flag_classes_by_index[sem_classes[i]] = name - self.semantic_flag_classes.append(name) - core.BNFreeRegisterList(sem_classes) + def __init__(self): + startup._init_plugins() - count = ctypes.c_ulonglong() - sem_groups = core.BNGetAllArchitectureSemanticFlagGroups(self.handle, count) - self._semantic_flag_groups = {} - self._semantic_flag_groups_by_index = {} - self.__dict__["semantic_flag_groups"] = [] - for i in xrange(0, count.value): - name = core.BNGetArchitectureSemanticFlagGroupName(self.handle, sem_groups[i]) - self._semantic_flag_groups[name] = sem_groups[i] - self._semantic_flag_groups_by_index[sem_groups[i]] = name - self.semantic_flag_groups.append(name) - core.BNFreeRegisterList(sem_groups) - - self._flag_roles = {} - self.__dict__["flag_roles"] = {} - for flag in self.__dict__["flags"]: - role = FlagRole(core.BNGetArchitectureFlagRole(self.handle, self._flags[flag], 0)) - self.__dict__["flag_roles"][flag] = role - self._flag_roles[self._flags[flag]] = role - - self.__dict__["flags_required_for_flag_condition"] = {} - for cond in LowLevelILFlagCondition: - count = ctypes.c_ulonglong() - flags = core.BNGetArchitectureFlagsRequiredForFlagCondition(self.handle, cond, 0, count) - flag_names = [] - for i in xrange(0, count.value): - flag_names.append(self._flags_by_index[flags[i]]) - core.BNFreeRegisterList(flags) - self.__dict__["flags_required_for_flag_condition"][cond] = flag_names - - self._flags_required_by_semantic_flag_group = {} - self.__dict__["flags_required_for_semantic_flag_group"] = {} - for group in self.semantic_flag_groups: - count = ctypes.c_ulonglong() - flags = core.BNGetArchitectureFlagsRequiredForSemanticFlagGroup(self.handle, - self._semantic_flag_groups[group], 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_by_semantic_flag_group[self._semantic_flag_groups[group]] = flag_indexes - self.__dict__["flags_required_for_semantic_flag_group"][cond] = flag_names - - self._flag_conditions_for_semantic_flag_group = {} - self.__dict__["flag_conditions_for_semantic_flag_group"] = {} - for group in self.semantic_flag_groups: - count = ctypes.c_ulonglong() - conditions = core.BNGetArchitectureFlagConditionsForSemanticFlagGroup(self.handle, - self._semantic_flag_groups[group], count) - class_index_cond = {} - class_cond = {} - for i in xrange(0, count.value): - class_index_cond[conditions[i].semanticClass] = conditions[i].condition - if conditions[i].semanticClass == 0: - class_cond[None] = conditions[i].condition - elif conditions[i].semanticClass in self._semantic_flag_classes_by_index: - class_cond[self._semantic_flag_classes_by_index[conditions[i].semanticClass]] = conditions[i].condition - core.BNFreeFlagConditionsForSemanticFlagGroup(conditions) - self._flag_conditions_for_semantic_flag_group[self._semantic_flag_groups[group]] = class_index_cond - self.__dict__["flag_conditions_for_semantic_flag_group"][group] = class_cond - - 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 - - self._semantic_class_for_flag_write_type = {} - self.__dict__["semantic_class_for_flag_write_type"] = {} - for write_type in self.flag_write_types: - sem_class = core.BNGetArchitectureSemanticClassForFlagWriteType(self.handle, - self._flag_write_types[write_type]) - if sem_class == 0: - sem_class_name = None + 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.getInstructionAlignment = self._cb.getInstructionAlignment.__class__(self._get_instruction_alignment) + self._cb.getMaxInstructionLength = self._cb.getMaxInstructionLength.__class__(self._get_max_instruction_length) + self._cb.getOpcodeDisplayLength = self._cb.getOpcodeDisplayLength.__class__(self._get_opcode_display_length) + self._cb.getAssociatedArchitectureByAddress = \ + self._cb.getAssociatedArchitectureByAddress.__class__(self._get_associated_arch_by_address) + 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.getSemanticFlagClassName = self._cb.getSemanticFlagClassName.__class__(self._get_semantic_flag_class_name) + self._cb.getSemanticFlagGroupName = self._cb.getSemanticFlagGroupName.__class__(self._get_semantic_flag_group_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.getAllSemanticFlagClasses = self._cb.getAllSemanticFlagClasses.__class__(self._get_all_semantic_flag_classes) + self._cb.getAllSemanticFlagGroups = self._cb.getAllSemanticFlagGroups.__class__(self._get_all_semantic_flag_groups) + 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.getFlagsRequiredForSemanticFlagGroup = self._cb.getFlagsRequiredForSemanticFlagGroup.__class__( + self._get_flags_required_for_semantic_flag_group) + self._cb.getFlagConditionsForSemanticFlagGroup = self._cb.getFlagConditionsForSemanticFlagGroup.__class__( + self._get_flag_conditions_for_semantic_flag_group) + self._cb.freeFlagConditionsForSemanticFlagGroup = self._cb.freeFlagConditionsForSemanticFlagGroup.__class__( + self._free_flag_conditions_for_semantic_flag_group) + self._cb.getFlagsWrittenByFlagWriteType = self._cb.getFlagsWrittenByFlagWriteType.__class__( + self._get_flags_written_by_flag_write_type) + self._cb.getSemanticClassForFlagWriteType = self._cb.getSemanticClassForFlagWriteType.__class__( + self._get_semantic_class_for_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.getSemanticFlagGroupLowLevelIL = self._cb.getSemanticFlagGroupLowLevelIL.__class__( + self._get_semantic_flag_group_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.getGlobalRegisters = self._cb.getGlobalRegisters.__class__(self._get_global_registers) + self._cb.getRegisterStackName = self._cb.getRegisterStackName.__class__(self._get_register_stack_name) + self._cb.getAllRegisterStacks = self._cb.getAllRegisterStacks.__class__(self._get_all_register_stacks) + self._cb.getRegisterStackInfo = self._cb.getRegisterStackInfo.__class__(self._get_register_stack_info) + self._cb.getIntrinsicName = self._cb.getIntrinsicName.__class__(self._get_intrinsic_name) + self._cb.getAllIntrinsics = self._cb.getAllIntrinsics.__class__(self._get_all_intrinsics) + self._cb.getIntrinsicInputs = self._cb.getIntrinsicInputs.__class__(self._get_intrinsic_inputs) + self._cb.freeNameAndTypeList = self._cb.freeNameAndTypeList.__class__(self._free_name_and_type_list) + self._cb.getIntrinsicOutputs = self._cb.getIntrinsicOutputs.__class__(self._get_intrinsic_outputs) + self._cb.freeTypeList = self._cb.freeTypeList.__class__(self._free_type_list) + 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.__dict__["endianness"] = self.__class__.endianness + self.__dict__["address_size"] = self.__class__.address_size + self.__dict__["default_int_size"] = self.__class__.default_int_size + self.__dict__["instr_alignment"] = self.__class__.instr_alignment + self.__dict__["max_instr_length"] = self.__class__.max_instr_length + self.__dict__["opcode_display_length"] = self.__class__.opcode_display_length + self.__dict__["stack_pointer"] = self.__class__.stack_pointer + self.__dict__["link_reg"] = self.__class__.link_reg + + self._all_regs = {} + self._full_width_regs = {} + self._regs_by_index = {} + self.__dict__["regs"] = self.__class__.regs + reg_index = 0 + + # Registers used for storage in register stacks must be sequential, so allocate these in order first + self._all_reg_stacks = {} + self._reg_stacks_by_index = {} + self.__dict__["reg_stacks"] = self.__class__.reg_stacks + reg_stack_index = 0 + for reg_stack in self.reg_stacks: + info = self.reg_stacks[reg_stack] + for reg in info.storage_regs: + self._all_regs[reg] = reg_index + self._regs_by_index[reg_index] = reg + self.regs[reg].index = reg_index + reg_index += 1 + for reg in info.top_relative_regs: + self._all_regs[reg] = reg_index + self._regs_by_index[reg_index] = reg + self.regs[reg].index = reg_index + reg_index += 1 + if reg_stack not in self._all_reg_stacks: + self._all_reg_stacks[reg_stack] = reg_stack_index + self._reg_stacks_by_index[reg_stack_index] = reg_stack + self.reg_stacks[reg_stack].index = reg_stack_index + reg_stack_index += 1 + + 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._semantic_flag_classes = {} + self._semantic_flag_classes_by_index = {} + self.__dict__["semantic_flag_classes"] = self.__class__.semantic_flag_classes + semantic_class_index = 1 + for sem_class in self.__class__.semantic_flag_classes: + if sem_class not in self._semantic_flag_classes: + self._semantic_flag_classes[sem_class] = semantic_class_index + self._semantic_flag_classes_by_index[semantic_class_index] = sem_class + semantic_class_index += 1 + + self._semantic_flag_groups = {} + self._semantic_flag_groups_by_index = {} + self.__dict__["semantic_flag_groups"] = self.__class__.semantic_flag_groups + semantic_group_index = 0 + for sem_group in self.__class__.semantic_flag_groups: + if sem_group not in self._semantic_flag_groups: + self._semantic_flag_groups[sem_group] = semantic_group_index + self._semantic_flag_groups_by_index[semantic_group_index] = sem_group + semantic_group_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 = FlagRole[role] + self._flag_roles[self._flags[flag]] = role + + self.__dict__["flags_required_for_flag_condition"] = self.__class__.flags_required_for_flag_condition + + self._flags_required_by_semantic_flag_group = {} + self.__dict__["flags_required_for_semantic_flag_group"] = self.__class__.flags_required_for_semantic_flag_group + for group in self.__class__.flags_required_for_semantic_flag_group: + flags = [] + for flag in self.__class__.flags_required_for_semantic_flag_group[group]: + flags.append(self._flags[flag]) + self._flags_required_by_semantic_flag_group[self._semantic_flag_groups[group]] = flags + + self._flag_conditions_for_semantic_flag_group = {} + self.__dict__["flag_conditions_for_semantic_flag_group"] = self.__class__.flag_conditions_for_semantic_flag_group + for group in self.__class__.flag_conditions_for_semantic_flag_group: + class_cond = {} + for sem_class in self.__class__.flag_conditions_for_semantic_flag_group[group]: + if sem_class is None: + class_cond[0] = self.__class__.flag_conditions_for_semantic_flag_group[group][sem_class] else: - sem_class_name = self._semantic_flag_classes_by_index[sem_class] - self._semantic_class_for_flag_write_type[self._flag_write_types[write_type]] = sem_class - self.__dict__["semantic_class_for_flag_write_type"][write_type] = sem_class_name - - count = ctypes.c_ulonglong() - regs = core.BNGetArchitectureGlobalRegisters(self.handle, count) - self.__dict__["global_regs"] = [] - for i in xrange(0, count.value): - self.global_regs.append(core.BNGetArchitectureRegisterName(self.handle, regs[i])) - core.BNFreeRegisterList(regs) - - count = ctypes.c_ulonglong() - regs = core.BNGetAllArchitectureRegisterStacks(self.handle, count) - self.__dict__["reg_stacks"] = {} - for i in xrange(0, count.value): - name = core.BNGetArchitectureRegisterStackName(self.handle, regs[i]) - info = core.BNGetArchitectureRegisterStackInfo(self.handle, regs[i]) - storage = [] - for j in xrange(0, info.storageCount): - storage.append(core.BNGetArchitectureRegisterName(self.handle, info.firstStorageReg + j)) - top_rel = [] - for j in xrange(0, info.topRelativeCount): - top_rel.append(core.BNGetArchitectureRegisterName(self.handle, info.firstTopRelativeReg + j)) - top = core.BNGetArchitectureRegisterName(self.handle, info.stackTopReg) - self.reg_stacks[name] = function.RegisterStackInfo(storage, top_rel, top, regs[i]) - core.BNFreeRegisterList(regs) + class_cond[self._semantic_flag_classes[sem_class]] = self.__class__.flag_conditions_for_semantic_flag_group[group][sem_class] + self._flag_conditions_for_semantic_flag_group[self._semantic_flag_groups[group]] = class_cond - count = ctypes.c_ulonglong() - intrinsics = core.BNGetAllArchitectureIntrinsics(self.handle, count) - self.__dict__["intrinsics"] = {} - for i in xrange(0, count.value): - name = core.BNGetArchitectureIntrinsicName(self.handle, intrinsics[i]) - input_count = ctypes.c_ulonglong() - inputs = core.BNGetArchitectureIntrinsicInputs(self.handle, intrinsics[i], input_count) - input_list = [] - for j in xrange(0, input_count.value): - input_name = inputs[j].name - type_obj = types.Type(core.BNNewTypeReference(inputs[j].type), confidence = inputs[j].typeConfidence) - input_list.append(function.IntrinsicInput(type_obj, input_name)) - core.BNFreeNameAndTypeList(inputs, input_count.value) - output_count = ctypes.c_ulonglong() - outputs = core.BNGetArchitectureIntrinsicOutputs(self.handle, intrinsics[i], output_count) - output_list = [] - for j in xrange(0, output_count.value): - output_list.append(types.Type(core.BNNewTypeReference(outputs[j].type), confidence = outputs[j].confidence)) - core.BNFreeOutputTypeList(outputs, output_count.value) - self.intrinsics[name] = function.IntrinsicInfo(input_list, output_list) - 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.getInstructionAlignment = self._cb.getInstructionAlignment.__class__(self._get_instruction_alignment) - self._cb.getMaxInstructionLength = self._cb.getMaxInstructionLength.__class__(self._get_max_instruction_length) - self._cb.getOpcodeDisplayLength = self._cb.getOpcodeDisplayLength.__class__(self._get_opcode_display_length) - self._cb.getAssociatedArchitectureByAddress = \ - self._cb.getAssociatedArchitectureByAddress.__class__(self._get_associated_arch_by_address) - 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.getSemanticFlagClassName = self._cb.getSemanticFlagClassName.__class__(self._get_semantic_flag_class_name) - self._cb.getSemanticFlagGroupName = self._cb.getSemanticFlagGroupName.__class__(self._get_semantic_flag_group_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.getAllSemanticFlagClasses = self._cb.getAllSemanticFlagClasses.__class__(self._get_all_semantic_flag_classes) - self._cb.getAllSemanticFlagGroups = self._cb.getAllSemanticFlagGroups.__class__(self._get_all_semantic_flag_groups) - 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.getFlagsRequiredForSemanticFlagGroup = self._cb.getFlagsRequiredForSemanticFlagGroup.__class__( - self._get_flags_required_for_semantic_flag_group) - self._cb.getFlagConditionsForSemanticFlagGroup = self._cb.getFlagConditionsForSemanticFlagGroup.__class__( - self._get_flag_conditions_for_semantic_flag_group) - self._cb.freeFlagConditionsForSemanticFlagGroup = self._cb.freeFlagConditionsForSemanticFlagGroup.__class__( - self._free_flag_conditions_for_semantic_flag_group) - self._cb.getFlagsWrittenByFlagWriteType = self._cb.getFlagsWrittenByFlagWriteType.__class__( - self._get_flags_written_by_flag_write_type) - self._cb.getSemanticClassForFlagWriteType = self._cb.getSemanticClassForFlagWriteType.__class__( - self._get_semantic_class_for_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.getSemanticFlagGroupLowLevelIL = self._cb.getSemanticFlagGroupLowLevelIL.__class__( - self._get_semantic_flag_group_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.getGlobalRegisters = self._cb.getGlobalRegisters.__class__(self._get_global_registers) - self._cb.getRegisterStackName = self._cb.getRegisterStackName.__class__(self._get_register_stack_name) - self._cb.getAllRegisterStacks = self._cb.getAllRegisterStacks.__class__(self._get_all_register_stacks) - self._cb.getRegisterStackInfo = self._cb.getRegisterStackInfo.__class__(self._get_register_stack_info) - self._cb.getIntrinsicName = self._cb.getIntrinsicName.__class__(self._get_intrinsic_name) - self._cb.getAllIntrinsics = self._cb.getAllIntrinsics.__class__(self._get_all_intrinsics) - self._cb.getIntrinsicInputs = self._cb.getIntrinsicInputs.__class__(self._get_intrinsic_inputs) - self._cb.freeNameAndTypeList = self._cb.freeNameAndTypeList.__class__(self._free_name_and_type_list) - self._cb.getIntrinsicOutputs = self._cb.getIntrinsicOutputs.__class__(self._get_intrinsic_outputs) - self._cb.freeTypeList = self._cb.freeTypeList.__class__(self._free_type_list) - 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 - - # Registers used for storage in register stacks must be sequential, so allocate these in order first - self._all_reg_stacks = {} - self._reg_stacks_by_index = {} - self.__dict__["reg_stacks"] = self.__class__.reg_stacks - reg_stack_index = 0 - for reg_stack in self.reg_stacks: - info = self.reg_stacks[reg_stack] - for reg in info.storage_regs: - self._all_regs[reg] = reg_index - self._regs_by_index[reg_index] = reg - self.regs[reg].index = reg_index - reg_index += 1 - for reg in info.top_relative_regs: - self._all_regs[reg] = reg_index - self._regs_by_index[reg_index] = reg - self.regs[reg].index = reg_index - reg_index += 1 - if reg_stack not in self._all_reg_stacks: - self._all_reg_stacks[reg_stack] = reg_stack_index - self._reg_stacks_by_index[reg_stack_index] = reg_stack - self.reg_stacks[reg_stack].index = reg_stack_index - reg_stack_index += 1 - - 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._semantic_flag_classes = {} - self._semantic_flag_classes_by_index = {} - self.__dict__["semantic_flag_classes"] = self.__class__.semantic_flag_classes - semantic_class_index = 1 - for sem_class in self.__class__.semantic_flag_classes: - if sem_class not in self._semantic_flag_classes: - self._semantic_flag_classes[sem_class] = semantic_class_index - self._semantic_flag_classes_by_index[semantic_class_index] = sem_class - semantic_class_index += 1 - - self._semantic_flag_groups = {} - self._semantic_flag_groups_by_index = {} - self.__dict__["semantic_flag_groups"] = self.__class__.semantic_flag_groups - semantic_group_index = 0 - for sem_group in self.__class__.semantic_flag_groups: - if sem_group not in self._semantic_flag_groups: - self._semantic_flag_groups[sem_group] = semantic_group_index - self._semantic_flag_groups_by_index[semantic_group_index] = sem_group - semantic_group_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 = FlagRole[role] - self._flag_roles[self._flags[flag]] = role - - self.__dict__["flags_required_for_flag_condition"] = self.__class__.flags_required_for_flag_condition - - self._flags_required_by_semantic_flag_group = {} - self.__dict__["flags_required_for_semantic_flag_group"] = self.__class__.flags_required_for_semantic_flag_group - for group in self.__class__.flags_required_for_semantic_flag_group: - flags = [] - for flag in self.__class__.flags_required_for_semantic_flag_group[group]: - flags.append(self._flags[flag]) - self._flags_required_by_semantic_flag_group[self._semantic_flag_groups[group]] = flags - - self._flag_conditions_for_semantic_flag_group = {} - self.__dict__["flag_conditions_for_semantic_flag_group"] = self.__class__.flag_conditions_for_semantic_flag_group - for group in self.__class__.flag_conditions_for_semantic_flag_group: - class_cond = {} - for sem_class in self.__class__.flag_conditions_for_semantic_flag_group[group]: - if sem_class is None: - class_cond[0] = self.__class__.flag_conditions_for_semantic_flag_group[group][sem_class] - else: - class_cond[self._semantic_flag_classes[sem_class]] = self.__class__.flag_conditions_for_semantic_flag_group[group][sem_class] - self._flag_conditions_for_semantic_flag_group[self._semantic_flag_groups[group]] = class_cond - - 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._semantic_class_for_flag_write_type = {} - self.__dict__["semantic_class_for_flag_write_type"] = self.__class__.semantic_class_for_flag_write_type - for write_type in self.__class__.semantic_class_for_flag_write_type: - sem_class = self.__class__.semantic_class_for_flag_write_type[write_type] - if sem_class in self._semantic_flag_classes: - sem_class_index = self._semantic_flag_classes[sem_class] - else: - sem_class_index = 0 - self._semantic_class_for_flag_write_type[self._flag_write_types[write_type]] = sem_class_index - - self.__dict__["global_regs"] = self.__class__.global_regs - - self._intrinsics = {} - self._intrinsics_by_index = {} - self.__dict__["intrinsics"] = self.__class__.intrinsics - intrinsic_index = 0 - for intrinsic in self.__class__.intrinsics.keys(): - if intrinsic not in self._intrinsics: - info = self.__class__.intrinsics[intrinsic] - for i in xrange(0, len(info.inputs)): - if isinstance(info.inputs[i], types.Type): - info.inputs[i] = function.IntrinsicInput(info.inputs[i]) - elif isinstance(info.inputs[i], tuple): - info.inputs[i] = function.IntrinsicInput(info.inputs[i][0], info.inputs[i][1]) - info.index = intrinsic_index - self._intrinsics[intrinsic] = intrinsic_index - self._intrinsics_by_index[intrinsic_index] = (intrinsic, info) - intrinsic_index += 1 - - self._pending_reg_lists = {} - self._pending_token_lists = {} - self._pending_condition_lists = {} - self._pending_name_and_type_lists = {} - self._pending_type_lists = {} + 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._semantic_class_for_flag_write_type = {} + self.__dict__["semantic_class_for_flag_write_type"] = self.__class__.semantic_class_for_flag_write_type + for write_type in self.__class__.semantic_class_for_flag_write_type: + sem_class = self.__class__.semantic_class_for_flag_write_type[write_type] + if sem_class in self._semantic_flag_classes: + sem_class_index = self._semantic_flag_classes[sem_class] + else: + sem_class_index = 0 + self._semantic_class_for_flag_write_type[self._flag_write_types[write_type]] = sem_class_index + + self.__dict__["global_regs"] = self.__class__.global_regs + + self._intrinsics = {} + self._intrinsics_by_index = {} + self.__dict__["intrinsics"] = self.__class__.intrinsics + intrinsic_index = 0 + for intrinsic in self.__class__.intrinsics.keys(): + if intrinsic not in self._intrinsics: + info = self.__class__.intrinsics[intrinsic] + for i in xrange(0, len(info.inputs)): + if isinstance(info.inputs[i], types.Type): + info.inputs[i] = function.IntrinsicInput(info.inputs[i]) + elif isinstance(info.inputs[i], tuple): + info.inputs[i] = function.IntrinsicInput(info.inputs[i][0], info.inputs[i][1]) + info.index = intrinsic_index + self._intrinsics[intrinsic] = intrinsic_index + self._intrinsics_by_index[intrinsic_index] = (intrinsic, info) + intrinsic_index += 1 + + self._pending_reg_lists = {} + self._pending_token_lists = {} + self._pending_condition_lists = {} + self._pending_name_and_type_lists = {} + self._pending_type_lists = {} def __eq__(self, value): if not isinstance(value, Architecture): @@ -624,49 +439,49 @@ class Architecture(object): def _get_endianness(self, ctxt): try: - return self.__class__.endianness + return self.endianness except: log.log_error(traceback.format_exc()) return Endianness.LittleEndian def _get_address_size(self, ctxt): try: - return self.__class__.address_size + return self.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 + return self.default_int_size except: log.log_error(traceback.format_exc()) return 4 def _get_instruction_alignment(self, ctxt): try: - return self.__class__.instr_alignment + return self.instr_alignment except: log.log_error(traceback.format_exc()) return 1 def _get_max_instruction_length(self, ctxt): try: - return self.__class__.max_instr_length + return self.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 + return self.opcode_display_length except: log.log_error(traceback.format_exc()) return 8 def _get_associated_arch_by_address(self, ctxt, addr): try: - result, new_addr = self.perform_get_associated_arch_by_address(addr[0]) + result, new_addr = self.get_associated_arch_by_address(addr[0]) addr[0] = new_addr return ctypes.cast(result.handle, ctypes.c_void_p).value except: @@ -677,7 +492,7 @@ class Architecture(object): try: buf = ctypes.create_string_buffer(max_len) ctypes.memmove(buf, data, max_len) - info = self.perform_get_instruction_info(buf.raw, addr) + info = self.get_instruction_info(buf.raw, addr) if info is None: return False result[0].length = info.length @@ -703,7 +518,7 @@ class Architecture(object): try: buf = ctypes.create_string_buffer(length[0]) ctypes.memmove(buf, data, length[0]) - info = self.perform_get_instruction_text(buf.raw, addr) + info = self.get_instruction_text(buf.raw, addr) if info is None: return False tokens = info[0] @@ -743,7 +558,7 @@ class Architecture(object): 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, + result = self.get_instruction_low_level_il(buf.raw, addr, lowlevelil.LowLevelILFunction(self, core.BNNewLowLevelILFunctionReference(il))) if result is None: return False @@ -782,8 +597,8 @@ class Architecture(object): def _get_semantic_flag_class_name(self, ctxt, sem_class): try: - if sem_class in self._semantic_flag_class_by_index: - return core.BNAllocString(self._semantic_flag_class_by_index[sem_class]) + if sem_class in self._semantic_flag_classes_by_index: + return core.BNAllocString(self._semantic_flag_classes_by_index[sem_class]) return core.BNAllocString("") except (KeyError, OSError): log.log_error(traceback.format_exc()) @@ -791,8 +606,8 @@ class Architecture(object): def _get_semantic_flag_group_name(self, ctxt, sem_group): try: - if sem_group in self._semantic_flag_group_by_index: - return core.BNAllocString(self._semantic_flag_group_by_index[sem_group]) + if sem_group in self._semantic_flag_groups_by_index: + return core.BNAllocString(self._semantic_flag_groups_by_index[sem_group]) return core.BNAllocString("") except (KeyError, OSError): log.log_error(traceback.format_exc()) @@ -894,23 +709,18 @@ class Architecture(object): sem_class = self._semantic_flag_classes_by_index[sem_class] else: sem_class = None - return self.perform_get_flag_role(flag, sem_class) + return self.get_flag_role(flag, sem_class) except KeyError: log.log_error(traceback.format_exc()) return FlagRole.SpecialFlagRole - def perform_get_flag_role(self, flag, sem_class): - if flag in self._flag_roles: - return self._flag_roles[flag] - return FlagRole.SpecialFlagRole - def _get_flags_required_for_flag_condition(self, ctxt, cond, sem_class, count): try: if sem_class in self._semantic_flag_classes_by_index: sem_class = self._semantic_flag_classes_by_index[sem_class] else: sem_class = None - flag_names = self.perform_get_flags_required_for_flag_condition(cond, sem_class) + flag_names = self.get_flags_required_for_flag_condition(cond, sem_class) flags = [] for name in flag_names: flags.append(self._flags[name]) @@ -926,11 +736,6 @@ class Architecture(object): count[0] = 0 return None - def perform_get_flags_required_for_flag_condition(self, cond, sem_class): - if cond in self.flags_required_for_flag_condition: - return self.flags_required_for_flag_condition[cond] - return [] - def _get_flags_required_for_semantic_flag_group(self, ctxt, sem_group, count): try: if sem_group in self._flags_required_by_semantic_flag_group: @@ -951,8 +756,8 @@ class Architecture(object): def _get_flag_conditions_for_semantic_flag_group(self, ctxt, sem_group, count): try: - if sem_group in self._flag_conditions_by_semantic_flag_group: - class_cond = self._flag_conditions_by_semantic_flag_group[sem_group] + if sem_group in self._flag_conditions_for_semantic_flag_group: + class_cond = self._flag_conditions_for_semantic_flag_group[sem_group] else: class_cond = {} count[0] = len(class_cond) @@ -963,7 +768,7 @@ class Architecture(object): cond_buf[i].condition = class_cond[class_index] i += 1 result = ctypes.cast(cond_buf, ctypes.c_void_p) - self._pending_conditions[result.value] = (result, cond_buf) + self._pending_condition_lists[result.value] = (result, cond_buf) return result.value except (KeyError, OSError): log.log_error(traceback.format_exc()) @@ -973,9 +778,9 @@ class Architecture(object): def _free_flag_conditions_for_semantic_flag_group(self, ctxt, conditions): try: buf = ctypes.cast(conditions, ctypes.c_void_p) - if buf.value not in self._pending_conditions: + if buf.value not in self._pending_condition_lists: raise ValueError("freeing condition list that wasn't allocated") - del self._pending_conditions[buf.value] + del self._pending_condition_lists[buf.value] except (ValueError, KeyError): log.log_error(traceback.format_exc()) @@ -1021,7 +826,7 @@ class Architecture(object): operand_list.append(lowlevelil.ILRegister(self, operands[i].reg)) else: operand_list.append(lowlevelil.ILRegister(self, operands[i].reg)) - return self.perform_get_flag_write_low_level_il(op, size, write_type_name, flag_name, operand_list, + return self.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()) @@ -1033,7 +838,7 @@ class Architecture(object): sem_class_name = self._semantic_flag_classes_by_index[sem_class] else: sem_class_name = None - return self.perform_get_flag_condition_low_level_il(cond, sem_class_name, + return self.get_flag_condition_low_level_il(cond, sem_class_name, lowlevelil.LowLevelILFunction(self, core.BNNewLowLevelILFunctionReference(il))).index except OSError: log.log_error(traceback.format_exc()) @@ -1045,7 +850,7 @@ class Architecture(object): sem_group_name = self._semantic_flag_groups_by_index[sem_group] else: sem_group_name = None - return self.perform_get_semantic_flag_group_low_level_il(sem_group_name, + return self.get_semantic_flag_group_low_level_il(sem_group_name, lowlevelil.LowLevelILFunction(self, core.BNNewLowLevelILFunctionReference(il))).index except OSError: log.log_error(traceback.format_exc()) @@ -1068,7 +873,7 @@ class Architecture(object): result[0].size = 0 result[0].extend = ImplicitRegisterExtend.NoExtend return - info = self.__class__.regs[self._regs_by_index[reg]] + info = self.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 @@ -1085,26 +890,26 @@ class Architecture(object): def _get_stack_pointer_register(self, ctxt): try: - return self._all_regs[self.__class__.stack_pointer] + return self._all_regs[self.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: + if self.link_reg is None: return 0xffffffff - return self._all_regs[self.__class__.link_reg] + return self._all_regs[self.link_reg] except KeyError: log.log_error(traceback.format_exc()) return 0 def _get_global_registers(self, ctxt, count): try: - count[0] = len(self.__class__.global_regs) - reg_buf = (ctypes.c_uint * len(self.__class__.global_regs))() - for i in xrange(0, len(self.__class__.global_regs)): - reg_buf[i] = self._all_regs[self.__class__.global_regs[i]] + count[0] = len(self.global_regs) + reg_buf = (ctypes.c_uint * len(self.global_regs))() + for i in xrange(0, len(self.global_regs)): + reg_buf[i] = self._all_regs[self.global_regs[i]] result = ctypes.cast(reg_buf, ctypes.c_void_p) self._pending_reg_lists[result.value] = (result, reg_buf) return result.value @@ -1146,7 +951,7 @@ class Architecture(object): result[0].topRelativeCount = 0 result[0].stackTopReg = 0 return - info = self.__class__.regs[self._reg_stacks_by_index[reg_stack]] + info = self.reg_stacks[self._reg_stacks_by_index[reg_stack]] result[0].firstStorageReg = self._all_regs[info.storage_regs[0]] result[0].storageCount = len(info.storage_regs) if len(info.top_relative_regs) > 0: @@ -1208,7 +1013,7 @@ class Architecture(object): count[0] = 0 return None - def _free_name_and_type_list(self, ctxt, buf_raw): + def _free_name_and_type_list(self, ctxt, buf_raw, length): try: buf = ctypes.cast(buf_raw, ctypes.c_void_p) if buf.value not in self._pending_name_and_type_lists: @@ -1240,7 +1045,7 @@ class Architecture(object): count[0] = 0 return None - def _free_type_list(self, ctxt, buf_raw): + def _free_type_list(self, ctxt, buf_raw, length): try: buf = ctypes.cast(buf_raw, ctypes.c_void_p) if buf.value not in self._pending_type_lists: @@ -1255,7 +1060,7 @@ class Architecture(object): def _assemble(self, ctxt, code, addr, result, errors): try: - data, error_str = self.perform_assemble(code, addr) + data, error_str = self.assemble(code, addr) errors[0] = core.BNAllocString(str(error_str)) if data is None: return False @@ -1273,7 +1078,7 @@ class Architecture(object): try: buf = ctypes.create_string_buffer(length) ctypes.memmove(buf, data, length) - return self.perform_is_never_branch_patch_available(buf.raw, addr) + return self.is_never_branch_patch_available(buf.raw, addr) except: log.log_error(traceback.format_exc()) return False @@ -1282,7 +1087,7 @@ class Architecture(object): try: buf = ctypes.create_string_buffer(length) ctypes.memmove(buf, data, length) - return self.perform_is_always_branch_patch_available(buf.raw, addr) + return self.is_always_branch_patch_available(buf.raw, addr) except: log.log_error(traceback.format_exc()) return False @@ -1291,7 +1096,7 @@ class Architecture(object): try: buf = ctypes.create_string_buffer(length) ctypes.memmove(buf, data, length) - return self.perform_is_invert_branch_patch_available(buf.raw, addr) + return self.is_invert_branch_patch_available(buf.raw, addr) except: log.log_error(traceback.format_exc()) return False @@ -1300,7 +1105,7 @@ class Architecture(object): 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) + return self.is_skip_and_return_zero_patch_available(buf.raw, addr) except: log.log_error(traceback.format_exc()) return False @@ -1309,7 +1114,7 @@ class Architecture(object): 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) + return self.is_skip_and_return_value_patch_available(buf.raw, addr) except: log.log_error(traceback.format_exc()) return False @@ -1318,7 +1123,7 @@ class Architecture(object): try: buf = ctypes.create_string_buffer(length) ctypes.memmove(buf, data, length) - result = self.perform_convert_to_nop(buf.raw, addr) + result = self.convert_to_nop(buf.raw, addr) if result is None: return False result = str(result) @@ -1334,7 +1139,7 @@ class Architecture(object): try: buf = ctypes.create_string_buffer(length) ctypes.memmove(buf, data, length) - result = self.perform_always_branch(buf.raw, addr) + result = self.always_branch(buf.raw, addr) if result is None: return False result = str(result) @@ -1350,7 +1155,7 @@ class Architecture(object): try: buf = ctypes.create_string_buffer(length) ctypes.memmove(buf, data, length) - result = self.perform_invert_branch(buf.raw, addr) + result = self.invert_branch(buf.raw, addr) if result is None: return False result = str(result) @@ -1366,7 +1171,7 @@ class Architecture(object): try: buf = ctypes.create_string_buffer(length) ctypes.memmove(buf, data, length) - result = self.perform_skip_and_return_value(buf.raw, addr, value) + result = self.skip_and_return_value(buf.raw, addr, value) if result is None: return False result = str(result) @@ -1379,27 +1184,15 @@ class Architecture(object): return False def perform_get_associated_arch_by_address(self, addr): + """ + Deprecated method provided for compatibility. Architecture plugins should override ``get_associated_arch_by_address``. + """ return self, addr @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 - ===================== =================================================== + Deprecated method provided for compatibility. Architecture plugins should override ``get_instruction_info``. :param str data: bytes to decode :param int addr: virtual address of the byte to be decoded @@ -1411,8 +1204,7 @@ class Architecture(object): @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. + Deprecated method provided for compatibility. Architecture plugins should override ``get_instruction_text``. :param str data: bytes to decode :param int addr: virtual address of the byte to be decoded @@ -1424,10 +1216,7 @@ class Architecture(object): @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. + Deprecated method provided for compatibility. Architecture plugins should override ``get_instruction_low_level_il``. :param str data: bytes to be interpreted as low-level IL instructions :param int addr: virtual address of start of ``data`` @@ -1439,8 +1228,7 @@ class Architecture(object): @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. + Deprecated method provided for compatibility. Architecture plugins should override ``get_flag_write_low_level_il``. :param LowLevelILOperation op: :param int size: @@ -1458,8 +1246,7 @@ class Architecture(object): @abc.abstractmethod def perform_get_flag_condition_low_level_il(self, cond, sem_class, il): """ - .. note:: Architecture subclasses should implement this method. - .. warning:: This method should never be called directly. + Deprecated method provided for compatibility. Architecture plugins should override ``get_flag_condition_low_level_il``. :param LowLevelILFlagCondition cond: Flag condition to be computed :param str sem_class: Semantic class to be used (None for default semantics) @@ -1471,8 +1258,7 @@ class Architecture(object): @abc.abstractmethod def perform_get_semantic_flag_group_low_level_il(self, sem_group, il): """ - .. note:: Architecture subclasses should implement this method. - .. warning:: This method should never be called directly. + Deprecated method provided for compatibility. Architecture plugins should override ``get_semantic_flag_group_low_level_il``. :param str sem_group: Semantic group to be computed :param LowLevelILFunction il: LowLevelILFunction object to append LowLevelILExpr objects to @@ -1483,14 +1269,7 @@ class Architecture(object): @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. + Deprecated method provided for compatibility. Architecture plugins should override ``assemble``. :param str code: string representation of the instructions to be assembled :param int addr: virtual address that the instructions will be loaded at @@ -1502,8 +1281,7 @@ class Architecture(object): @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. + Deprecated method provided for compatibility. Architecture plugins should override ``is_never_branch_patch_available``. .. note:: Architecture subclasses should implement this method. .. warning:: This method should never be called directly. @@ -1518,11 +1296,7 @@ class Architecture(object): @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. + Deprecated method provided for compatibility. Architecture plugins should override ``is_always_branch_patch_available``. :param str data: bytes to be checked :param int addr: the virtual address of the instruction to be patched @@ -1534,11 +1308,7 @@ class Architecture(object): @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. + Deprecated method provided for compatibility. Architecture plugins should override ``is_invert_branch_patch_available``. :param int addr: the virtual address of the instruction to be patched :return: True if the instruction can be patched, False otherwise @@ -1549,13 +1319,7 @@ class Architecture(object): @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. + Deprecated method provided for compatibility. Architecture plugins should override ``is_skip_and_return_zero_patch_available``. :param str data: bytes to be checked :param int addr: the virtual address of the instruction to be patched @@ -1567,13 +1331,7 @@ class Architecture(object): @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. + Deprecated method provided for compatibility. Architecture plugins should override ``is_skip_and_return_value_patch_available``. :param str data: bytes to be checked :param int addr: the virtual address of the instruction to be patched @@ -1585,10 +1343,7 @@ class Architecture(object): @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. + Deprecated method provided for compatibility. Architecture plugins should override ``convert_to_nop``. :param str data: bytes at virtual address ``addr`` :param int addr: the virtual address of the instruction to be patched @@ -1600,11 +1355,7 @@ class Architecture(object): @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. + Deprecated method provided for compatibility. Architecture plugins should override ``always_branch``. :param str data: bytes to be checked :param int addr: the virtual address of the instruction to be patched @@ -1616,11 +1367,7 @@ class Architecture(object): @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. + Deprecated method provided for compatibility. Architecture plugins should override ``invert_branch``. :param str data: bytes to be checked :param int addr: the virtual address of the instruction to be patched @@ -1632,12 +1379,7 @@ class Architecture(object): @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. + Deprecated method provided for compatibility. Architecture plugins should override ``skip_and_return_value``. :param str data: bytes to be checked :param int addr: the virtual address of the instruction to be patched @@ -1647,76 +1389,70 @@ class Architecture(object): """ return None + def perform_get_flag_role(self, flag, sem_class): + """ + Deprecated method provided for compatibility. Architecture plugins should override ``get_flag_role``. + """ + if flag in self._flag_roles: + return self._flag_roles[flag] + return FlagRole.SpecialFlagRole + + def perform_get_flags_required_for_flag_condition(self, cond, sem_class): + """ + Deprecated method provided for compatibility. Architecture plugins should override ``get_flags_required_for_flag_condition``. + """ + if cond in self.flags_required_for_flag_condition: + return self.flags_required_for_flag_condition[cond] + return [] + def get_associated_arch_by_address(self, addr): - new_addr = ctypes.c_ulonglong() - new_addr.value = addr - result = core.BNGetAssociatedArchitectureByAddress(self.handle, new_addr) - return Architecture(handle = result), new_addr.value + return self.perform_get_associated_arch_by_address(addr) 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:: Architecture subclasses should implement this method. + .. 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. + If the instruction is a branch instruction architecture plugins 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 Branch destination is an unknown address + ===================== =================================================== + :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.arch_transition_by_target_addr = info.archTransitionByTargetAddr - result.branch_delay = info.branchDelay - for i in xrange(0, info.branchCount): - target = info.branchTarget[i] - if info.branchArch[i]: - arch = Architecture(info.branchArch[i]) - else: - arch = None - result.add_branch(BranchType(info.branchType[i]), target, arch) - return result + return self.perform_get_instruction_info(data, addr) 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``. + .. note:: Architecture subclasses should implement this method. + :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 = InstructionTextTokenType(tokens[i].type) - text = tokens[i].text - value = tokens[i].value - size = tokens[i].size - operand = tokens[i].operand - context = tokens[i].context - confidence = tokens[i].confidence - address = tokens[i].address - result.append(function.InstructionTextToken(token_type, text, value, size, operand, context, address, confidence)) - core.BNFreeInstructionText(tokens, count.value) - return result, length.value + return self.perform_get_instruction_text(data, addr) def get_instruction_low_level_il_instruction(self, bv, addr): il = lowlevelil.LowLevelILFunction(self) @@ -1732,19 +1468,15 @@ class Architecture(object): This is used to analyze arbitrary data at an address, if you are working with an existing binary, you likely want to be using ``Function.get_low_level_il_at``. + .. note:: Architecture subclasses should implement this method. + :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 + return self.perform_get_instruction_low_level_il(data, addr, il) def get_low_level_il_from_bytes(self, data, addr): """ @@ -1944,9 +1676,7 @@ class Architecture(object): :return: flag role :rtype: FlagRole """ - flag = self.get_flag_index(flag) - sem_class = self.get_semantic_flag_class_index(sem_class) - return FlagRole(core.BNGetArchitectureFlagRole(self.handle, flag, sem_class)) + return self.perform_get_flag_role(flag, sem_class) def get_flag_write_low_level_il(self, op, size, write_type, flag, operands, il): """ @@ -1958,20 +1688,7 @@ class Architecture(object): :param LowLevelILFunction il: :rtype: LowLevelILExpr """ - flag = self.get_flag_index(flag) - 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.regs[operands[i]].index - elif isinstance(operands[i], lowlevelil.ILRegister): - operand_list[i].constant = False - operand_list[i].reg = operands[i].index - 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], flag, operand_list, len(operand_list), il.handle)) + return self.perform_get_flag_write_low_level_il(op, size, write_type, flag, operands, il) def get_default_flag_write_low_level_il(self, op, size, role, operands, il): """ @@ -1997,13 +1714,14 @@ class Architecture(object): return lowlevelil.LowLevelILExpr(core.BNGetDefaultArchitectureFlagWriteLowLevelIL(self.handle, op, size, role, operand_list, len(operand_list), il.handle)) - def get_flag_condition_low_level_il(self, cond, il): + def get_flag_condition_low_level_il(self, cond, sem_class, il): """ - :param LowLevelILFlagCondition cond: - :param LowLevelILFunction il: + :param LowLevelILFlagCondition cond: Flag condition to be computed + :param str sem_class: Semantic class to be used (None for default semantics) + :param LowLevelILFunction il: LowLevelILFunction object to append LowLevelILExpr objects to :rtype: LowLevelILExpr """ - return lowlevelil.LowLevelILExpr(core.BNGetArchitectureFlagConditionLowLevelIL(self.handle, cond, il.handle)) + return self.perform_get_flag_condition_low_level_il(cond, sem_class, il) def get_default_flag_condition_low_level_il(self, cond, sem_class, il): """ @@ -2021,18 +1739,10 @@ class Architecture(object): :param LowLevelILFunction il: :rtype: LowLevelILExpr """ - group_index = self.get_semantic_flag_group_index(sem_group) - return lowlevelil.LowLevelILExpr(core.BNGetArchitectureSemanticFlagGroupLowLevelIL(self.handle, group_index, il.handle)) + return self.perform_get_semantic_flag_group_low_level_il(sem_group, il) def get_flags_required_for_flag_condition(self, cond, sem_class = None): - sem_class = self.get_semantic_flag_class_index(sem_class) - count = ctypes.c_ulonglong() - flags = core.BNGetArchitectureFlagsRequiredForFlagCondition(self.handle, cond, sem_class, count) - flag_names = [] - for i in xrange(0, count.value): - flag_names.append(self._flags_by_index[flags[i]]) - core.BNFreeRegisterList(flags) - return flag_names + return self.perform_get_flags_required_for_flag_condition(cond, sem_class) def get_modified_regs_on_write(self, reg): """ @@ -2056,6 +1766,14 @@ class Architecture(object): ``assemble`` converts the string of assembly instructions ``code`` loaded at virtual address ``addr`` to the byte representation of those instructions. + .. note:: Architecture subclasses should implement this method. + + Architecture plugins can override this method to provide assembler functionality. This can be done by + simply shelling out to an assembler like yasm or llvm-mc, since this method isn't performance sensitive. + + .. 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. + :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 @@ -2066,16 +1784,14 @@ class Architecture(object): ('\\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 + return self.perform_assemble(code, addr) 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**. + .. note:: Architecture subclasses should implement this method. + :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 @@ -2088,16 +1804,15 @@ class Architecture(object): 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)) + return self.perform_is_never_branch_patch_available(data, addr) 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**. + .. note:: Architecture subclasses should implement this method. + :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 @@ -2110,15 +1825,14 @@ class Architecture(object): 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)) + return self.perform_is_always_branch_patch_available(data, addr) def is_invert_branch_patch_available(self, data, addr): """ ``is_always_branch_patch_available`` determines if the instruction ``data`` at ``addr`` can be inverted. + .. note:: Architecture subclasses should implement this method. + :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 @@ -2131,16 +1845,15 @@ class Architecture(object): 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)) + return self.perform_is_invert_branch_patch_available(data, addr) 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*. + .. note:: Architecture subclasses should implement this method. + :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 @@ -2155,39 +1868,37 @@ class Architecture(object): 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)) + return self.perform_is_skip_and_return_zero_patch_available(data, addr) 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* + ``is_skip_and_return_value_patch_available`` determines if the instruction ``data`` at ``addr`` is a *call-like* instruction that can be made into an instruction *returns a value*. + .. note:: Architecture subclasses should implement this method. + :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) + >>> arch.is_skip_and_return_value_patch_available(arch.assemble("call 0")[0], 0) True - >>> arch.is_skip_and_return_zero_patch_available(arch.assemble("jmp eax")[0], 0) + >>> arch.is_skip_and_return_value_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)) + return self.perform_is_skip_and_return_value_patch_available(data, addr) 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 + .. note:: Architecture subclasses should implement this method. + + :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 @@ -2197,20 +1908,15 @@ class Architecture(object): '\\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 + return self.perform_convert_to_nop(data, addr) 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. + .. note:: Architecture subclasses should implement this method. + :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 @@ -2224,20 +1930,15 @@ class Architecture(object): (['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 + return self.perform_always_branch(data, addr) 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. + .. note:: Architecture subclasses should implement this method. + :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 @@ -2252,20 +1953,15 @@ class Architecture(object): (['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 + return self.perform_invert_branch(data, addr) 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*. + .. note:: Architecture subclasses should implement this method. + :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 @@ -2276,14 +1972,7 @@ class Architecture(object): (['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 + return self.perform_skip_and_return_value(data, addr, value) def is_view_type_constant_defined(self, type_name, const_name): """ @@ -2348,6 +2037,656 @@ class Architecture(object): core.BNRegisterCallingConvention(self.handle, cc.handle) +class CoreArchitecture(Architecture): + def __init__(self, handle): + super(CoreArchitecture, self).__init__() + + self.handle = core.handle_of_type(handle, core.BNArchitecture) + self.__dict__["name"] = core.BNGetArchitectureName(self.handle) + self.__dict__["endianness"] = Endianness(core.BNGetArchitectureEndianness(self.handle)) + self.__dict__["address_size"] = core.BNGetArchitectureAddressSize(self.handle) + self.__dict__["default_int_size"] = core.BNGetArchitectureDefaultIntegerSize(self.handle) + self.__dict__["instr_alignment"] = core.BNGetArchitectureInstructionAlignment(self.handle) + self.__dict__["max_instr_length"] = core.BNGetArchitectureMaxInstructionLength(self.handle) + self.__dict__["opcode_display_length"] = core.BNGetArchitectureOpcodeDisplayLength(self.handle) + self.__dict__["stack_pointer"] = core.BNGetArchitectureRegisterName(self.handle, + core.BNGetArchitectureStackPointerRegister(self.handle)) + + link_reg = core.BNGetArchitectureLinkRegister(self.handle) + if link_reg == 0xffffffff: + self.__dict__["link_reg"] = None + else: + self.__dict__["link_reg"] = core.BNGetArchitectureRegisterName(self.handle, link_reg) + + count = ctypes.c_ulonglong() + regs = core.BNGetAllArchitectureRegisters(self.handle, count) + self._all_regs = {} + self._regs_by_index = {} + self._full_width_regs = {} + 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, + ImplicitRegisterExtend(info.extend), regs[i]) + self._all_regs[name] = regs[i] + self._regs_by_index[regs[i]] = name + for i in xrange(0, count.value): + info = core.BNGetArchitectureRegisterInfo(self.handle, regs[i]) + full_width_reg = core.BNGetArchitectureRegisterName(self.handle, info.fullWidthRegister) + if full_width_reg not in self._full_width_regs: + self._full_width_regs[full_width_reg] = self._all_regs[full_width_reg] + 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() + write_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, write_types[i]) + self._flag_write_types[name] = write_types[i] + self._flag_write_types_by_index[write_types[i]] = name + self.flag_write_types.append(name) + core.BNFreeRegisterList(write_types) + + count = ctypes.c_ulonglong() + sem_classes = core.BNGetAllArchitectureSemanticFlagClasses(self.handle, count) + self._semantic_flag_classes = {} + self._semantic_flag_classes_by_index = {} + self.__dict__["semantic_flag_classes"] = [] + for i in xrange(0, count.value): + name = core.BNGetArchitectureSemanticFlagClassName(self.handle, sem_classes[i]) + self._semantic_flag_classes[name] = sem_classes[i] + self._semantic_flag_classes_by_index[sem_classes[i]] = name + self.semantic_flag_classes.append(name) + core.BNFreeRegisterList(sem_classes) + + count = ctypes.c_ulonglong() + sem_groups = core.BNGetAllArchitectureSemanticFlagGroups(self.handle, count) + self._semantic_flag_groups = {} + self._semantic_flag_groups_by_index = {} + self.__dict__["semantic_flag_groups"] = [] + for i in xrange(0, count.value): + name = core.BNGetArchitectureSemanticFlagGroupName(self.handle, sem_groups[i]) + self._semantic_flag_groups[name] = sem_groups[i] + self._semantic_flag_groups_by_index[sem_groups[i]] = name + self.semantic_flag_groups.append(name) + core.BNFreeRegisterList(sem_groups) + + self._flag_roles = {} + self.__dict__["flag_roles"] = {} + for flag in self.__dict__["flags"]: + role = FlagRole(core.BNGetArchitectureFlagRole(self.handle, self._flags[flag], 0)) + self.__dict__["flag_roles"][flag] = role + self._flag_roles[self._flags[flag]] = role + + self.__dict__["flags_required_for_flag_condition"] = {} + for cond in LowLevelILFlagCondition: + count = ctypes.c_ulonglong() + flags = core.BNGetArchitectureFlagsRequiredForFlagCondition(self.handle, cond, 0, count) + flag_names = [] + for i in xrange(0, count.value): + flag_names.append(self._flags_by_index[flags[i]]) + core.BNFreeRegisterList(flags) + self.__dict__["flags_required_for_flag_condition"][cond] = flag_names + + self._flags_required_by_semantic_flag_group = {} + self.__dict__["flags_required_for_semantic_flag_group"] = {} + for group in self.semantic_flag_groups: + count = ctypes.c_ulonglong() + flags = core.BNGetArchitectureFlagsRequiredForSemanticFlagGroup(self.handle, + self._semantic_flag_groups[group], 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_by_semantic_flag_group[self._semantic_flag_groups[group]] = flag_indexes + self.__dict__["flags_required_for_semantic_flag_group"][cond] = flag_names + + self._flag_conditions_for_semantic_flag_group = {} + self.__dict__["flag_conditions_for_semantic_flag_group"] = {} + for group in self.semantic_flag_groups: + count = ctypes.c_ulonglong() + conditions = core.BNGetArchitectureFlagConditionsForSemanticFlagGroup(self.handle, + self._semantic_flag_groups[group], count) + class_index_cond = {} + class_cond = {} + for i in xrange(0, count.value): + class_index_cond[conditions[i].semanticClass] = conditions[i].condition + if conditions[i].semanticClass == 0: + class_cond[None] = conditions[i].condition + elif conditions[i].semanticClass in self._semantic_flag_classes_by_index: + class_cond[self._semantic_flag_classes_by_index[conditions[i].semanticClass]] = conditions[i].condition + core.BNFreeFlagConditionsForSemanticFlagGroup(conditions) + self._flag_conditions_for_semantic_flag_group[self._semantic_flag_groups[group]] = class_index_cond + self.__dict__["flag_conditions_for_semantic_flag_group"][group] = class_cond + + 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 + + self._semantic_class_for_flag_write_type = {} + self.__dict__["semantic_class_for_flag_write_type"] = {} + for write_type in self.flag_write_types: + sem_class = core.BNGetArchitectureSemanticClassForFlagWriteType(self.handle, + self._flag_write_types[write_type]) + if sem_class == 0: + sem_class_name = None + else: + sem_class_name = self._semantic_flag_classes_by_index[sem_class] + self._semantic_class_for_flag_write_type[self._flag_write_types[write_type]] = sem_class + self.__dict__["semantic_class_for_flag_write_type"][write_type] = sem_class_name + + count = ctypes.c_ulonglong() + regs = core.BNGetArchitectureGlobalRegisters(self.handle, count) + self.__dict__["global_regs"] = [] + for i in xrange(0, count.value): + self.global_regs.append(core.BNGetArchitectureRegisterName(self.handle, regs[i])) + core.BNFreeRegisterList(regs) + + count = ctypes.c_ulonglong() + regs = core.BNGetAllArchitectureRegisterStacks(self.handle, count) + self._all_reg_stacks = {} + self._reg_stacks_by_index = {} + self.__dict__["reg_stacks"] = {} + for i in xrange(0, count.value): + name = core.BNGetArchitectureRegisterStackName(self.handle, regs[i]) + info = core.BNGetArchitectureRegisterStackInfo(self.handle, regs[i]) + storage = [] + for j in xrange(0, info.storageCount): + storage.append(core.BNGetArchitectureRegisterName(self.handle, info.firstStorageReg + j)) + top_rel = [] + for j in xrange(0, info.topRelativeCount): + top_rel.append(core.BNGetArchitectureRegisterName(self.handle, info.firstTopRelativeReg + j)) + top = core.BNGetArchitectureRegisterName(self.handle, info.stackTopReg) + self.reg_stacks[name] = function.RegisterStackInfo(storage, top_rel, top, regs[i]) + self._all_reg_stacks[name] = regs[i] + self._reg_stacks_by_index[regs[i]] = name + core.BNFreeRegisterList(regs) + + count = ctypes.c_ulonglong() + intrinsics = core.BNGetAllArchitectureIntrinsics(self.handle, count) + self._intrinsics = {} + self._intrinsics_by_index = {} + self.__dict__["intrinsics"] = {} + for i in xrange(0, count.value): + name = core.BNGetArchitectureIntrinsicName(self.handle, intrinsics[i]) + input_count = ctypes.c_ulonglong() + inputs = core.BNGetArchitectureIntrinsicInputs(self.handle, intrinsics[i], input_count) + input_list = [] + for j in xrange(0, input_count.value): + input_name = inputs[j].name + type_obj = types.Type(core.BNNewTypeReference(inputs[j].type), confidence = inputs[j].typeConfidence) + input_list.append(function.IntrinsicInput(type_obj, input_name)) + core.BNFreeNameAndTypeList(inputs, input_count.value) + output_count = ctypes.c_ulonglong() + outputs = core.BNGetArchitectureIntrinsicOutputs(self.handle, intrinsics[i], output_count) + output_list = [] + for j in xrange(0, output_count.value): + output_list.append(types.Type(core.BNNewTypeReference(outputs[j].type), confidence = outputs[j].confidence)) + core.BNFreeOutputTypeList(outputs, output_count.value) + self.intrinsics[name] = function.IntrinsicInfo(input_list, output_list) + self._intrinsics[name] = intrinsics[i] + self._intrinsics_by_index[intrinsics[i]] = (name, self.intrinsics[name]) + core.BNFreeRegisterList(intrinsics) + + def get_associated_arch_by_address(self, addr): + new_addr = ctypes.c_ulonglong() + new_addr.value = addr + result = core.BNGetAssociatedArchitectureByAddress(self.handle, new_addr) + return CoreArchitecture(handle = result), new_addr.value + + 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.arch_transition_by_target_addr = info.archTransitionByTargetAddr + result.branch_delay = info.branchDelay + for i in xrange(0, info.branchCount): + target = info.branchTarget[i] + if info.branchArch[i]: + arch = CoreArchitecture(info.branchArch[i]) + else: + arch = None + result.add_branch(BranchType(info.branchType[i]), 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 = InstructionTextTokenType(tokens[i].type) + text = tokens[i].text + value = tokens[i].value + size = tokens[i].size + operand = tokens[i].operand + context = tokens[i].context + confidence = tokens[i].confidence + address = tokens[i].address + result.append(function.InstructionTextToken(token_type, text, value, size, operand, context, address, confidence)) + 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 to ``il`` for the instruction at the given + virtual address ``addr`` with data ``data``. + + This is used to analyze arbitrary data at an address, if you are working with an existing binary, you likely + want to be using ``Function.get_low_level_il_at``. + + :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_flag_write_low_level_il(self, op, size, write_type, flag, 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 + """ + flag = self.get_flag_index(flag) + 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.regs[operands[i]].index + elif isinstance(operands[i], lowlevelil.ILRegister): + operand_list[i].constant = False + operand_list[i].reg = operands[i].index + 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], flag, operand_list, len(operand_list), il.handle)) + + def get_flag_condition_low_level_il(self, cond, sem_class, il): + """ + :param LowLevelILFlagCondition cond: Flag condition to be computed + :param str sem_class: Semantic class to be used (None for default semantics) + :param LowLevelILFunction il: LowLevelILFunction object to append LowLevelILExpr objects to + :rtype: LowLevelILExpr + """ + class_index = self.get_semantic_flag_class_index(sem_class) + return lowlevelil.LowLevelILExpr(core.BNGetArchitectureFlagConditionLowLevelIL(self.handle, cond, + class_index, il.handle)) + + def get_semantic_flag_group_low_level_il(self, sem_group, il): + """ + :param str sem_group: + :param LowLevelILFunction il: + :rtype: LowLevelILExpr + """ + group_index = self.get_semantic_flag_group_index(sem_group) + return lowlevelil.LowLevelILExpr(core.BNGetArchitectureSemanticFlagGroupLowLevelIL(self.handle, group_index, il.handle)) + + 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_value_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_value_patch_available(arch.assemble("call 0")[0], 0) + True + >>> arch.is_skip_and_return_value_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 get_flag_role(self, flag, sem_class = None): + """ + ``get_flag_role`` gets the role of a given flag. + + :param int flag: flag + :param int sem_class: optional semantic flag class + :return: flag role + :rtype: FlagRole + """ + flag = self.get_flag_index(flag) + sem_class = self.get_semantic_flag_class_index(sem_class) + return FlagRole(core.BNGetArchitectureFlagRole(self.handle, flag, sem_class)) + + def get_flags_required_for_flag_condition(self, cond, sem_class = None): + sem_class = self.get_semantic_flag_class_index(sem_class) + count = ctypes.c_ulonglong() + flags = core.BNGetArchitectureFlagsRequiredForFlagCondition(self.handle, cond, sem_class, count) + flag_names = [] + for i in xrange(0, count.value): + flag_names.append(self._flags_by_index[flags[i]]) + core.BNFreeRegisterList(flags) + return flag_names + + +class ArchitectureHook(CoreArchitecture): + def __init__(self, base_arch): + self.base_arch = base_arch + super(ArchitectureHook, self).__init__(base_arch.handle) + + # To improve performance of simpler hooks, use null callback for functions that are not being overridden + if self.get_associated_arch_by_address.__code__ == CoreArchitecture.get_associated_arch_by_address.__code__: + self._cb.getAssociatedArchitectureByAddress = self._cb.getAssociatedArchitectureByAddress.__class__() + if self.get_instruction_info.__code__ == CoreArchitecture.get_instruction_info.__code__: + self._cb.getInstructionInfo = self._cb.getInstructionInfo.__class__() + if self.get_instruction_text.__code__ == CoreArchitecture.get_instruction_text.__code__: + self._cb.getInstructionText = self._cb.getInstructionText.__class__() + if self.__class__.stack_pointer is None: + self._cb.getStackPointerRegister = self._cb.getStackPointerRegister.__class__() + if self.__class__.link_reg is None: + self._cb.getLinkRegister = self._cb.getLinkRegister.__class__() + if len(self.__class__.regs) == 0: + self._cb.getRegisterInfo = self._cb.getRegisterInfo.__class__() + self._cb.getRegisterName = self._cb.getRegisterName.__class__() + if len(self.__class__.reg_stacks) == 0: + self._cb.getRegisterStackName = self._cb.getRegisterStackName.__class__() + self._cb.getRegisterStackInfo = self._cb.getRegisterStackInfo.__class__() + if len(self.__class__.intrinsics) == 0: + self._cb.getIntrinsicName = self._cb.getIntrinsicName.__class__() + self._cb.getIntrinsicInputs = self._cb.getIntrinsicInputs.__class__() + self._cb.freeNameAndTypeList = self._cb.freeNameAndTypeList.__class__() + self._cb.getIntrinsicOutputs = self._cb.getIntrinsicOutputs.__class__() + self._cb.freeTypeList = self._cb.freeTypeList.__class__() + + def register(self): + self.__class__._registered_cb = self._cb + self.handle = core.BNRegisterArchitectureHook(self.base_arch.handle, self._cb) + + class ReferenceSource(object): def __init__(self, func, arch, addr): self.function = func diff --git a/python/basicblock.py b/python/basicblock.py index 8e64c1c1..c93d0b2c 100644 --- a/python/basicblock.py +++ b/python/basicblock.py @@ -100,7 +100,7 @@ class BasicBlock(object): arch = core.BNGetBasicBlockArchitecture(self.handle) if arch is None: return None - self._arch = architecture.Architecture(arch) + self._arch = architecture.CoreArchitecture(arch) return self._arch @property diff --git a/python/binaryview.py b/python/binaryview.py index fae98c14..7a6bc875 100644 --- a/python/binaryview.py +++ b/python/binaryview.py @@ -400,7 +400,7 @@ class BinaryViewType(object): arch = core.BNGetArchitectureForViewType(self.handle, ident, endian) if arch is None: return None - return architecture.Architecture(arch) + return architecture.CoreArchitecture(arch) def register_platform(self, ident, arch, plat): core.BNRegisterPlatformForViewType(self.handle, ident, arch.handle, plat.handle) @@ -812,7 +812,7 @@ class BinaryView(object): arch = core.BNGetDefaultArchitecture(self.handle) if arch is None: return None - return architecture.Architecture(handle=arch) + return architecture.CoreArchitecture(handle=arch) @arch.setter def arch(self, value): @@ -2191,7 +2191,7 @@ class BinaryView(object): else: func = None if refs[i].arch: - arch = architecture.Architecture(refs[i].arch) + arch = architecture.CoreArchitecture(refs[i].arch) else: arch = None addr = refs[i].addr diff --git a/python/callingconvention.py b/python/callingconvention.py index 5aad3317..49ef7666 100644 --- a/python/callingconvention.py +++ b/python/callingconvention.py @@ -75,7 +75,7 @@ class CallingConvention(object): self.__class__._registered_calling_conventions.append(self) else: self.handle = handle - self.arch = architecture.Architecture(core.BNGetCallingConventionArchitecture(self.handle)) + self.arch = architecture.CoreArchitecture(core.BNGetCallingConventionArchitecture(self.handle)) self.__dict__["name"] = core.BNGetCallingConventionName(self.handle) self.__dict__["arg_regs_share_index"] = core.BNAreArgumentRegistersSharedIndex(self.handle) self.__dict__["stack_reserved_for_arg_regs"] = core.BNIsStackReservedForArgumentRegisters(self.handle) diff --git a/python/examples/arch_hook.py b/python/examples/arch_hook.py new file mode 100644 index 00000000..452bd2b6 --- /dev/null +++ b/python/examples/arch_hook.py @@ -0,0 +1,16 @@ +from binaryninja.architecture import Architecture, ArchitectureHook + +class X86ReturnHook(ArchitectureHook): + def get_instruction_text(self, data, addr): + # Call the original implementation's method by calling the superclass + result, length = super(X86ReturnHook, self).get_instruction_text(data, addr) + + # Patch the name of the 'retn' instruction to 'ret' + if len(result) > 0 and result[0].text == 'retn': + result[0].text = 'ret' + + return result, length + +# Install the hook by constructing it with the desired architecture to hook, then registering it +X86ReturnHook(Architecture['x86']).register() + diff --git a/python/examples/nes.py b/python/examples/nes.py index 39544c9f..00451abd 100644 --- a/python/examples/nes.py +++ b/python/examples/nes.py @@ -423,7 +423,7 @@ class M6502(Architecture): return instr, operand, length, value - def perform_get_instruction_info(self, data, addr): + def get_instruction_info(self, data, addr): instr, operand, length, value = self.decode_instruction(data, addr) if instr is None: return None @@ -445,7 +445,7 @@ class M6502(Architecture): result.add_branch(BranchType.FalseBranch, addr + 2) return result - def perform_get_instruction_text(self, data, addr): + def get_instruction_text(self, data, addr): instr, operand, length, value = self.decode_instruction(data, addr) if instr is None: return None @@ -455,7 +455,7 @@ class M6502(Architecture): tokens += OperandTokens[operand](value) return tokens, length - def perform_get_instruction_low_level_il(self, data, addr, il): + def get_instruction_low_level_il(self, data, addr, il): instr, operand, length, value = self.decode_instruction(data, addr) if instr is None: return None @@ -470,48 +470,48 @@ class M6502(Architecture): return length - def perform_get_flag_write_low_level_il(self, op, size, write_type, flag, operands, il): + def get_flag_write_low_level_il(self, op, size, write_type, flag, operands, il): if flag == 'c': if (op == LowLevelILOperation.LLIL_SUB) or (op == LowLevelILOperation.LLIL_SBB): # Subtraction carry flag is inverted from the commom implementation return il.not_expr(0, self.get_default_flag_write_low_level_il(op, size, FlagRole.CarryFlagRole, operands, il)) # Other operations use a normal carry flag return self.get_default_flag_write_low_level_il(op, size, FlagRole.CarryFlagRole, operands, il) - return Architecture.perform_get_flag_write_low_level_il(self, op, size, write_type, flag, operands, il) + return Architecture.get_flag_write_low_level_il(self, op, size, write_type, flag, operands, il) - def perform_is_never_branch_patch_available(self, data, addr): + def is_never_branch_patch_available(self, data, addr): if (data[0] == "\x10") or (data[0] == "\x30") or (data[0] == "\x50") or (data[0] == "\x70") or (data[0] == "\x90") or (data[0] == "\xb0") or (data[0] == "\xd0") or (data[0] == "\xf0"): return True return False - def perform_is_invert_branch_patch_available(self, data, addr): + def is_invert_branch_patch_available(self, data, addr): if (data[0] == "\x10") or (data[0] == "\x30") or (data[0] == "\x50") or (data[0] == "\x70") or (data[0] == "\x90") or (data[0] == "\xb0") or (data[0] == "\xd0") or (data[0] == "\xf0"): return True return False - def perform_is_always_branch_patch_available(self, data, addr): + def is_always_branch_patch_available(self, data, addr): return False - def perform_is_skip_and_return_zero_patch_available(self, data, addr): + def is_skip_and_return_zero_patch_available(self, data, addr): return (data[0] == "\x20") and (len(data) == 3) - def perform_is_skip_and_return_value_patch_available(self, data, addr): + def is_skip_and_return_value_patch_available(self, data, addr): return (data[0] == "\x20") and (len(data) == 3) - def perform_convert_to_nop(self, data, addr): + def convert_to_nop(self, data, addr): return "\xea" * len(data) - def perform_never_branch(self, data, addr): + def never_branch(self, data, addr): if (data[0] == "\x10") or (data[0] == "\x30") or (data[0] == "\x50") or (data[0] == "\x70") or (data[0] == "\x90") or (data[0] == "\xb0") or (data[0] == "\xd0") or (data[0] == "\xf0"): return "\xea" * len(data) return None - def perform_invert_branch(self, data, addr): + def invert_branch(self, data, addr): if (data[0] == "\x10") or (data[0] == "\x30") or (data[0] == "\x50") or (data[0] == "\x70") or (data[0] == "\x90") or (data[0] == "\xb0") or (data[0] == "\xd0") or (data[0] == "\xf0"): return chr(ord(data[0]) ^ 0x20) + data[1:] return None - def perform_skip_and_return_value(self, data, addr, value): + def skip_and_return_value(self, data, addr, value): if (data[0] != "\x20") or (len(data) != 3): return None return "\xa9" + chr(value & 0xff) + "\xea" diff --git a/python/function.py b/python/function.py index 39ace7c6..f68aa76f 100644 --- a/python/function.py +++ b/python/function.py @@ -419,7 +419,7 @@ class Function(object): arch = core.BNGetFunctionArchitecture(self.handle) if arch is None: return None - self._arch = architecture.Architecture(arch) + self._arch = architecture.CoreArchitecture(arch) return self._arch @property @@ -557,7 +557,7 @@ class Function(object): branches = core.BNGetIndirectBranches(self.handle, count) result = [] for i in xrange(0, count.value): - result.append(IndirectBranchInfo(architecture.Architecture(branches[i].sourceArch), branches[i].sourceAddr, architecture.Architecture(branches[i].destArch), branches[i].destAddr, branches[i].autoDefined)) + result.append(IndirectBranchInfo(architecture.CoreArchitecture(branches[i].sourceArch), branches[i].sourceAddr, architecture.Architecture(branches[i].destArch), branches[i].destAddr, branches[i].autoDefined)) core.BNFreeIndirectBranchList(branches) return result @@ -1115,7 +1115,7 @@ class Function(object): branches = core.BNGetIndirectBranchesAt(self.handle, arch.handle, addr, count) result = [] for i in xrange(0, count.value): - result.append(IndirectBranchInfo(architecture.Architecture(branches[i].sourceArch), branches[i].sourceAddr, architecture.Architecture(branches[i].destArch), branches[i].destAddr, branches[i].autoDefined)) + result.append(IndirectBranchInfo(architecture.CoreArchitecture(branches[i].sourceArch), branches[i].sourceAddr, architecture.Architecture(branches[i].destArch), branches[i].destAddr, branches[i].autoDefined)) core.BNFreeIndirectBranchList(branches) return result @@ -1633,7 +1633,7 @@ class FunctionGraphBlock(object): arch = core.BNGetFunctionGraphBlockArchitecture(self.handle) if arch is None: return None - return architecture.Architecture(arch) + return architecture.CoreArchitecture(arch) @property def start(self): diff --git a/python/platform.py b/python/platform.py index 5e63d836..09670bac 100644 --- a/python/platform.py +++ b/python/platform.py @@ -105,7 +105,7 @@ class Platform(object): else: self.handle = handle self.__dict__["name"] = core.BNGetPlatformName(self.handle) - self.arch = architecture.Architecture(core.BNGetPlatformArchitecture(self.handle)) + self.arch = architecture.CoreArchitecture(core.BNGetPlatformArchitecture(self.handle)) def __del__(self): core.BNFreePlatform(self.handle) -- cgit v1.3.1 From 8849fb2b2b8dc824bd3f17ce1026a04856477a94 Mon Sep 17 00:00:00 2001 From: Jordan Wiens Date: Sun, 4 Mar 2018 18:08:04 -0500 Subject: working division, prints, and metaclasses, but imports broken, still needs work --- python/__init__.py | 60 +-- python/architecture.py | 36 +- python/basicblock.py | 12 +- python/binaryview.py | 66 ++- python/callingconvention.py | 17 +- python/databuffer.py | 4 +- python/demangle.py | 7 +- python/examples/README.md | 56 --- python/examples/angr_plugin.py | 153 ------- python/examples/arch_hook.py | 16 - python/examples/bin_info.py | 72 ---- python/examples/breakpoint.py | 50 --- python/examples/export_svg.py | 224 ----------- python/examples/instruction_iterator.py | 53 --- python/examples/jump_table.py | 86 ---- python/examples/nds.py | 117 ------ python/examples/nes.py | 646 ------------------------------ python/examples/notification_callbacks.py | 51 --- python/examples/nsf.py | 145 ------- python/examples/print_syscalls.py | 55 --- python/examples/version_switcher.py | 150 ------- python/fileaccessor.py | 7 +- python/filemetadata.py | 17 +- python/function.py | 35 +- python/functionrecognizer.py | 14 +- python/generator.cpp | 4 +- python/highlight.py | 6 +- python/interaction.py | 12 +- python/log.py | 5 +- python/lowlevelil.py | 25 +- python/mainthread.py | 6 +- python/mediumlevelil.py | 18 +- python/metadata.py | 7 +- python/platform.py | 19 +- python/plugin.py | 29 +- python/pluginmanager.py | 8 +- python/scriptingprovider.py | 25 +- python/setting.py | 4 +- python/startup.py | 2 +- python/transform.py | 18 +- python/types.py | 15 +- python/undoaction.py | 10 +- python/update.py | 17 +- 43 files changed, 278 insertions(+), 2101 deletions(-) delete mode 100644 python/examples/README.md delete mode 100644 python/examples/angr_plugin.py delete mode 100644 python/examples/arch_hook.py delete mode 100644 python/examples/bin_info.py delete mode 100644 python/examples/breakpoint.py delete mode 100755 python/examples/export_svg.py delete mode 100644 python/examples/instruction_iterator.py delete mode 100644 python/examples/jump_table.py delete mode 100644 python/examples/nds.py delete mode 100644 python/examples/nes.py delete mode 100644 python/examples/notification_callbacks.py delete mode 100644 python/examples/nsf.py delete mode 100644 python/examples/print_syscalls.py delete mode 100644 python/examples/version_switcher.py (limited to 'python/examples') diff --git a/python/__init__.py b/python/__init__.py index fda5f297..66f4bfe8 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -19,41 +19,41 @@ # IN THE SOFTWARE. +from __future__ import absolute_import import atexit import sys from time import gmtime # Binary Ninja components -import _binaryninjacore as core -from .enums import * -from .databuffer import * -from .filemetadata import * -from .fileaccessor import * -from .binaryview import * -from .transform import * -from .architecture import * -from .basicblock import * -from .function import * -from .log import * -from .lowlevelil import * -from .mediumlevelil import * -from .types import * -from .functionrecognizer import * -from .update import * -from .plugin import * -from .callingconvention import * -from .platform import * -from .demangle import * -from .mainthread import * -from .interaction import * -from .lineardisassembly import * -from .undoaction import * -from .highlight import * -from .scriptingprovider import * -from .downloadprovider import * -from .pluginmanager import * -from .setting import * -from .metadata import * +from binaryninja import _binaryninjacore as core +from binaryninja.enums import * +from binaryninja.databuffer import * +from binaryninja.filemetadata import * +from binaryninja.fileaccessor import * +from binaryninja.binaryview import * +from binaryninja.transform import * +from binaryninja.architecture import * +from binaryninja.basicblock import * +from binaryninja.function import * +from binaryninja.log import * +from binaryninja.lowlevelil import * +from binaryninja.mediumlevelil import * +from binaryninja.types import * +from binaryninja.functionrecognizer import * +from binaryninja.update import * +from binaryninja.plugin import * +from binaryninja.callingconvention import * +from binaryninja.platform import * +from binaryninja.demangle import * +from binaryninja.mainthread import * +from binaryninja.interaction import * +from binaryninja.lineardisassembly import * +from binaryninja.undoaction import * +from binaryninja.highlight import * +from binaryninja.scriptingprovider import * +from binaryninja.pluginmanager import * +from binaryninja.setting import * +from binaryninja.metadata import * def shutdown(): diff --git a/python/architecture.py b/python/architecture.py index df22da7f..360ce6bf 100644 --- a/python/architecture.py +++ b/python/architecture.py @@ -18,25 +18,22 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. +from __future__ import absolute_import import traceback import ctypes import abc -# Binary Ninja components -import _binaryninjacore as core -from enums import (Endianness, ImplicitRegisterExtend, BranchType, +# Binary Ninja components -- additional imports belong in the appropriate class +from binaryninja import _binaryninjacore as core +from binaryninja.enums import (Endianness, ImplicitRegisterExtend, BranchType, InstructionTextTokenType, LowLevelILFlagCondition, FlagRole) -import startup -import function -import lowlevelil -import callingconvention -import platform -import log -import databuffer -import types + +#2-3 compatibility +from six import with_metaclass class _ArchitectureMetaClass(type): + from binaryninja import startup @property def list(self): startup._init_plugins() @@ -80,12 +77,12 @@ class _ArchitectureMetaClass(type): raise AttributeError("attribute '%s' is read only" % name) -class Architecture(object): +class Architecture(with_metaclass(_ArchitectureMetaClass, 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 + ``class Architecture`` has a metaclass with the additional methods ``register``, and supports iteration:: >>> #List the architectures @@ -130,10 +127,17 @@ class Architecture(object): semantic_class_for_flag_write_type = {} reg_stacks = {} intrinsics = {} - __metaclass__ = _ArchitectureMetaClass next_address = 0 def __init__(self): + from binaryninja import function + from binaryninja import startup + from binaryninja import lowlevelil + from binaryninja import types + from binaryninja import databuffer + from binaryninja import log + from binaryninja import platform + from binaryninja import callingconvention startup._init_plugins() if self.__class__.opcode_display_length > self.__class__.max_instr_length: @@ -2045,6 +2049,10 @@ class Architecture(object): _architecture_cache = {} class CoreArchitecture(Architecture): def __init__(self, handle): + from binaryninja import function + from binaryninja import types + from binaryninja import databuffer + from binaryninja import lowlevelil super(CoreArchitecture, self).__init__() self.handle = core.handle_of_type(handle, core.BNArchitecture) diff --git a/python/basicblock.py b/python/basicblock.py index a79b53ad..907b4065 100644 --- a/python/basicblock.py +++ b/python/basicblock.py @@ -20,12 +20,9 @@ import ctypes -# Binary Ninja components -import _binaryninjacore as core -from enums import BranchType, HighlightColorStyle, HighlightStandardColor, InstructionTextTokenType -import architecture -import highlight -import function +# Binary Ninja components -- additional imports belong in the appropriate class +from binaryninja import _binaryninjacore as core +from binaryninja.enums import BranchType, HighlightColorStyle, HighlightStandardColor, InstructionTextTokenType class BasicBlockEdge(object): @@ -54,6 +51,9 @@ class BasicBlockEdge(object): class BasicBlock(object): def __init__(self, view, handle): + from binaryninja import architecture + from binaryninja import function + from binaryninja import highlight self.view = view self.handle = core.handle_of_type(handle, core.BNBasicBlock) self._arch = None diff --git a/python/binaryview.py b/python/binaryview.py index 1e078523..f6a1bc83 100644 --- a/python/binaryview.py +++ b/python/binaryview.py @@ -24,23 +24,14 @@ import ctypes import abc import threading -# Binary Ninja components -import _binaryninjacore as core -from enums import (AnalysisState, SymbolType, InstructionTextTokenType, +# Binary Ninja components -- additional imports belong in the appropriate class +from binaryninja import _binaryninjacore as core +from binaryninja.enums import (AnalysisState, SymbolType, InstructionTextTokenType, Endianness, ModificationStatus, StringType, SegmentFlag, SectionSemantics) -import function -import startup -import architecture -import platform -import associateddatastore -import fileaccessor -import filemetadata -import log -import databuffer -import basicblock -import types -import lineardisassembly -import metadata +from binaryninja import associateddatastore # required for _BinaryViewAssociatedDataStore + +#2-3 compatibility +from six import with_metaclass class BinaryDataNotification(object): @@ -113,12 +104,13 @@ class AnalysisCompletionEvent(object): :Example: >>> def on_complete(self): - ... print "Analysis Complete", self.view + ... print("Analysis Complete", self.view) ... >>> evt = AnalysisCompletionEvent(bv, on_complete) >>> """ def __init__(self, view, callback): + from binaryninja import log self.view = view self.callback = callback self._cb = ctypes.CFUNCTYPE(None, ctypes.c_void_p)(self._notify) @@ -197,6 +189,9 @@ class DataVariable(object): class BinaryDataNotificationCallbacks(object): def __init__(self, view, notify): + from binaryninja import function + from binaryninja import types + from binaryninja import log self.view = view self.notify = notify self._cb = core.BNBinaryDataNotification() @@ -319,6 +314,7 @@ class BinaryDataNotificationCallbacks(object): class _BinaryViewTypeMetaclass(type): + from binaryninja import startup @property def list(self): """List all BinaryView types (read-only)""" @@ -349,10 +345,12 @@ class _BinaryViewTypeMetaclass(type): return BinaryViewType(view_type) -class BinaryViewType(object): - __metaclass__ = _BinaryViewTypeMetaclass +class BinaryViewType(with_metaclass(_BinaryViewTypeMetaclass, object)): def __init__(self, handle): + from binaryninja import architecture + from binaryninja import filemetadata + from binaryninja import platform self.handle = core.handle_of_type(handle, core.BNBinaryViewType) def __eq__(self, value): @@ -581,6 +579,18 @@ class BinaryView(object): database (e.g. ``remove_user_function()`` rather than ``remove_function()``). Thus use ``_user_`` methods if saving \ to the database is desired. """ + import binaryninja.function + import binaryninja.architecture + import binaryninja.platform + import binaryninja.fileaccessor + import binaryninja.filemetadata + import binaryninja.databuffer + import binaryninja.basicblock + import binaryninja.types + import binaryninja.log + import binaryninja.startup + import binaryninja.lineardisassembly + import binaryninja.metadata name = None long_name = None _registered = False @@ -590,6 +600,18 @@ class BinaryView(object): _associated_data = {} def __init__(self, file_metadata=None, parent_view=None, handle=None): + function = binaryninja.function + architecture = binaryninja.architecture + platform = binaryninja.platform + fileaccessor = binaryninja.fileaccessor + filemetadata = binaryninja.filemetadata + databuffer = binaryninja.databuffer + basicblock = binaryninja.basicblock + types = binaryninja.types + log = binaryninja.log + startup = binaryninja.startup + lineardisassembly = binaryninja.lineardisassembly + metadata = binaryninja.metadata if handle is not None: self.handle = core.handle_of_type(handle, core.BNBinaryView) if file_metadata is None: @@ -680,7 +702,7 @@ class BinaryView(object): @classmethod def open(cls, src, file_metadata=None): - startup._init_plugins() + binaryninja.startup._init_plugins() if isinstance(src, fileaccessor.FileAccessor): if file_metadata is None: file_metadata = filemetadata.FileMetadata() @@ -2790,7 +2812,7 @@ class BinaryView(object): :Example: >>> def completionEvent(): - ... print "done" + ... print("done") ... >>> bv.add_analysis_completion_event(completionEvent) @@ -3098,7 +3120,7 @@ class BinaryView(object): >>> settings = DisassemblySettings() >>> lines = bv.get_linear_disassembly(settings) >>> for line in lines: - ... print line + ... print(line) ... break ... cf fa ed fe 07 00 00 01 ........ diff --git a/python/callingconvention.py b/python/callingconvention.py index e3ec7261..ab8058fc 100644 --- a/python/callingconvention.py +++ b/python/callingconvention.py @@ -21,17 +21,11 @@ import traceback import ctypes -# Binary Ninja components -import _binaryninjacore as core -import architecture -import log -import types -import function -import binaryview -from enums import VariableSourceType - +# Binary Ninja components -- additional imports belong in the appropriate class +from binaryninja import _binaryninjacore as core class CallingConvention(object): + from binaryninja import types name = None caller_saved_regs = [] int_arg_regs = [] @@ -48,6 +42,11 @@ class CallingConvention(object): _registered_calling_conventions = [] def __init__(self, arch=None, name=None, handle=None, confidence=types.max_confidence): + from binaryninja import architecture + from binaryninja import log + from binaryninja import function + from binaryninja import binaryview + from binaryninja.enums import VariableSourceType if handle is None: if arch is None or name is None: raise ValueError("Must specify either handle or architecture and name") diff --git a/python/databuffer.py b/python/databuffer.py index 3f9e4ce5..bf366750 100644 --- a/python/databuffer.py +++ b/python/databuffer.py @@ -20,8 +20,8 @@ import ctypes -# Binary Ninja components -import _binaryninjacore as core +# Binary Ninja components -- additional imports belong in the appropriate class +from binaryninja import _binaryninjacore as core class DataBuffer(object): diff --git a/python/demangle.py b/python/demangle.py index 11673263..50322694 100644 --- a/python/demangle.py +++ b/python/demangle.py @@ -20,9 +20,8 @@ import ctypes -# Binary Ninja components -import _binaryninjacore as core -import types +# Binary Ninja components -- additional imports belong in the appropriate class +from binaryninja import _binaryninjacore as core def get_qualified_name(names): @@ -56,6 +55,7 @@ def demangle_ms(arch, mangled_name): (, ['Foobar', 'testf']) >>> """ + from binaryninja import types handle = ctypes.POINTER(core.BNType)() outName = ctypes.POINTER(ctypes.c_char_p)() outSize = ctypes.c_ulonglong() @@ -69,6 +69,7 @@ def demangle_ms(arch, mangled_name): def demangle_gnu3(arch, mangled_name): + from binaryninja import types handle = ctypes.POINTER(core.BNType)() outName = ctypes.POINTER(ctypes.c_char_p)() outSize = ctypes.c_ulonglong() diff --git a/python/examples/README.md b/python/examples/README.md deleted file mode 100644 index 43af34be..00000000 --- a/python/examples/README.md +++ /dev/null @@ -1,56 +0,0 @@ -# Binary Ninja Python API Examples - -The following examples demonstrate some of the Binary Ninja API. They include both stand-alone examples that directly call into the core without a GUI, as well as examples meant to be loaded as plugins available from the UI. - -## Stand-alone - -These plugins only operate when run directly outside of the UI - -* bin_info.py - general binary information -* print_syscalls.py - extract syscall numbers from IL on specified file. Can be run both headless and in Binary Ninja -* version_switcher.py - uses the update API to see raw version notes and manually downgrade or upgrade -* instruction_iterator.py - very simple plugin that iterates through functions, blocks, and instructions - -To use the stand-alone Python examples, make sure your `PYTHON_PATH` includes the API, as shown below. Please note, this is a feature that requires the "GUI-less processing" capability not available in the personal edition. In the personal edition, all scripts must by run from the integrated python console (follow the directions below under "Loading Plugins" section) - -``` -PYTHONPATH=$PYTHONPATH:/Applications/Binary\ Ninja.app/Contents/Resources/python -``` - -## GUI Plugins - -These plugins require the UI to be running - -* breakpoint.py - small example showing how to modify a file and register a GUI menu item -* jump_table.py - heuristic based jump table detection for when the data-flow based computation fails, triggered by right-clicking on the location where the jump value is computed -* angr_plugin.py - a plugin to demonstrate both background threads, the simplified plugin UI elements, and highlighting -* export_svg.py - exports the graph view of a function to an SVG file for including in reports - -## Both - -These plugins are able to operate in either the GUI or as stand-alone plugins - -* nes.py - 6502 CPU architecture including LLIL lifting and `.NES` file format parser - - -## Loading Plugins - -Plugins are meant to be loaded into a running Binary Ninja GUI and should either be copied or symlinked into the appropriate plugin folder. You'll need to then re-start Binary Ninja. - -### OSX - -``` -~/Library/Application Support/Binary Ninja/plugins -``` - -### Windows - -``` -%APPDATA%\Binary Ninja\plugins -``` - -### Linux - -``` -~/.binaryninja/plugins -``` diff --git a/python/examples/angr_plugin.py b/python/examples/angr_plugin.py deleted file mode 100644 index 26f8040c..00000000 --- a/python/examples/angr_plugin.py +++ /dev/null @@ -1,153 +0,0 @@ -# Copyright (c) 2015-2017 Vector 35 LLC -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to -# deal in the Software without restriction, including without limitation the -# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -# sell copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -# IN THE SOFTWARE. - - -# This plugin assumes angr is already installed and available on the system. See the angr documentation -# for information about installing angr. It should be installed using the virtualenv method. -# -# This plugin is currently only known to work on Linux using virtualenv. Switch to the virtual environment -# (using a command such as "workon angr"), then run the Binary Ninja UI from the command line. -# -# This method is known to fail on Mac OS X as the virtualenv used by angr does not appear to provide a -# way to automatically link to the correct version of Python, even when running the UI from within the -# virtual environment. A later update may allow for a manual override to link to the required version -# of Python. - -import tempfile -import logging -import os - -__name__ = "__console__" # angr looks for this, it won't load from within a UI without it - -import angr -# For the lazy instead you can just import everything 'from binaryninja import *'' -from binaryninja.binaryview import BinaryView -from binaryninja.plugin import BackgroundTaskThread, PluginCommand -from binaryninja.interaction import show_plain_text_report, show_message_box -from binaryninja.highlight import HighlightColor -from binaryninja.enums import HighlightStandardColor, MessageBoxButtonSet, MessageBoxIcon - -# Disable warning logs as they show up as errors in the UI -logging.disable(logging.WARNING) - -# Create sets in the BinaryView's data field to store the desired path for each view -BinaryView.set_default_session_data("angr_find", set()) -BinaryView.set_default_session_data("angr_avoid", set()) - - -def escaped_output(str): - return '\n'.join([s.encode("string_escape") for s in str.split('\n')]) - - -# Define a background thread object for solving in the background -class Solver(BackgroundTaskThread): - def __init__(self, find, avoid, view): - BackgroundTaskThread.__init__(self, "Solving with angr...", True) - self.find = tuple(find) - self.avoid = tuple(avoid) - self.view = view - - # Write the binary to disk so that the angr API can read it - self.binary = tempfile.NamedTemporaryFile() - self.binary.write(view.file.raw.read(0, len(view.file.raw))) - self.binary.flush() - - def run(self): - # Create an angr project and an explorer with the user's settings - p = angr.Project(self.binary.name) - e = p.surveyors.Explorer(find = self.find, avoid = self.avoid) - - # Solve loop - while not e.done: - if self.cancelled: - # Solve cancelled, show results if there were any - if len(e.found) > 0: - break - return - - # Perform the next step in the solve - e.step() - - # Update status - active_count = len(e.active) - found_count = len(e.found) - - progress = "Solving with angr (%d active path%s" % (active_count, "s" if active_count != 1 else "") - if found_count > 0: - progress += ", %d path%s found" % (found_count, "s" if found_count != 1 else "") - self.progress = progress + ")..." - - # Solve complete, show report - text_report = "Found %d path%s.\n\n" % (len(e.found), "s" if len(e.found) != 1 else "") - i = 1 - for f in e.found: - text_report += "Path %d\n" % i + "=" * 10 + "\n" - text_report += "stdin:\n" + escaped_output(f.state.posix.dumps(0)) + "\n\n" - text_report += "stdout:\n" + escaped_output(f.state.posix.dumps(1)) + "\n\n" - text_report += "stderr:\n" + escaped_output(f.state.posix.dumps(2)) + "\n\n" - i += 1 - - name = self.view.file.filename - if len(name) > 0: - show_plain_text_report("Results from angr - " + os.path.basename(self.view.file.filename), text_report) - else: - show_plain_text_report("Results from angr", text_report) - - -def find_instr(bv, addr): - # Highlight the instruction in green - blocks = bv.get_basic_blocks_at(addr) - for block in blocks: - block.set_auto_highlight(HighlightColor(HighlightStandardColor.GreenHighlightColor, alpha = 128)) - block.function.set_auto_instr_highlight(addr, HighlightStandardColor.GreenHighlightColor) - - # Add the instruction to the list associated with the current view - bv.session_data.angr_find.add(addr) - - -def avoid_instr(bv, addr): - # Highlight the instruction in red - blocks = bv.get_basic_blocks_at(addr) - for block in blocks: - block.set_auto_highlight(HighlightColor(HighlightStandardColor.RedHighlightColor, alpha = 128)) - block.function.set_auto_instr_highlight(addr, HighlightStandardColor.RedHighlightColor) - - # Add the instruction to the list associated with the current view - bv.session_data.angr_avoid.add(addr) - - -def solve(bv): - if len(bv.session_data.angr_find) == 0: - show_message_box("Angr Solve", "You have not specified a goal instruction.\n\n" + - "Please right click on the goal instruction and select \"Find Path to This Instruction\" to " + - "continue.", MessageBoxButtonSet.OKButtonSet, MessageBoxIcon.ErrorIcon) - return - - # Start a solver thread for the path associated with the view - s = Solver(bv.session_data.angr_find, bv.session_data.angr_avoid, bv) - s.start() - - -# Register commands for the user to interact with the plugin -PluginCommand.register_for_address("Find Path to This Instruction", - "When solving, find a path that gets to this instruction", find_instr) -PluginCommand.register_for_address("Avoid This Instruction", - "When solving, avoid paths that reach this instruction", avoid_instr) -PluginCommand.register("Solve With Angr", "Attempt to solve for a path that satisfies the constraints given", solve) diff --git a/python/examples/arch_hook.py b/python/examples/arch_hook.py deleted file mode 100644 index 452bd2b6..00000000 --- a/python/examples/arch_hook.py +++ /dev/null @@ -1,16 +0,0 @@ -from binaryninja.architecture import Architecture, ArchitectureHook - -class X86ReturnHook(ArchitectureHook): - def get_instruction_text(self, data, addr): - # Call the original implementation's method by calling the superclass - result, length = super(X86ReturnHook, self).get_instruction_text(data, addr) - - # Patch the name of the 'retn' instruction to 'ret' - if len(result) > 0 and result[0].text == 'retn': - result[0].text = 'ret' - - return result, length - -# Install the hook by constructing it with the desired architecture to hook, then registering it -X86ReturnHook(Architecture['x86']).register() - diff --git a/python/examples/bin_info.py b/python/examples/bin_info.py deleted file mode 100644 index 17a96685..00000000 --- a/python/examples/bin_info.py +++ /dev/null @@ -1,72 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2015-2017 Vector 35 LLC -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to -# deal in the Software without restriction, including without limitation the -# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -# sell copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -# IN THE SOFTWARE. - -import sys -import binaryninja.log as log -from binaryninja.binaryview import BinaryViewType -import binaryninja.interaction as interaction -from binaryninja.plugin import PluginCommand - - -def get_bininfo(bv): - if bv is None: - filename = "" - if len(sys.argv) > 1: - filename = sys.argv[1] - else: - filename = interaction.get_open_filename_input("Filename:") - if filename is None: - log.log_warn("No file specified") - sys.exit(1) - - bv = BinaryViewType.get_view_of_file(filename) - log.log_to_stdout(True) - - contents = "## %s ##\n" % bv.file.filename - contents += "- START: 0x%x\n\n" % bv.start - contents += "- ENTRY: 0x%x\n\n" % bv.entry_point - contents += "- ARCH: %s\n\n" % bv.arch.name - contents += "### First 10 Functions ###\n" - - contents += "| Start | Name |\n" - contents += "|------:|:-------|\n" - for i in xrange(min(10, len(bv.functions))): - contents += "| 0x%x | %s |\n" % (bv.functions[i].start, bv.functions[i].symbol.full_name) - - contents += "### First 10 Strings ###\n" - contents += "| Start | Length | String |\n" - contents += "|------:|-------:|:-------|\n" - for i in xrange(min(10, len(bv.strings))): - start = bv.strings[i].start - length = bv.strings[i].length - string = bv.read(start, length) - contents += "| 0x%x |%d | %s |\n" % (start, length, string) - return contents - - -def display_bininfo(bv): - interaction.show_markdown_report("Binary Info Report", get_bininfo(bv)) - - -if __name__ == "__main__": - print get_bininfo(None) -else: - PluginCommand.register("Binary Info", "Display basic info about the binary", display_bininfo) diff --git a/python/examples/breakpoint.py b/python/examples/breakpoint.py deleted file mode 100644 index a2801511..00000000 --- a/python/examples/breakpoint.py +++ /dev/null @@ -1,50 +0,0 @@ -# Copyright (c) 2015-2017 Vector 35 LLC -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to -# deal in the Software without restriction, including without limitation the -# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -# sell copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -# IN THE SOFTWARE. - - -from binaryninja.plugin import PluginCommand -from binaryninja.log import log_error - - -def write_breakpoint(view, start, length): - """Sample function to show registering a plugin menu item for a range of bytes. Also possible: - register - register_for_address - register_for_function - """ - bkpt_str = { - "x86": "int3", - "x86_64": "int3", - "armv7": "bkpt", - "aarch64": "brk #0", - "mips32": "break"} - - if view.arch.name not in bkpt_str: - log_error("Architecture %s not supported" % view.arch.name) - return - - bkpt, err = view.arch.assemble(bkpt_str[view.arch.name]) - if bkpt is None: - log_error(err) - return - view.write(start, bkpt * length / len(bkpt)) - - -PluginCommand.register_for_range("Convert to breakpoint", "Fill region with breakpoint instructions.", write_breakpoint) diff --git a/python/examples/export_svg.py b/python/examples/export_svg.py deleted file mode 100755 index 7814c9fd..00000000 --- a/python/examples/export_svg.py +++ /dev/null @@ -1,224 +0,0 @@ -# from binaryninja import * -import os -import webbrowser -import time -try: - from urllib import pathname2url # Python 2.x -except: - from urllib.request import pathname2url # Python 3.x - -from binaryninja.interaction import get_save_filename_input, show_message_box -from binaryninja.enums import MessageBoxButtonSet, MessageBoxIcon, MessageBoxButtonResult, InstructionTextTokenType, BranchType -from binaryninja.plugin import PluginCommand - -colors = {'green': [162, 217, 175], 'red': [222, 143, 151], 'blue': [128, 198, 233], 'cyan': [142, 230, 237], 'lightCyan': [176, 221, 228], 'orange': [237, 189, 129], 'yellow': [237, 223, 179], 'magenta': [218, 196, 209], 'none': [74, 74, 74]} - -escape_table = { - "'": "'", - ">": ">", - "<": "<", - '"': """, - ' ': " " -} - - -def escape(toescape): - toescape = toescape.decode('utf-8').encode('ascii', 'xmlcharrefreplace') # handle extended unicode - return ''.join(escape_table.get(i, i) for i in toescape) # still escape the basics - - -def save_svg(bv, function): - address = hex(function.start).replace('L', '') - path = os.path.dirname(bv.file.filename) - origname = os.path.basename(bv.file.filename) - filename = os.path.join(path, 'binaryninja-{filename}-{function}.html'.format(filename=origname, function=address)) - outputfile = get_save_filename_input('File name for export_svg', 'HTML files (*.html)', filename) - if outputfile is None: - return - content = render_svg(function, origname) - output = open(outputfile, 'w') - output.write(content) - output.close() - result = show_message_box("Open SVG", "Would you like to view the exported SVG?", - buttons = MessageBoxButtonSet.YesNoButtonSet, icon = MessageBoxIcon.QuestionIcon) - if result == MessageBoxButtonResult.YesButton: - url = 'file:{}'.format(pathname2url(outputfile)) - webbrowser.open(url) - - -def instruction_data_flow(function, address): - ''' TODO: Extract data flow information ''' - length = function.view.get_instruction_length(address) - bytes = function.view.read(address, length) - hex = bytes.encode('hex') - padded = ' '.join([hex[i:i + 2] for i in range(0, len(hex), 2)]) - return 'Opcode: {bytes}'.format(bytes=padded) - - -def render_svg(function, origname): - graph = function.create_graph() - graph.layout_and_wait() - heightconst = 15 - ratio = 0.48 - widthconst = heightconst * ratio - - output = ''' - - - - -''' - output += ''' - - - - - - - - - - - - - - - '''.format(width=graph.width * widthconst + 20, height=graph.height * heightconst + 20) - output += ''' - Function Graph 0 - ''' - edges = '' - for i, block in enumerate(graph.blocks): - - # Calculate basic block location and coordinates - x = ((block.x) * widthconst) - y = ((block.y) * heightconst) - width = ((block.width) * widthconst) - height = ((block.height) * heightconst) - - # Render block - output += ' \n'.format(i=i) - output += ' Basic Block {i}\n'.format(i=i) - rgb = colors['none'] - try: - bb = block.basic_block - color_code = bb.highlight.color - color_str = bb.highlight._standard_color_to_str(color_code) - if color_str in colors: - rgb = colors[color_str] - except: - pass - output += ' \n'.format(x=x, y=y, width=width + 16, height=height + 12, r=rgb[0], g=rgb[1], b=rgb[2]) - - # Render instructions, unfortunately tspans don't allow copying/pasting more - # than one line at a time, need SVG 1.2 textarea tags for that it looks like - - output += ' \n'.format(x=x, y=y + (i + 1) * heightconst) - for i, line in enumerate(block.lines): - output += ' '.format(x=x + 6, y=y + 6 + (i + 0.7) * heightconst, address=hex(line.address)[:-1]) - hover = instruction_data_flow(function, line.address) - output += '{hover}'.format(hover=hover) - for token in line.tokens: - # TODO: add hover for hex, function, and reg tokens - output += '{text}'.format(text=escape(token.text), tokentype=InstructionTextTokenType(token.type).name) - output += '\n' - output += ' \n' - output += ' \n' - - # Edges are rendered in a seperate chunk so they have priority over the - # basic blocks or else they'd render below them - - for edge in block.outgoing_edges: - points = "" - x, y = edge.points[0] - points += str(x * widthconst) + "," + str(y * heightconst + 12) + " " - for x, y in edge.points[1:-1]: - points += str(x * widthconst) + "," + str(y * heightconst) + " " - x, y = edge.points[-1] - points += str(x * widthconst) + "," + str(y * heightconst + 0) + " " - if edge.back_edge: - edges += ' \n'.format(type=BranchType(edge.type).name, points=points) - else: - edges += ' \n'.format(type=BranchType(edge.type).name, points=points) - output += ' ' + edges + '\n' - output += ' \n' - output += '' - - output += '

This CFG generated by Binary Ninja from {filename} on {timestring}.

'.format(filename = origname, timestring = time.strftime("%c")) - output += '' - return output - - -PluginCommand.register_for_function("Export to SVG", "Exports an SVG of the current function", save_svg) diff --git a/python/examples/instruction_iterator.py b/python/examples/instruction_iterator.py deleted file mode 100644 index f55e1c1b..00000000 --- a/python/examples/instruction_iterator.py +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2015-2017 Vector 35 LLC -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to -# deal in the Software without restriction, including without limitation the -# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -# sell copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -# IN THE SOFTWARE. - -import sys -import binaryninja as binja - -if len(sys.argv) > 1: - target = sys.argv[1] - -bv = binja.BinaryViewType.get_view_of_file(target) -binja.log_to_stdout(True) -binja.log_info("-------- %s --------" % target) -binja.log_info("START: 0x%x" % bv.start) -binja.log_info("ENTRY: 0x%x" % bv.entry_point) -binja.log_info("ARCH: %s" % bv.arch.name) -binja.log_info("\n-------- Function List --------") - -""" print all the functions, their basic blocks, and their il instructions """ -for func in bv.functions: - binja.log_info(repr(func)) - for block in func.low_level_il: - binja.log_info("\t{0}".format(block)) - - for insn in block: - binja.log_info("\t\t{0}".format(insn)) - - -""" print all the functions, their basic blocks, and their mc instructions """ -for func in bv.functions: - binja.log_info(repr(func)) - for block in func: - binja.log_info("\t{0}".format(block)) - - for insn in block: - binja.log_info("\t\t{0}".format(insn)) diff --git a/python/examples/jump_table.py b/python/examples/jump_table.py deleted file mode 100644 index 419cc188..00000000 --- a/python/examples/jump_table.py +++ /dev/null @@ -1,86 +0,0 @@ -# Copyright (c) 2015-2017 Vector 35 LLC -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to -# deal in the Software without restriction, including without limitation the -# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -# sell copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -# IN THE SOFTWARE. - -# This plugin will attempt to resolve simple jump tables (an array of code pointers) and add the destinations -# as indirect branch targets so that the flow graph reflects the jump table's control flow. -from binaryninja.plugin import PluginCommand -from binaryninja.enums import InstructionTextTokenType -import struct - - -def find_jump_table(bv, addr): - for block in bv.get_basic_blocks_at(addr): - func = block.function - arch = func.arch - addrsize = arch.address_size - - # Grab the instruction tokens so that we can look for the table's starting address - tokens, length = arch.get_instruction_text(bv.read(addr, 16), addr) - - # Look for the next jump instruction, which may be the current instruction. Some jump tables will - # compute the address first then jump to the computed address as a separate instruction. - jump_addr = addr - while jump_addr < block.end: - info = arch.get_instruction_info(bv.read(jump_addr, 16), jump_addr) - if len(info.branches) != 0: - break - jump_addr += info.length - if jump_addr >= block.end: - print "Unable to find jump after instruction 0x%x" % addr - continue - print "Jump at 0x%x" % jump_addr - - # Collect the branch targets for any tables referenced by the clicked instruction - branches = [] - for token in tokens: - if InstructionTextTokenType(token.type) == InstructionTextTokenType.PossibleAddressToken: # Table addresses will be a "possible address" token - tbl = token.value - print "Found possible table at 0x%x" % tbl - i = 0 - while True: - # Read the next pointer from the table - data = bv.read(tbl + (i * addrsize), addrsize) - if len(data) == addrsize: - if addrsize == 4: - ptr = struct.unpack("= bv.start) and (ptr < bv.end): - print "Found destination 0x%x" % ptr - branches.append((arch, ptr)) - else: - # Once a value that is not a pointer is encountered, the jump table is ended - break - else: - # Reading invalid memory - break - - i += 1 - - # Set the indirect branch targets on the jump instruction to be the list of targets discovered - func.set_user_indirect_branches(jump_addr, branches) - - -# Create a plugin command so that the user can right click on an instruction referencing a jump table and -# invoke the command -PluginCommand.register_for_address("Process jump table", "Look for jump table destinations", find_jump_table) diff --git a/python/examples/nds.py b/python/examples/nds.py deleted file mode 100644 index 34d8a292..00000000 --- a/python/examples/nds.py +++ /dev/null @@ -1,117 +0,0 @@ -# Copyright (c) 2015-2017 Vector 35 LLC -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to -# deal in the Software without restriction, including without limitation the -# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -# sell copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -# IN THE SOFTWARE. - -# from binaryninja import * -from binaryninja.binaryview import BinaryView -from binaryninja.architecture import Architecture -from binaryninja.enums import SegmentFlag -from binaryninja.log import log_error - -import struct -import traceback - - -def crc16(data): - crc = 0xffff - for ch in data: - crc ^= ord(ch) - for bit in xrange(0, 8): - if (crc & 1) == 1: - crc = (crc >> 1) ^ 0xa001 - else: - crc >>= 1 - return crc - - -class DSView(BinaryView): - def __init__(self, data): - BinaryView.__init__(self, file_metadata = data.file, parent_view = data) - self.raw = data - - @classmethod - def is_valid_for_data(self, data): - hdr = data.read(0, 0x160) - if len(hdr) < 0x160: - return False - if struct.unpack("> 4) - self.ram_banks = struct.unpack("B", hdr[8])[0] - self.rom_offset = 16 - if self.rom_flags & 4: - self.rom_offset += 512 - self.rom_length = self.rom_banks * 0x4000 - - # Add mapping for RAM and hardware registers, not backed by file contents - self.add_auto_segment(0, 0x8000, 0, 0, SegmentFlag.SegmentReadable | SegmentFlag.SegmentWritable | SegmentFlag.SegmentExecutable) - - # Add ROM mappings - self.add_auto_segment(0x8000, 0x4000, self.rom_offset + (self.__class__.bank * 0x4000), 0x4000, - SegmentFlag.SegmentReadable | SegmentFlag.SegmentExecutable) - self.add_auto_segment(0xc000, 0x4000, self.rom_offset + self.rom_length - 0x4000, 0x4000, - SegmentFlag.SegmentReadable | SegmentFlag.SegmentExecutable) - - nmi = struct.unpack("= 0x8000: - self.add_function(addr) - - return True - except: - log_error(traceback.format_exc()) - return False - - def perform_is_executable(self): - return True - - def perform_get_entry_point(self): - return struct.unpack("'.format(sys.argv[0])) - else: - print_syscalls(sys.argv[1]) diff --git a/python/examples/version_switcher.py b/python/examples/version_switcher.py deleted file mode 100644 index 3e1cab40..00000000 --- a/python/examples/version_switcher.py +++ /dev/null @@ -1,150 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2015-2017 Vector 35 LLC -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to -# deal in the Software without restriction, including without limitation the -# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -# sell copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -# IN THE SOFTWARE. - -import sys - -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 = UpdateChannel.list[0].name -channel = None -versions = [] - - -def load_channel(newchannel): - global channel - global versions - if (channel is not None and newchannel == channel.name): - print "Same channel, not updating." - else: - try: - print "Loading channel %s" % newchannel - channel = UpdateChannel[newchannel] - print "Loading versions..." - versions = channel.versions - except Exception: - print "%s is not a valid channel name. Defaulting to " % chandefault - channel = UpdateChannel[chandefault] - - -def select(version): - done = False - date = datetime.datetime.fromtimestamp(version.time).strftime('%c') - while not done: - print "Version:\t%s" % version.version - print "Updated:\t%s" % date - print "Notes:\n\n-----\n%s" % version.notes - print "-----" - print "\t1)\tSwitch to version" - print "\t2)\tMain Menu" - selection = raw_input('Choice: ') - if selection.isdigit(): - selection = int(selection) - else: - selection = 0 - if (selection == 2): - done = True - elif (selection == 1): - if (version.version == channel.latest_version.version): - print "Requesting update to latest version." - else: - print "Requesting update to prior version." - if are_auto_updates_enabled(): - print "Disabling automatic updates." - set_auto_updates_enabled(False) - if (version.version == core_version): - print "Already running %s" % version.version - else: - print "version.version %s" % version.version - 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: - print "Invalid selection" - - -def list_channels(): - done = False - print "\tSelect channel:\n" - while not done: - 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") - selection = raw_input('Choice: ') - if selection.isdigit(): - selection = int(selection) - else: - selection = 0 - if (selection <= 0 or selection > len(channel_list) + 1): - print "%s is an invalid choice." % selection - else: - done = True - if (selection != len(channel_list) + 1): - load_channel(channel_list[selection - 1].name) - - -def toggle_updates(): - set_auto_updates_enabled(not are_auto_updates_enabled()) - - -def main(): - global channel - done = False - load_channel(chandefault) - while not done: - print "\n\tBinary Ninja Version Switcher" - print "\t\tCurrent Channel:\t%s" % channel.name - 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) - print "\t%d)\t%s" % (len(versions) + 1, "Switch Channel") - print "\t%d)\t%s" % (len(versions) + 2, "Toggle Auto Updates") - print "\t%d)\t%s" % (len(versions) + 3, "Exit") - selection = raw_input('Choice: ') - if selection.isdigit(): - selection = int(selection) - else: - selection = 0 - if (selection <= 0 or selection > len(versions) + 3): - print "%d is an invalid choice.\n\n" % selection - else: - if (selection == len(versions) + 3): - done = True - elif (selection == len(versions) + 2): - toggle_updates() - elif (selection == len(versions) + 1): - list_channels() - else: - select(versions[selection - 1]) - - -if __name__ == "__main__": - main() diff --git a/python/fileaccessor.py b/python/fileaccessor.py index 2c1f1d19..5a602b89 100644 --- a/python/fileaccessor.py +++ b/python/fileaccessor.py @@ -21,12 +21,11 @@ import traceback import ctypes -# Binary Ninja components -import _binaryninjacore as core -import log - +# Binary Ninja components -- additional imports belong in the appropriate class +from binaryninja import _binaryninjacore as core class FileAccessor(object): + from binaryninja import log def __init__(self): self._cb = core.BNFileAccessor() self._cb.context = 0 diff --git a/python/filemetadata.py b/python/filemetadata.py index 4bfc0214..695b95f3 100644 --- a/python/filemetadata.py +++ b/python/filemetadata.py @@ -18,18 +18,17 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. +from __future__ import absolute_import import traceback import ctypes -# Binary Ninja components -import _binaryninjacore as core -import startup -import associateddatastore -import log -import binaryview +# Binary Ninja components -- additional imports belong in the appropriate class +from binaryninja import _binaryninjacore as core +from binaryninja import associateddatastore #required for _FileMetadataAssociatedDataStore class NavigationHandler(object): + from binaryninja import log def _register(self, handle): self._cb = core.BNNavigationHandler() self._cb.context = 0 @@ -66,12 +65,14 @@ class _FileMetadataAssociatedDataStore(associateddatastore._AssociatedDataStore) class FileMetadata(object): - _associated_data = {} - """ ``class FileMetadata`` represents the file being analyzed by Binary Ninja. It is responsible for opening, closing, creating the database (.bndb) files, and is used to keep track of undoable actions. """ + from binaryninja import startup + from binaryninja import binaryview + _associated_data = {} + def __init__(self, filename = None, handle = None): """ Instantiates a new FileMetadata class. diff --git a/python/function.py b/python/function.py index 2f794f47..2ee9b20d 100644 --- a/python/function.py +++ b/python/function.py @@ -18,27 +18,18 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. +from __future__ import absolute_import import threading import traceback import ctypes -# Binary Ninja components -import _binaryninjacore as core -from enums import (AnalysisSkipReason, FunctionGraphType, BranchType, SymbolType, InstructionTextTokenType, +# Binary Ninja components -- additional imports belong in the appropriate class +from binaryninja import _binaryninjacore as core +from binaryninja.enums import (AnalysisSkipReason, FunctionGraphType, BranchType, SymbolType, InstructionTextTokenType, HighlightStandardColor, HighlightColorStyle, RegisterValueType, ImplicitRegisterExtend, DisassemblyOption, IntegerDisplayType, InstructionTextTokenContext, VariableSourceType, FunctionAnalysisSkipOverride) -import architecture -import platform -import highlight -import associateddatastore -import types -import basicblock -import lowlevelil -import mediumlevelil -import binaryview -import log -import callingconvention +from binaryninja import associateddatastore #required in the main scope due to being an argument for _FunctionAssociatedDataStore class LookupTableEntry(object): @@ -51,6 +42,7 @@ class LookupTableEntry(object): class RegisterValue(object): + from binaryninja import types def __init__(self, arch = None, value = None, confidence = types.max_confidence): self.is_constant = False if value is None: @@ -329,6 +321,7 @@ class IndirectBranchInfo(object): class ParameterVariables(object): + from binaryninja import types def __init__(self, var_list, confidence = types.max_confidence): self.vars = var_list self.confidence = confidence @@ -355,6 +348,14 @@ class _FunctionAssociatedDataStore(associateddatastore._AssociatedDataStore): class Function(object): + from binaryninja import callingconvention + from binaryninja import mediumlevelil + from binaryninja import lowlevelil + from binaryninja import basicblock + from binaryninja import types + from binaryninja import highlight + from binaryninja import platform + from binaryninja import architecture _associated_data = {} def __init__(self, view, handle): @@ -1631,7 +1632,8 @@ class FunctionGraphEdge(object): class FunctionGraphBlock(object): - def __init__(self, handle, graph): + def __init__(self, handle): + from binaryninja import binaryview self.handle = handle self.graph = graph @@ -1839,6 +1841,8 @@ class DisassemblySettings(object): class FunctionGraph(object): + from binaryninja import log + from binaryninja import types def __init__(self, view, handle): self.view = view self.handle = handle @@ -2140,6 +2144,7 @@ class InstructionTextToken(object): ========================== ============================================ """ + from binaryninja import types def __init__(self, token_type, text, value = 0, size = 0, operand = 0xffffffff, context = InstructionTextTokenContext.NoTokenContext, address = 0, confidence = types.max_confidence): self.type = InstructionTextTokenType(token_type) diff --git a/python/functionrecognizer.py b/python/functionrecognizer.py index 4ac304ca..158a0700 100644 --- a/python/functionrecognizer.py +++ b/python/functionrecognizer.py @@ -20,16 +20,16 @@ import traceback -# Binary Ninja components -import _binaryninjacore as core -import function -import filemetadata -import binaryview -import lowlevelil -import log +# Binary Ninja components -- additional imports belong in the appropriate class +from binaryninja import _binaryninjacore as core class FunctionRecognizer(object): + from binaryninja import function + from binaryninja import filemetadata + from binaryninja import binaryview + from binaryninja import lowlevelil + from binaryninja import log _instance = None def __init__(self): diff --git a/python/generator.cpp b/python/generator.cpp index f838b36d..bb32ab69 100644 --- a/python/generator.cpp +++ b/python/generator.cpp @@ -374,10 +374,10 @@ int main(int argc, char* argv[]) fprintf(out, "def handle_of_type(value, handle_type):\n"); fprintf(out, "\tif isinstance(value, ctypes.POINTER(handle_type)) or isinstance(value, ctypes.c_void_p):\n"); fprintf(out, "\t\treturn ctypes.cast(value, ctypes.POINTER(handle_type))\n"); - fprintf(out, "\traise ValueError, 'expected pointer to %%s' %% str(handle_type)\n"); + fprintf(out, "\traise ValueError('expected pointer to %%s' %% str(handle_type))\n"); fprintf(out, "\n# Set path for core plugins\n"); - fprintf(out, "BNSetBundledPluginDirectory(os.path.join(_base_path, \"plugins\"))\n"); + fprintf(out, "BNSetBundledPluginDirectory(os.path.join(_base_path, \"plugins\").encode('utf-8'))\n"); fclose(out); fclose(enums); diff --git a/python/highlight.py b/python/highlight.py index 96bc543d..b04561bd 100644 --- a/python/highlight.py +++ b/python/highlight.py @@ -19,9 +19,9 @@ # IN THE SOFTWARE. -# Binary Ninja components -import _binaryninjacore as core -from enums import HighlightColorStyle, HighlightStandardColor +# Binary Ninja components -- additional imports belong in the appropriate class +from binaryninja import _binaryninjacore as core +from binaryninja.enums import HighlightColorStyle, HighlightStandardColor class HighlightColor(object): diff --git a/python/interaction.py b/python/interaction.py index 5b7ec497..8507682c 100644 --- a/python/interaction.py +++ b/python/interaction.py @@ -21,11 +21,9 @@ import ctypes import traceback -# Binary Ninja components -import _binaryninjacore as core -from enums import FormInputFieldType, MessageBoxIcon, MessageBoxButtonSet, MessageBoxButtonResult -import binaryview -import log +# Binary Ninja components -- additional imports belong in the appropriate class +from binaryninja import _binaryninjacore as core +from binaryninja.enums import FormInputFieldType, MessageBoxIcon, MessageBoxButtonSet, MessageBoxButtonResult class LabelField(object): @@ -241,6 +239,8 @@ class DirectoryNameField(object): class InteractionHandler(object): + from binaryninja import log + from binaryninja import binaryview _interaction_handler = None def __init__(self): @@ -724,7 +724,7 @@ def get_form_input(fields, title): 3) Maybe Options 1 >>> True - >>> print tex_f.result, int_f.result, choice_f.result + >>> print(tex_f.result, int_f.result, choice_f.result) Peter 1337 0 """ value = (core.BNFormInputField * len(fields))() diff --git a/python/log.py b/python/log.py index b1fd7095..e7cd956d 100644 --- a/python/log.py +++ b/python/log.py @@ -19,9 +19,8 @@ # IN THE SOFTWARE. -# Binary Ninja components -import _binaryninjacore as core -from enums import LogLevel +# Binary Ninja components -- additional imports belong in the appropriate class +from binaryninja import _binaryninjacore as core _output_to_log = False diff --git a/python/lowlevelil.py b/python/lowlevelil.py index bd1e9349..3b12c118 100644 --- a/python/lowlevelil.py +++ b/python/lowlevelil.py @@ -19,15 +19,13 @@ # IN THE SOFTWARE. import ctypes - -# Binary Ninja components -import _binaryninjacore as core -from .enums import LowLevelILOperation, LowLevelILFlagCondition, InstructionTextTokenType -import function -import basicblock -import mediumlevelil import struct +# Binary Ninja components -- additional imports belong in the appropriate class +from binaryninja import _binaryninjacore as core +from binaryninja.enums import LowLevelILOperation, LowLevelILFlagCondition, InstructionTextTokenType +from binaryninja import basicblock #required for LowLevelILBasicBlock + class LowLevelILLabel(object): def __init__(self, handle = None): @@ -342,6 +340,8 @@ class LowLevelILInstruction(object): } def __init__(self, func, expr_index, instr_index=None): + from binaryninja import function + from binaryninja import mediumlevelil instr = core.BNGetLowLevelILByIndex(func.handle, expr_index) self.function = func self.expr_index = expr_index @@ -441,7 +441,7 @@ class LowLevelILInstruction(object): operand_list = core.BNLowLevelILGetOperandList(func.handle, self.expr_index, i, count) i += 1 value = [] - for j in xrange(count.value / 2): + for j in xrange(count.value // 2): reg = operand_list[j * 2] reg_version = operand_list[(j * 2) + 1] value.append(SSARegister(ILRegister(func.arch, reg), reg_version)) @@ -451,7 +451,7 @@ class LowLevelILInstruction(object): operand_list = core.BNLowLevelILGetOperandList(func.handle, self.expr_index, i, count) i += 1 value = [] - for j in xrange(count.value / 2): + for j in xrange(count.value // 2): reg_stack = operand_list[j * 2] reg_version = operand_list[(j * 2) + 1] value.append(SSARegisterStack(ILRegisterStack(func.arch, reg_stack), reg_version)) @@ -461,7 +461,7 @@ class LowLevelILInstruction(object): operand_list = core.BNLowLevelILGetOperandList(func.handle, self.expr_index, i, count) i += 1 value = [] - for j in xrange(count.value / 2): + for j in xrange(count.value // 2): flag = operand_list[j * 2] flag_version = operand_list[(j * 2) + 1] value.append(SSAFlag(ILFlag(func.arch, flag), flag_version)) @@ -471,7 +471,7 @@ class LowLevelILInstruction(object): operand_list = core.BNLowLevelILGetOperandList(func.handle, self.expr_index, i, count) i += 1 value = [] - for j in xrange(count.value / 2): + for j in xrange(count.value // 2): if (operand_list[j * 2] & (1 << 32)) != 0: reg_or_flag = ILFlag(func.arch, operand_list[j * 2] & 0xffffffff) else: @@ -484,7 +484,7 @@ class LowLevelILInstruction(object): operand_list = core.BNLowLevelILGetOperandList(func.handle, self.expr_index, i, count) i += 1 value = {} - for j in xrange(count.value / 2): + for j in xrange(count.value // 2): reg_stack = operand_list[j * 2] adjust = operand_list[(j * 2) + 1] if adjust & 0x80000000: @@ -720,6 +720,7 @@ class LowLevelILFunction(object): LLFC_NO !overflow No overflow ======================= ========== =============================== """ + from binaryninja import mediumlevelil def __init__(self, arch, handle = None, source_func = None): self.arch = arch self.source_function = source_func diff --git a/python/mainthread.py b/python/mainthread.py index 3e12bd65..16938f1a 100644 --- a/python/mainthread.py +++ b/python/mainthread.py @@ -18,9 +18,9 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. -# Binary Ninja components -import _binaryninjacore as core -import scriptingprovider +# Binary Ninja components -- additional imports belong in the appropriate class +from binaryninja import _binaryninjacore as core +from binaryninja import scriptingprovider def execute_on_main_thread(func): diff --git a/python/mediumlevelil.py b/python/mediumlevelil.py index 100795fe..9a21bb23 100644 --- a/python/mediumlevelil.py +++ b/python/mediumlevelil.py @@ -19,16 +19,13 @@ # IN THE SOFTWARE. import ctypes - -# Binary Ninja components -import _binaryninjacore as core -from .enums import MediumLevelILOperation, InstructionTextTokenType, ILBranchDependence -import function -import basicblock -import lowlevelil -import types import struct +# Binary Ninja components -- additional imports belong in the appropriate class +from binaryninja import _binaryninjacore as core +from binaryninja.enums import MediumLevelILOperation, InstructionTextTokenType, ILBranchDependence +from binaryninja import basicblock #required for MediumLevelILBasicBlock argument + class SSAVariable(object): def __init__(self, var, version): @@ -208,6 +205,9 @@ class MediumLevelILInstruction(object): } def __init__(self, func, expr_index, instr_index=None): + from binaryninja import function + from binaryninja import types + from binaryninja import lowlevelil instr = core.BNGetMediumLevelILByIndex(func.handle, expr_index) self.function = func self.expr_index = expr_index @@ -272,7 +272,7 @@ class MediumLevelILInstruction(object): operand_list = core.BNMediumLevelILGetOperandList(func.handle, self.expr_index, i, count) i += 1 value = [] - for j in xrange(count.value / 2): + for j in xrange(count.value // 2): var_id = operand_list[j * 2] var_version = operand_list[(j * 2) + 1] value.append(SSAVariable(function.Variable.from_identifier(self.function.source_function, diff --git a/python/metadata.py b/python/metadata.py index 554bbcf4..21e74533 100644 --- a/python/metadata.py +++ b/python/metadata.py @@ -19,11 +19,12 @@ # IN THE SOFTWARE. +from __future__ import absolute_import import ctypes -# Binary Ninja components -import _binaryninjacore as core -from enums import MetadataType +# Binary Ninja components -- additional imports belong in the appropriate class +from binaryninja import _binaryninjacore as core +from binaryninja.enums import MetadataType class Metadata(object): diff --git a/python/platform.py b/python/platform.py index dd756178..ff59be50 100644 --- a/python/platform.py +++ b/python/platform.py @@ -20,15 +20,16 @@ import ctypes -# Binary Ninja components -import _binaryninjacore as core -import startup -import architecture -import callingconvention -import types +# Binary Ninja components -- additional imports belong in the appropriate class +from binaryninja import _binaryninjacore as core + +#2-3 compatibility +from six import with_metaclass class _PlatformMetaClass(type): + from binaryninja import startup + from binaryninja import types @property def list(self): startup._init_plugins() @@ -90,12 +91,14 @@ class _PlatformMetaClass(type): return result -class Platform(object): +class Platform(with_metaclass(_PlatformMetaClass, object)): """ ``class Platform`` contains all information releated to the execution environment of the binary, mainly the calling conventions used. """ - __metaclass__ = _PlatformMetaClass + from binaryninja import architecture + from binaryninja import callingconvention + from binaryninja import types name = None def __init__(self, arch, handle = None): diff --git a/python/plugin.py b/python/plugin.py index 13ca51af..5dad9fb6 100644 --- a/python/plugin.py +++ b/python/plugin.py @@ -22,16 +22,12 @@ import traceback import ctypes import threading -# Binary Ninja components -import _binaryninjacore as core -from enums import PluginCommandType -import startup -import filemetadata -import binaryview -import function -import log -import lowlevelil -import mediumlevelil +# Binary Ninja components -- additional imports belong in the appropriate class +from binaryninja import _binaryninjacore as core +from binaryninja.enums import PluginCommandType + +#2-3 compatibility +from six import with_metaclass class PluginCommandContext(object): @@ -44,6 +40,7 @@ class PluginCommandContext(object): class _PluginCommandMetaClass(type): + from binaryninja import startup @property def list(self): startup._init_plugins() @@ -72,9 +69,13 @@ class _PluginCommandMetaClass(type): raise AttributeError("attribute '%s' is read only" % name) -class PluginCommand(object): +class PluginCommand(with_metaclass(_PluginCommandMetaClass, object)): + from binaryninja import startup + from binaryninja import filemetadata + from binaryninja import binaryview + from binaryninja import function + from binaryninja import log _registered_commands = [] - __metaclass__ = _PluginCommandMetaClass def __init__(self, cmd): self.command = core.BNPluginCommand() @@ -536,6 +537,7 @@ class MainThreadAction(object): class MainThreadActionHandler(object): + from binaryninja import log _main_thread = None def __init__(self): @@ -580,8 +582,7 @@ class _BackgroundTaskMetaclass(type): core.BNFreeBackgroundTaskList(tasks, count.value) -class BackgroundTask(object): - __metaclass__ = _BackgroundTaskMetaclass +class BackgroundTask(with_metaclass(_BackgroundTaskMetaclass, object)): def __init__(self, initial_progress_text = "", can_cancel = False, handle = None): if handle is None: diff --git a/python/pluginmanager.py b/python/pluginmanager.py index 0cc43aca..66f642c3 100644 --- a/python/pluginmanager.py +++ b/python/pluginmanager.py @@ -20,10 +20,8 @@ import ctypes -# Binary Ninja components -import _binaryninjacore as core -from .enums import PluginType, PluginUpdateStatus -import startup +# Binary Ninja components -- additional imports belong in the appropriate class +from binaryninja import _binaryninjacore as core class RepoPlugin(object): @@ -31,6 +29,7 @@ class RepoPlugin(object): ``RepoPlugin` is mostly read-only, however you can install/uninstall enable/disable plugins. RepoPlugins are created by parsing the plugins.json in a plugin repository. """ + from binaryninja.enums import PluginType, PluginUpdateStatus def __init__(self, handle): raise Exception("RepoPlugin temporarily disabled!") self.handle = core.handle_of_type(handle, core.BNRepoPlugin) @@ -200,6 +199,7 @@ class RepositoryManager(object): ``RepositoryManager`` Keeps track of all the repositories and keeps the enabled_plugins.json file coherent with the plugins that are installed/unstalled enabled/disabled """ + from binaryninja import startup def __init__(self, handle=None): raise Exception("RepositoryManager temporarily disabled!") self.handle = core.BNGetRepositoryManager() diff --git a/python/scriptingprovider.py b/python/scriptingprovider.py index 9cacc6ad..ac0ffd4c 100644 --- a/python/scriptingprovider.py +++ b/python/scriptingprovider.py @@ -26,14 +26,12 @@ import threading import abc import sys -# Binary Ninja Components -import _binaryninjacore as core -from enums import ScriptingProviderExecuteResult, ScriptingProviderInputReadyState -import binaryview -import function -import basicblock -import startup -import log +# Binary Ninja components -- additional imports belong in the appropriate class +from binaryninja import _binaryninjacore as core +from binaryninja.enums import ScriptingProviderExecuteResult, ScriptingProviderInputReadyState + +#2-3 compatibility +from six import with_metaclass class _ThreadActionContext(object): @@ -60,6 +58,7 @@ class _ThreadActionContext(object): class ScriptingOutputListener(object): + from binaryninja import log def _register(self, handle): self._cb = core.BNScriptingOutputListener() self._cb.context = 0 @@ -100,6 +99,10 @@ class ScriptingOutputListener(object): class ScriptingInstance(object): + from binaryninja import binaryview + from binaryninja import basicblock + from binaryninja import log + from binaryninja import function def __init__(self, provider, handle = None): if handle is None: self._cb = core.BNScriptingInstanceCallbacks() @@ -256,6 +259,7 @@ class ScriptingInstance(object): class _ScriptingProviderMetaclass(type): + from binaryninja import startup @property def list(self): """List all ScriptingProvider types (read-only)""" @@ -292,8 +296,8 @@ class _ScriptingProviderMetaclass(type): raise AttributeError("attribute '%s' is read only" % name) -class ScriptingProvider(object): - __metaclass__ = _ScriptingProviderMetaclass +class ScriptingProvider(with_metaclass(_ScriptingProviderMetaclass, object)): + from binaryninja import log name = None instance_class = None @@ -335,6 +339,7 @@ class ScriptingProvider(object): class _PythonScriptingInstanceOutput(object): + from binaryninja import log def __init__(self, orig, is_error): self.orig = orig self.is_error = is_error diff --git a/python/setting.py b/python/setting.py index d58c8955..7cde3865 100644 --- a/python/setting.py +++ b/python/setting.py @@ -20,8 +20,8 @@ import ctypes -# Binary Ninja components -import _binaryninjacore as core +# Binary Ninja components -- additional imports belong in the appropriate class +from binaryninja import _binaryninjacore as core class Setting(object): diff --git a/python/startup.py b/python/startup.py index 0abc47cb..919a8fbe 100644 --- a/python/startup.py +++ b/python/startup.py @@ -18,7 +18,7 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. -import _binaryninjacore as core +from binaryninja import _binaryninjacore as core _plugin_init = False diff --git a/python/transform.py b/python/transform.py index 3284ed74..f04bfdca 100644 --- a/python/transform.py +++ b/python/transform.py @@ -22,15 +22,16 @@ import traceback import ctypes import abc -# Binary Ninja components -import _binaryninjacore as core -from enums import TransformType -import startup -import log -import databuffer +# Binary Ninja components -- additional imports belong in the appropriate class +from binaryninja import _binaryninjacore as core +from binaryninja.enums import TransformType + +#2-3 compatibility +from six import with_metaclass class _TransformMetaClass(type): + from binaryninja import startup @property def list(self): startup._init_plugins() @@ -90,14 +91,15 @@ class TransformParameter(object): self.fixed_length = fixed_length -class Transform(object): +class Transform(with_metaclass(_TransformMetaClass, object)): + from binaryninja import log + from binaryninja import databuffer transform_type = None name = None long_name = None group = None parameters = [] _registered_cb = None - __metaclass__ = _TransformMetaClass def __init__(self, handle): if handle is None: diff --git a/python/types.py b/python/types.py index 42ed7ddd..cbe5a7bc 100644 --- a/python/types.py +++ b/python/types.py @@ -18,15 +18,14 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. +from __future__ import absolute_import max_confidence = 255 import ctypes -# Binary Ninja components -import _binaryninjacore as core -from enums import SymbolType, TypeClass, NamedTypeReferenceClass, InstructionTextTokenType, StructureType, ReferenceType, VariableSourceType -import callingconvention -import function +# Binary Ninja components -- additional imports belong in the appropriate class +from binaryninja import _binaryninjacore as core +from binaryninja.enums import SymbolType, TypeClass, NamedTypeReferenceClass, InstructionTextTokenType, StructureType, ReferenceType, VariableSourceType class QualifiedName(object): @@ -213,6 +212,8 @@ class FunctionParameter(object): class Type(object): def __init__(self, handle, platform = None, confidence = max_confidence): + from binaryninja import callingconvention + from binaryninja import function self.handle = handle self.confidence = confidence self.platform = platform @@ -344,7 +345,7 @@ class Type(object): return None return Enumeration(result) - @property + @property def named_type_reference(self): """Reference to a named type (read-only)""" result = core.BNGetTypeNamedTypeReference(self.handle) @@ -376,7 +377,7 @@ class Type(object): def __repr__(self): if self.confidence < max_confidence: - return "" % (str(self), (self.confidence * 100) / max_confidence) + return "" % (str(self), (self.confidence * 100) // max_confidence) return "" % str(self) def get_string_before_name(self): diff --git a/python/undoaction.py b/python/undoaction.py index 7e3c76a0..9771df38 100644 --- a/python/undoaction.py +++ b/python/undoaction.py @@ -22,14 +22,14 @@ import traceback import json import ctypes -# Binary Ninja components -import _binaryninjacore as core -from enums import ActionType -import startup -import log +# Binary Ninja components -- additional imports belong in the appropriate class +from binaryninja import _binaryninjacore as core +from binaryninja.enums import ActionType class UndoAction(object): + from binaryninja import startup + from binaryninja import log name = None action_type = None _registered = False diff --git a/python/update.py b/python/update.py index 1eb8ea61..1fda149b 100644 --- a/python/update.py +++ b/python/update.py @@ -21,14 +21,17 @@ import traceback import ctypes -# Binary Ninja components -import _binaryninjacore as core -from enums import UpdateResult -import startup -import log +# Binary Ninja components -- additional imports belong in the appropriate class +from binaryninja import _binaryninjacore as core +from binaryninja.enums import UpdateResult + + +#2-3 compatibility +from six import with_metaclass class _UpdateChannelMetaClass(type): + from binaryninja import startup @property def list(self): startup._init_plugins() @@ -95,6 +98,7 @@ class _UpdateChannelMetaClass(type): class UpdateProgressCallback(object): + from binaryninja import log def __init__(self, func): self.cb = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.c_ulonglong, ctypes.c_ulonglong)(self.callback) self.func = func @@ -108,8 +112,7 @@ class UpdateProgressCallback(object): log.log_error(traceback.format_exc()) -class UpdateChannel(object): - __metaclass__ = _UpdateChannelMetaClass +class UpdateChannel(with_metaclass(_UpdateChannelMetaClass, object)): def __init__(self, name, desc, ver): self.name = name -- cgit v1.3.1 From 975249d22e10360ed2c4c0bcea151c39d9446b0a Mon Sep 17 00:00:00 2001 From: KyleMiles Date: Thu, 5 Jul 2018 18:28:59 -0400 Subject: Rebased so commit history is correct now --- .gitignore | 6 - python/__init__.py | 1 + python/downloadprovider.py | 1 - python/examples/README.md | 56 +++ python/examples/angr_plugin.py | 153 +++++++ python/examples/arch_hook.py | 16 + python/examples/bin_info.py | 76 ++++ python/examples/breakpoint.py | 50 +++ python/examples/export_svg.py | 224 ++++++++++ python/examples/instruction_iterator.py | 53 +++ python/examples/jump_table.py | 86 ++++ python/examples/nds.py | 120 ++++++ python/examples/nes.py | 650 ++++++++++++++++++++++++++++++ python/examples/notification_callbacks.py | 74 ++++ python/examples/nsf.py | 145 +++++++ python/examples/print_syscalls.py | 55 +++ python/examples/version_switcher.py | 150 +++++++ python/function.py | 14 +- python/plugin.py | 32 +- suite/generator.py | 1 + suite/oracle.pkl | Bin 9098577 -> 440689 bytes suite/testcommon.py | 6 +- suite/unit.py | 7 +- 23 files changed, 1937 insertions(+), 39 deletions(-) create mode 100644 python/examples/README.md create mode 100644 python/examples/angr_plugin.py create mode 100644 python/examples/arch_hook.py create mode 100644 python/examples/bin_info.py create mode 100644 python/examples/breakpoint.py create mode 100755 python/examples/export_svg.py create mode 100644 python/examples/instruction_iterator.py create mode 100644 python/examples/jump_table.py create mode 100644 python/examples/nds.py create mode 100644 python/examples/nes.py create mode 100644 python/examples/notification_callbacks.py create mode 100644 python/examples/nsf.py create mode 100644 python/examples/print_syscalls.py create mode 100644 python/examples/version_switcher.py (limited to 'python/examples') diff --git a/.gitignore b/.gitignore index eff64eb4..302ad960 100644 --- a/.gitignore +++ b/.gitignore @@ -33,14 +33,8 @@ api-docs/source/index.rst .coverage # Make generator -libcrypto.so.1.0.2 -libcurl.so.4 -libssl.so.1.0.2 plugins/ python/examples/binaryninja/ -python/libcrypto.so.1.0.2 -python/libcurl.so.4 -python/libssl.so.1.0.2 python/plugins/ python/types/ suite/binaryninja/ diff --git a/python/__init__.py b/python/__init__.py index e1288b0a..36ed4f0d 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -108,6 +108,7 @@ from binaryninja.lineardisassembly import * from binaryninja.undoaction import * from binaryninja.highlight import * from binaryninja.scriptingprovider import * +from binaryninja.downloadprovider import * from binaryninja.pluginmanager import * from binaryninja.setting import * from binaryninja.metadata import * diff --git a/python/downloadprovider.py b/python/downloadprovider.py index c714cb98..09abd516 100644 --- a/python/downloadprovider.py +++ b/python/downloadprovider.py @@ -20,7 +20,6 @@ import abc -import code import ctypes import sys import traceback diff --git a/python/examples/README.md b/python/examples/README.md new file mode 100644 index 00000000..43af34be --- /dev/null +++ b/python/examples/README.md @@ -0,0 +1,56 @@ +# Binary Ninja Python API Examples + +The following examples demonstrate some of the Binary Ninja API. They include both stand-alone examples that directly call into the core without a GUI, as well as examples meant to be loaded as plugins available from the UI. + +## Stand-alone + +These plugins only operate when run directly outside of the UI + +* bin_info.py - general binary information +* print_syscalls.py - extract syscall numbers from IL on specified file. Can be run both headless and in Binary Ninja +* version_switcher.py - uses the update API to see raw version notes and manually downgrade or upgrade +* instruction_iterator.py - very simple plugin that iterates through functions, blocks, and instructions + +To use the stand-alone Python examples, make sure your `PYTHON_PATH` includes the API, as shown below. Please note, this is a feature that requires the "GUI-less processing" capability not available in the personal edition. In the personal edition, all scripts must by run from the integrated python console (follow the directions below under "Loading Plugins" section) + +``` +PYTHONPATH=$PYTHONPATH:/Applications/Binary\ Ninja.app/Contents/Resources/python +``` + +## GUI Plugins + +These plugins require the UI to be running + +* breakpoint.py - small example showing how to modify a file and register a GUI menu item +* jump_table.py - heuristic based jump table detection for when the data-flow based computation fails, triggered by right-clicking on the location where the jump value is computed +* angr_plugin.py - a plugin to demonstrate both background threads, the simplified plugin UI elements, and highlighting +* export_svg.py - exports the graph view of a function to an SVG file for including in reports + +## Both + +These plugins are able to operate in either the GUI or as stand-alone plugins + +* nes.py - 6502 CPU architecture including LLIL lifting and `.NES` file format parser + + +## Loading Plugins + +Plugins are meant to be loaded into a running Binary Ninja GUI and should either be copied or symlinked into the appropriate plugin folder. You'll need to then re-start Binary Ninja. + +### OSX + +``` +~/Library/Application Support/Binary Ninja/plugins +``` + +### Windows + +``` +%APPDATA%\Binary Ninja\plugins +``` + +### Linux + +``` +~/.binaryninja/plugins +``` diff --git a/python/examples/angr_plugin.py b/python/examples/angr_plugin.py new file mode 100644 index 00000000..26f8040c --- /dev/null +++ b/python/examples/angr_plugin.py @@ -0,0 +1,153 @@ +# Copyright (c) 2015-2017 Vector 35 LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + + +# This plugin assumes angr is already installed and available on the system. See the angr documentation +# for information about installing angr. It should be installed using the virtualenv method. +# +# This plugin is currently only known to work on Linux using virtualenv. Switch to the virtual environment +# (using a command such as "workon angr"), then run the Binary Ninja UI from the command line. +# +# This method is known to fail on Mac OS X as the virtualenv used by angr does not appear to provide a +# way to automatically link to the correct version of Python, even when running the UI from within the +# virtual environment. A later update may allow for a manual override to link to the required version +# of Python. + +import tempfile +import logging +import os + +__name__ = "__console__" # angr looks for this, it won't load from within a UI without it + +import angr +# For the lazy instead you can just import everything 'from binaryninja import *'' +from binaryninja.binaryview import BinaryView +from binaryninja.plugin import BackgroundTaskThread, PluginCommand +from binaryninja.interaction import show_plain_text_report, show_message_box +from binaryninja.highlight import HighlightColor +from binaryninja.enums import HighlightStandardColor, MessageBoxButtonSet, MessageBoxIcon + +# Disable warning logs as they show up as errors in the UI +logging.disable(logging.WARNING) + +# Create sets in the BinaryView's data field to store the desired path for each view +BinaryView.set_default_session_data("angr_find", set()) +BinaryView.set_default_session_data("angr_avoid", set()) + + +def escaped_output(str): + return '\n'.join([s.encode("string_escape") for s in str.split('\n')]) + + +# Define a background thread object for solving in the background +class Solver(BackgroundTaskThread): + def __init__(self, find, avoid, view): + BackgroundTaskThread.__init__(self, "Solving with angr...", True) + self.find = tuple(find) + self.avoid = tuple(avoid) + self.view = view + + # Write the binary to disk so that the angr API can read it + self.binary = tempfile.NamedTemporaryFile() + self.binary.write(view.file.raw.read(0, len(view.file.raw))) + self.binary.flush() + + def run(self): + # Create an angr project and an explorer with the user's settings + p = angr.Project(self.binary.name) + e = p.surveyors.Explorer(find = self.find, avoid = self.avoid) + + # Solve loop + while not e.done: + if self.cancelled: + # Solve cancelled, show results if there were any + if len(e.found) > 0: + break + return + + # Perform the next step in the solve + e.step() + + # Update status + active_count = len(e.active) + found_count = len(e.found) + + progress = "Solving with angr (%d active path%s" % (active_count, "s" if active_count != 1 else "") + if found_count > 0: + progress += ", %d path%s found" % (found_count, "s" if found_count != 1 else "") + self.progress = progress + ")..." + + # Solve complete, show report + text_report = "Found %d path%s.\n\n" % (len(e.found), "s" if len(e.found) != 1 else "") + i = 1 + for f in e.found: + text_report += "Path %d\n" % i + "=" * 10 + "\n" + text_report += "stdin:\n" + escaped_output(f.state.posix.dumps(0)) + "\n\n" + text_report += "stdout:\n" + escaped_output(f.state.posix.dumps(1)) + "\n\n" + text_report += "stderr:\n" + escaped_output(f.state.posix.dumps(2)) + "\n\n" + i += 1 + + name = self.view.file.filename + if len(name) > 0: + show_plain_text_report("Results from angr - " + os.path.basename(self.view.file.filename), text_report) + else: + show_plain_text_report("Results from angr", text_report) + + +def find_instr(bv, addr): + # Highlight the instruction in green + blocks = bv.get_basic_blocks_at(addr) + for block in blocks: + block.set_auto_highlight(HighlightColor(HighlightStandardColor.GreenHighlightColor, alpha = 128)) + block.function.set_auto_instr_highlight(addr, HighlightStandardColor.GreenHighlightColor) + + # Add the instruction to the list associated with the current view + bv.session_data.angr_find.add(addr) + + +def avoid_instr(bv, addr): + # Highlight the instruction in red + blocks = bv.get_basic_blocks_at(addr) + for block in blocks: + block.set_auto_highlight(HighlightColor(HighlightStandardColor.RedHighlightColor, alpha = 128)) + block.function.set_auto_instr_highlight(addr, HighlightStandardColor.RedHighlightColor) + + # Add the instruction to the list associated with the current view + bv.session_data.angr_avoid.add(addr) + + +def solve(bv): + if len(bv.session_data.angr_find) == 0: + show_message_box("Angr Solve", "You have not specified a goal instruction.\n\n" + + "Please right click on the goal instruction and select \"Find Path to This Instruction\" to " + + "continue.", MessageBoxButtonSet.OKButtonSet, MessageBoxIcon.ErrorIcon) + return + + # Start a solver thread for the path associated with the view + s = Solver(bv.session_data.angr_find, bv.session_data.angr_avoid, bv) + s.start() + + +# Register commands for the user to interact with the plugin +PluginCommand.register_for_address("Find Path to This Instruction", + "When solving, find a path that gets to this instruction", find_instr) +PluginCommand.register_for_address("Avoid This Instruction", + "When solving, avoid paths that reach this instruction", avoid_instr) +PluginCommand.register("Solve With Angr", "Attempt to solve for a path that satisfies the constraints given", solve) diff --git a/python/examples/arch_hook.py b/python/examples/arch_hook.py new file mode 100644 index 00000000..452bd2b6 --- /dev/null +++ b/python/examples/arch_hook.py @@ -0,0 +1,16 @@ +from binaryninja.architecture import Architecture, ArchitectureHook + +class X86ReturnHook(ArchitectureHook): + def get_instruction_text(self, data, addr): + # Call the original implementation's method by calling the superclass + result, length = super(X86ReturnHook, self).get_instruction_text(data, addr) + + # Patch the name of the 'retn' instruction to 'ret' + if len(result) > 0 and result[0].text == 'retn': + result[0].text = 'ret' + + return result, length + +# Install the hook by constructing it with the desired architecture to hook, then registering it +X86ReturnHook(Architecture['x86']).register() + diff --git a/python/examples/bin_info.py b/python/examples/bin_info.py new file mode 100644 index 00000000..05a72ed0 --- /dev/null +++ b/python/examples/bin_info.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python +# Copyright (c) 2015-2017 Vector 35 LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + +import sys + +import binaryninja.log as log +from binaryninja.binaryview import BinaryViewType +import binaryninja.interaction as interaction +from binaryninja.plugin import PluginCommand + +# 2-3 compatibility +from binaryninja import range + + +def get_bininfo(bv): + if bv is None: + filename = "" + if len(sys.argv) > 1: + filename = sys.argv[1] + else: + filename = interaction.get_open_filename_input("Filename:") + if filename is None: + log.log_warn("No file specified") + sys.exit(1) + + bv = BinaryViewType.get_view_of_file(filename) + log.log_to_stdout(True) + + contents = "## %s ##\n" % bv.file.filename + contents += "- START: 0x%x\n\n" % bv.start + contents += "- ENTRY: 0x%x\n\n" % bv.entry_point + contents += "- ARCH: %s\n\n" % bv.arch.name + contents += "### First 10 Functions ###\n" + + contents += "| Start | Name |\n" + contents += "|------:|:-------|\n" + for i in range(min(10, len(bv.functions))): + contents += "| 0x%x | %s |\n" % (bv.functions[i].start, bv.functions[i].symbol.full_name) + + contents += "### First 10 Strings ###\n" + contents += "| Start | Length | String |\n" + contents += "|------:|-------:|:-------|\n" + for i in range(min(10, len(bv.strings))): + start = bv.strings[i].start + length = bv.strings[i].length + string = bv.read(start, length) + contents += "| 0x%x |%d | %s |\n" % (start, length, string) + return contents + + +def display_bininfo(bv): + interaction.show_markdown_report("Binary Info Report", get_bininfo(bv)) + + +if __name__ == "__main__": + print(get_bininfo(None)) +else: + PluginCommand.register("Binary Info", "Display basic info about the binary", display_bininfo) diff --git a/python/examples/breakpoint.py b/python/examples/breakpoint.py new file mode 100644 index 00000000..cbcb86d4 --- /dev/null +++ b/python/examples/breakpoint.py @@ -0,0 +1,50 @@ +# Copyright (c) 2015-2017 Vector 35 LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + + +from binaryninja.plugin import PluginCommand +from binaryninja.log import log_error + + +def write_breakpoint(view, start, length): + """Sample function to show registering a plugin menu item for a range of bytes. Also possible: + register + register_for_address + register_for_function + """ + bkpt_str = { + "x86": "int3", + "x86_64": "int3", + "armv7": "bkpt", + "aarch64": "brk #0", + "mips32": "break"} + + if view.arch.name not in bkpt_str: + log_error("Architecture %s not supported" % view.arch.name) + return + + bkpt, err = view.arch.assemble(bkpt_str[view.arch.name]) + if bkpt is None: + log_error(err) + return + view.write(start, bkpt * length // len(bkpt)) + + +PluginCommand.register_for_range("Convert to breakpoint", "Fill region with breakpoint instructions.", write_breakpoint) diff --git a/python/examples/export_svg.py b/python/examples/export_svg.py new file mode 100755 index 00000000..5bb27824 --- /dev/null +++ b/python/examples/export_svg.py @@ -0,0 +1,224 @@ +# from binaryninja import * +import os +import webbrowser +import time +try: + from urllib import pathname2url # Python 2.x +except: + from urllib.request import pathname2url # Python 3.x + +from binaryninja.interaction import get_save_filename_input, show_message_box +from binaryninja.enums import MessageBoxButtonSet, MessageBoxIcon, MessageBoxButtonResult, InstructionTextTokenType, BranchType +from binaryninja.plugin import PluginCommand + +colors = {'green': [162, 217, 175], 'red': [222, 143, 151], 'blue': [128, 198, 233], 'cyan': [142, 230, 237], 'lightCyan': [176, 221, 228], 'orange': [237, 189, 129], 'yellow': [237, 223, 179], 'magenta': [218, 196, 209], 'none': [74, 74, 74]} + +escape_table = { + "'": "'", + ">": ">", + "<": "<", + '"': """, + ' ': " " +} + + +def escape(toescape): + toescape = toescape.decode('utf-8').encode('ascii', 'xmlcharrefreplace') # handle extended unicode + return ''.join(escape_table.get(i, i) for i in toescape) # still escape the basics + + +def save_svg(bv, function): + address = hex(function.start).replace('L', '') + path = os.path.dirname(bv.file.filename) + origname = os.path.basename(bv.file.filename) + filename = os.path.join(path, 'binaryninja-{filename}-{function}.html'.format(filename=origname, function=address)) + outputfile = get_save_filename_input('File name for export_svg', 'HTML files (*.html)', filename) + if outputfile is None: + return + content = render_svg(function, origname) + output = open(outputfile, 'w') + output.write(content) + output.close() + result = show_message_box("Open SVG", "Would you like to view the exported SVG?", + buttons = MessageBoxButtonSet.YesNoButtonSet, icon = MessageBoxIcon.QuestionIcon) + if result == MessageBoxButtonResult.YesButton: + url = 'file:{}'.format(pathname2url(outputfile)) + webbrowser.open(url) + + +def instruction_data_flow(function, address): + ''' TODO: Extract data flow information ''' + length = binaryninja.function.view.get_instruction_length(address) + bytes = binaryninja.function.view.read(address, length) + hex = bytes.encode('hex') + padded = ' '.join([hex[i:i + 2] for i in range(0, len(hex), 2)]) + return 'Opcode: {bytes}'.format(bytes=padded) + + +def render_svg(function, origname): + graph = binaryninja.function.create_graph() + graph.layout_and_wait() + heightconst = 15 + ratio = 0.48 + widthconst = heightconst * ratio + + output = ''' + + + + +''' + output += ''' + + + + + + + + + + + + + + + '''.format(width=graph.width * widthconst + 20, height=graph.height * heightconst + 20) + output += ''' + Function Graph 0 + ''' + edges = '' + for i, block in enumerate(graph.blocks): + + # Calculate basic block location and coordinates + x = ((block.x) * widthconst) + y = ((block.y) * heightconst) + width = ((block.width) * widthconst) + height = ((block.height) * heightconst) + + # Render block + output += ' \n'.format(i=i) + output += ' Basic Block {i}\n'.format(i=i) + rgb = colors['none'] + try: + bb = block.basic_block + color_code = bb.highlight.color + color_str = bb.highlight._standard_color_to_str(color_code) + if color_str in colors: + rgb = colors[color_str] + except: + pass + output += ' \n'.format(x=x, y=y, width=width + 16, height=height + 12, r=rgb[0], g=rgb[1], b=rgb[2]) + + # Render instructions, unfortunately tspans don't allow copying/pasting more + # than one line at a time, need SVG 1.2 textarea tags for that it looks like + + output += ' \n'.format(x=x, y=y + (i + 1) * heightconst) + for i, line in enumerate(block.lines): + output += ' '.format(x=x + 6, y=y + 6 + (i + 0.7) * heightconst, address=hex(line.address)[:-1]) + hover = instruction_data_flow(function, line.address) + output += '{hover}'.format(hover=hover) + for token in line.tokens: + # TODO: add hover for hex, function, and reg tokens + output += '{text}'.format(text=escape(token.text), tokentype=InstructionTextTokenType(token.type).name) + output += '\n' + output += ' \n' + output += ' \n' + + # Edges are rendered in a seperate chunk so they have priority over the + # basic blocks or else they'd render below them + + for edge in block.outgoing_edges: + points = "" + x, y = edge.points[0] + points += str(x * widthconst) + "," + str(y * heightconst + 12) + " " + for x, y in edge.points[1:-1]: + points += str(x * widthconst) + "," + str(y * heightconst) + " " + x, y = edge.points[-1] + points += str(x * widthconst) + "," + str(y * heightconst + 0) + " " + if edge.back_edge: + edges += ' \n'.format(type=BranchType(edge.type).name, points=points) + else: + edges += ' \n'.format(type=BranchType(edge.type).name, points=points) + output += ' ' + edges + '\n' + output += ' \n' + output += '' + + output += '

This CFG generated by Binary Ninja from {filename} on {timestring}.

'.format(filename = origname, timestring = time.strftime("%c")) + output += '' + return output + + +PluginCommand.register_for_function("Export to SVG", "Exports an SVG of the current function", save_svg) diff --git a/python/examples/instruction_iterator.py b/python/examples/instruction_iterator.py new file mode 100644 index 00000000..f55e1c1b --- /dev/null +++ b/python/examples/instruction_iterator.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python +# Copyright (c) 2015-2017 Vector 35 LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + +import sys +import binaryninja as binja + +if len(sys.argv) > 1: + target = sys.argv[1] + +bv = binja.BinaryViewType.get_view_of_file(target) +binja.log_to_stdout(True) +binja.log_info("-------- %s --------" % target) +binja.log_info("START: 0x%x" % bv.start) +binja.log_info("ENTRY: 0x%x" % bv.entry_point) +binja.log_info("ARCH: %s" % bv.arch.name) +binja.log_info("\n-------- Function List --------") + +""" print all the functions, their basic blocks, and their il instructions """ +for func in bv.functions: + binja.log_info(repr(func)) + for block in func.low_level_il: + binja.log_info("\t{0}".format(block)) + + for insn in block: + binja.log_info("\t\t{0}".format(insn)) + + +""" print all the functions, their basic blocks, and their mc instructions """ +for func in bv.functions: + binja.log_info(repr(func)) + for block in func: + binja.log_info("\t{0}".format(block)) + + for insn in block: + binja.log_info("\t\t{0}".format(insn)) diff --git a/python/examples/jump_table.py b/python/examples/jump_table.py new file mode 100644 index 00000000..4ed8dda2 --- /dev/null +++ b/python/examples/jump_table.py @@ -0,0 +1,86 @@ +# Copyright (c) 2015-2017 Vector 35 LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + +# This plugin will attempt to resolve simple jump tables (an array of code pointers) and add the destinations +# as indirect branch targets so that the flow graph reflects the jump table's control flow. +from binaryninja.plugin import PluginCommand +from binaryninja.enums import InstructionTextTokenType +import struct + + +def find_jump_table(bv, addr): + for block in bv.get_basic_blocks_at(addr): + func = block.function + arch = func.arch + addrsize = arch.address_size + + # Grab the instruction tokens so that we can look for the table's starting address + tokens, length = arch.get_instruction_text(bv.read(addr, 16), addr) + + # Look for the next jump instruction, which may be the current instruction. Some jump tables will + # compute the address first then jump to the computed address as a separate instruction. + jump_addr = addr + while jump_addr < block.end: + info = arch.get_instruction_info(bv.read(jump_addr, 16), jump_addr) + if len(info.branches) != 0: + break + jump_addr += info.length + if jump_addr >= block.end: + print("Unable to find jump after instruction 0x%x" % addr) + continue + print("Jump at 0x%x" % jump_addr) + + # Collect the branch targets for any tables referenced by the clicked instruction + branches = [] + for token in tokens: + if InstructionTextTokenType(token.type) == InstructionTextTokenType.PossibleAddressToken: # Table addresses will be a "possible address" token + tbl = token.value + print("Found possible table at 0x%x" % tbl) + i = 0 + while True: + # Read the next pointer from the table + data = bv.read(tbl + (i * addrsize), addrsize) + if len(data) == addrsize: + if addrsize == 4: + ptr = struct.unpack("= bv.start) and (ptr < bv.end): + print("Found destination 0x%x" % ptr) + branches.append((arch, ptr)) + else: + # Once a value that is not a pointer is encountered, the jump table is ended + break + else: + # Reading invalid memory + break + + i += 1 + + # Set the indirect branch targets on the jump instruction to be the list of targets discovered + func.set_user_indirect_branches(jump_addr, branches) + + +# Create a plugin command so that the user can right click on an instruction referencing a jump table and +# invoke the command +PluginCommand.register_for_address("Process jump table", "Look for jump table destinations", find_jump_table) diff --git a/python/examples/nds.py b/python/examples/nds.py new file mode 100644 index 00000000..a99303cf --- /dev/null +++ b/python/examples/nds.py @@ -0,0 +1,120 @@ +# Copyright (c) 2015-2017 Vector 35 LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + +# from binaryninja import * +from binaryninja.binaryview import BinaryView +from binaryninja.architecture import Architecture +from binaryninja.enums import SegmentFlag +from binaryninja.log import log_error + +import struct +import traceback + +# 2-3 compatibility +from binaryninja import range + + +def crc16(data): + crc = 0xffff + for ch in data: + crc ^= ord(ch) + for bit in range(0, 8): + if (crc & 1) == 1: + crc = (crc >> 1) ^ 0xa001 + else: + crc >>= 1 + return crc + + +class DSView(BinaryView): + def __init__(self, data): + BinaryView.__init__(self, file_metadata = data.file, parent_view = data) + self.raw = data + + @classmethod + def is_valid_for_data(self, data): + hdr = data.read(0, 0x160) + if len(hdr) < 0x160: + return False + if struct.unpack("> 4) + self.ram_banks = struct.unpack("B", hdr[8])[0] + self.rom_offset = 16 + if self.rom_flags & 4: + self.rom_offset += 512 + self.rom_length = self.rom_banks * 0x4000 + + # Add mapping for RAM and hardware registers, not backed by file contents + self.add_auto_segment(0, 0x8000, 0, 0, SegmentFlag.SegmentReadable | SegmentFlag.SegmentWritable | SegmentFlag.SegmentExecutable) + + # Add ROM mappings + self.add_auto_segment(0x8000, 0x4000, self.rom_offset + (self.__class__.bank * 0x4000), 0x4000, + SegmentFlag.SegmentReadable | SegmentFlag.SegmentExecutable) + self.add_auto_segment(0xc000, 0x4000, self.rom_offset + self.rom_length - 0x4000, 0x4000, + SegmentFlag.SegmentReadable | SegmentFlag.SegmentExecutable) + + nmi = struct.unpack("= 0x8000: + self.add_function(addr) + + return True + except: + log_error(traceback.format_exc()) + return False + + def perform_is_executable(self): + return True + + def perform_get_entry_point(self): + return struct.unpack("'.format(sys.argv[0])) + else: + print_syscalls(sys.argv[1]) diff --git a/python/examples/version_switcher.py b/python/examples/version_switcher.py new file mode 100644 index 00000000..c990a6c0 --- /dev/null +++ b/python/examples/version_switcher.py @@ -0,0 +1,150 @@ +#!/usr/bin/env python +# Copyright (c) 2015-2017 Vector 35 LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + +import sys + +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 = UpdateChannel.list[0].name +channel = None +versions = [] + + +def load_channel(newchannel): + global channel + global versions + if (channel is not None and newchannel == channel.name): + print("Same channel, not updating.") + else: + try: + print("Loading channel %s" % newchannel) + channel = UpdateChannel[newchannel] + print("Loading versions...") + versions = channel.versions + except Exception: + print("%s is not a valid channel name. Defaulting to " % chandefault) + channel = UpdateChannel[chandefault] + + +def select(version): + done = False + date = datetime.datetime.fromtimestamp(version.time).strftime('%c') + while not done: + print("Version:\t%s" % version.version) + print("Updated:\t%s" % date) + print("Notes:\n\n-----\n%s" % version.notes) + print("-----") + print("\t1)\tSwitch to version") + print("\t2)\tMain Menu") + selection = raw_input('Choice: ') + if selection.isdigit(): + selection = int(selection) + else: + selection = 0 + if (selection == 2): + done = True + elif (selection == 1): + if (version.version == channel.latest_version.version): + print("Requesting update to latest version.") + else: + print("Requesting update to prior version.") + if are_auto_updates_enabled(): + print("Disabling automatic updates.") + set_auto_updates_enabled(False) + if (version.version == core_version): + print("Already running %s" % version.version) + else: + print("version.version %s" % version.version) + 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: + print("Invalid selection") + + +def list_channels(): + done = False + print("\tSelect channel:\n") + while not done: + 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")) + selection = raw_input('Choice: ') + if selection.isdigit(): + selection = int(selection) + else: + selection = 0 + if (selection <= 0 or selection > len(channel_list) + 1): + print("%s is an invalid choice." % selection) + else: + done = True + if (selection != len(channel_list) + 1): + load_channel(channel_list[selection - 1].name) + + +def toggle_updates(): + set_auto_updates_enabled(not are_auto_updates_enabled()) + + +def main(): + global channel + done = False + load_channel(chandefault) + while not done: + print("\n\tBinary Ninja Version Switcher") + print("\t\tCurrent Channel:\t%s" % channel.name) + 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)) + print("\t%d)\t%s" % (len(versions) + 1, "Switch Channel")) + print("\t%d)\t%s" % (len(versions) + 2, "Toggle Auto Updates")) + print("\t%d)\t%s" % (len(versions) + 3, "Exit")) + selection = raw_input('Choice: ') + if selection.isdigit(): + selection = int(selection) + else: + selection = 0 + if (selection <= 0 or selection > len(versions) + 3): + print("%d is an invalid choice.\n\n" % selection) + else: + if (selection == len(versions) + 3): + done = True + elif (selection == len(versions) + 2): + toggle_updates() + elif (selection == len(versions) + 1): + list_channels() + else: + select(versions[selection - 1]) + + +if __name__ == "__main__": + main() diff --git a/python/function.py b/python/function.py index 8407eece..0589a5e2 100644 --- a/python/function.py +++ b/python/function.py @@ -26,7 +26,7 @@ import ctypes # Binary Ninja components import binaryninja from binaryninja import _binaryninjacore as core -from binaryninja import associateddatastore #required in the main scope due to being an argument for _FunctionAssociatedDataStore +from binaryninja import associateddatastore # Required in the main scope due to being an argument for _FunctionAssociatedDataStore from binaryninja import highlight from binaryninja import log from binaryninja import types @@ -1631,7 +1631,7 @@ class FunctionGraphEdge(object): class FunctionGraphBlock(object): - def __init__(self, handle): + def __init__(self, handle, graph): self.handle = handle self.graph = graph @@ -1657,14 +1657,14 @@ class FunctionGraphBlock(object): core.BNFreeBasicBlock(block) return None - view = binaryview.BinaryView(handle = core.BNGetFunctionData(func_handle)) + view = binaryninja.binaryview.BinaryView(handle = core.BNGetFunctionData(func_handle)) func = Function(view, func_handle) if core.BNIsLowLevelILBasicBlock(block): - block = lowlevelil.LowLevelILBasicBlock(view, block, + block = binaryninja.lowlevelil.LowLevelILBasicBlock(view, block, lowlevelil.LowLevelILFunction(func.arch, core.BNGetBasicBlockLowLevelILFunction(block), func)) elif core.BNIsMediumLevelILBasicBlock(block): - block = mediumlevelil.MediumLevelILBasicBlock(view, block, + block = binaryninja.mediumlevelil.MediumLevelILBasicBlock(view, block, mediumlevelil.MediumLevelILFunction(func.arch, core.BNGetBasicBlockMediumLevelILFunction(block), func)) else: block = binaryninja.basicblock.BasicBlock(view, block) @@ -1942,12 +1942,12 @@ class FunctionGraph(object): il_func = core.BNGetFunctionGraphLowLevelILFunction(self.handle) if not il_func: return None - return lowlevelil.LowLevelILFunction(self.function.arch, il_func, self.function) + return binaryninja.lowlevelil.LowLevelILFunction(self.function.arch, il_func, self.function) if self.is_medium_level_il: il_func = core.BNGetFunctionGraphMediumLevelILFunction(self.handle) if not il_func: return None - return mediumlevelil.MediumLevelILFunction(self.function.arch, il_func, self.function) + return binaryninja.mediumlevelil.MediumLevelILFunction(self.function.arch, il_func, self.function) return None def __setattr__(self, name, value): diff --git a/python/plugin.py b/python/plugin.py index b8217f0b..2fc30052 100644 --- a/python/plugin.py +++ b/python/plugin.py @@ -121,7 +121,7 @@ class PluginCommand(with_metaclass(_PluginCommandMetaClass, object)): try: file_metadata = binaryninja.filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) view_obj = binaryninja.binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) - func_obj = binaryninja.function.Function(view_obj, core.BNNewFunctionReference(func)) + func_obj = function.Function(view_obj, core.BNNewFunctionReference(func)) action(view_obj, func_obj) except: log.log_error(traceback.format_exc()) @@ -132,7 +132,7 @@ class PluginCommand(with_metaclass(_PluginCommandMetaClass, object)): file_metadata = filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) view_obj = binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) owner = function.Function(view_obj, core.BNGetLowLevelILOwnerFunction(func)) - func_obj = lowlevelil.LowLevelILFunction(owner.arch, core.BNNewLowLevelILFunctionReference(func), owner) + func_obj = binaryninja.lowlevelil.LowLevelILFunction(owner.arch, core.BNNewLowLevelILFunctionReference(func), owner) action(view_obj, func_obj) except: log.log_error(traceback.format_exc()) @@ -143,7 +143,7 @@ class PluginCommand(with_metaclass(_PluginCommandMetaClass, object)): file_metadata = filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) view_obj = binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) owner = function.Function(view_obj, core.BNGetLowLevelILOwnerFunction(func)) - func_obj = lowlevelil.LowLevelILFunction(owner.arch, core.BNNewLowLevelILFunctionReference(func), owner) + func_obj = binaryninja.lowlevelil.LowLevelILFunction(owner.arch, core.BNNewLowLevelILFunctionReference(func), owner) action(view_obj, func_obj[instr]) except: log.log_error(traceback.format_exc()) @@ -154,7 +154,7 @@ class PluginCommand(with_metaclass(_PluginCommandMetaClass, object)): file_metadata = filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) view_obj = binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) owner = function.Function(view_obj, core.BNGetMediumLevelILOwnerFunction(func)) - func_obj = mediumlevelil.MediumLevelILFunction(owner.arch, core.BNNewMediumLevelILFunctionReference(func), owner) + func_obj = binaryninja.mediumlevelil.MediumLevelILFunction(owner.arch, core.BNNewMediumLevelILFunctionReference(func), owner) action(view_obj, func_obj) except: log.log_error(traceback.format_exc()) @@ -165,7 +165,7 @@ class PluginCommand(with_metaclass(_PluginCommandMetaClass, object)): file_metadata = filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) view_obj = binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) owner = function.Function(view_obj, core.BNGetMediumLevelILOwnerFunction(func)) - func_obj = mediumlevelil.MediumLevelILFunction(owner.arch, core.BNNewMediumLevelILFunctionReference(func), owner) + func_obj = binaryninja.mediumlevelil.MediumLevelILFunction(owner.arch, core.BNNewMediumLevelILFunctionReference(func), owner) action(view_obj, func_obj[instr]) except: log.log_error(traceback.format_exc()) @@ -213,7 +213,7 @@ class PluginCommand(with_metaclass(_PluginCommandMetaClass, object)): return True file_metadata = binaryninja.filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) view_obj = binaryninja.binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) - func_obj = binaryninja.function.Function(view_obj, core.BNNewFunctionReference(func)) + func_obj = function.Function(view_obj, core.BNNewFunctionReference(func)) return is_valid(view_obj, func_obj) except: log.log_error(traceback.format_exc()) @@ -227,7 +227,7 @@ class PluginCommand(with_metaclass(_PluginCommandMetaClass, object)): file_metadata = filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) view_obj = binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) owner = function.Function(view_obj, core.BNGetLowLevelILOwnerFunction(func)) - func_obj = lowlevelil.LowLevelILFunction(owner.arch, core.BNNewLowLevelILFunctionReference(func), owner) + func_obj = binaryninja.lowlevelil.LowLevelILFunction(owner.arch, core.BNNewLowLevelILFunctionReference(func), owner) return is_valid(view_obj, func_obj) except: log.log_error(traceback.format_exc()) @@ -241,7 +241,7 @@ class PluginCommand(with_metaclass(_PluginCommandMetaClass, object)): file_metadata = filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) view_obj = binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) owner = function.Function(view_obj, core.BNGetLowLevelILOwnerFunction(func)) - func_obj = lowlevelil.LowLevelILFunction(owner.arch, core.BNNewLowLevelILFunctionReference(func), owner) + func_obj = binaryninja.lowlevelil.LowLevelILFunction(owner.arch, core.BNNewLowLevelILFunctionReference(func), owner) return is_valid(view_obj, func_obj[instr]) except: log.log_error(traceback.format_exc()) @@ -255,7 +255,7 @@ class PluginCommand(with_metaclass(_PluginCommandMetaClass, object)): file_metadata = filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) view_obj = binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) owner = function.Function(view_obj, core.BNGetMediumLevelILOwnerFunction(func)) - func_obj = mediumlevelil.MediumLevelILFunction(owner.arch, core.BNNewMediumLevelILFunctionReference(func), owner) + func_obj = binaryninja.mediumlevelil.MediumLevelILFunction(owner.arch, core.BNNewMediumLevelILFunctionReference(func), owner) return is_valid(view_obj, func_obj) except: log.log_error(traceback.format_exc()) @@ -269,7 +269,7 @@ class PluginCommand(with_metaclass(_PluginCommandMetaClass, object)): file_metadata = filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) view_obj = binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) owner = function.Function(view_obj, core.BNGetMediumLevelILOwnerFunction(func)) - func_obj = mediumlevelil.MediumLevelILFunction(owner.arch, core.BNNewMediumLevelILFunctionReference(func), owner) + func_obj = binaryninja.mediumlevelil.MediumLevelILFunction(owner.arch, core.BNNewMediumLevelILFunctionReference(func), owner) return is_valid(view_obj, func_obj[instr]) except: log.log_error(traceback.format_exc()) @@ -364,7 +364,7 @@ class PluginCommand(with_metaclass(_PluginCommandMetaClass, object)): .. warning:: Calling ``register_for_low_level_il_function`` with the same function name will replace the existing function but will leak the memory of the original plugin. """ - startup._init_plugins() + binaryninja.startup._init_plugins() action_obj = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView), ctypes.POINTER(core.BNLowLevelILFunction))(lambda ctxt, view, func: cls._low_level_il_function_action(view, func, action)) is_valid_obj = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView), ctypes.POINTER(core.BNLowLevelILFunction))(lambda ctxt, view, func: cls._low_level_il_function_is_valid(view, func, is_valid)) cls._registered_commands.append((action_obj, is_valid_obj)) @@ -383,7 +383,7 @@ class PluginCommand(with_metaclass(_PluginCommandMetaClass, object)): .. warning:: Calling ``register_for_low_level_il_instruction`` with the same function name will replace the existing function but will leak the memory of the original plugin. """ - startup._init_plugins() + binaryninja.startup._init_plugins() action_obj = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView), ctypes.POINTER(core.BNLowLevelILFunction), ctypes.c_ulonglong)(lambda ctxt, view, func, instr: cls._low_level_il_instruction_action(view, func, instr, action)) is_valid_obj = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView), ctypes.POINTER(core.BNLowLevelILFunction), ctypes.c_ulonglong)(lambda ctxt, view, func, instr: cls._low_level_il_instruction_is_valid(view, func, instr, is_valid)) cls._registered_commands.append((action_obj, is_valid_obj)) @@ -402,7 +402,7 @@ class PluginCommand(with_metaclass(_PluginCommandMetaClass, object)): .. warning:: Calling ``register_for_medium_level_il_function`` with the same function name will replace the existing function but will leak the memory of the original plugin. """ - startup._init_plugins() + binaryninja.startup._init_plugins() action_obj = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView), ctypes.POINTER(core.BNMediumLevelILFunction))(lambda ctxt, view, func: cls._medium_level_il_function_action(view, func, action)) is_valid_obj = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView), ctypes.POINTER(core.BNMediumLevelILFunction))(lambda ctxt, view, func: cls._medium_level_il_function_is_valid(view, func, is_valid)) cls._registered_commands.append((action_obj, is_valid_obj)) @@ -421,7 +421,7 @@ class PluginCommand(with_metaclass(_PluginCommandMetaClass, object)): .. warning:: Calling ``register_for_medium_level_il_instruction`` with the same function name will replace the existing function but will leak the memory of the original plugin. """ - startup._init_plugins() + binaryninja.startup._init_plugins() action_obj = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView), ctypes.POINTER(core.BNMediumLevelILFunction), ctypes.c_ulonglong)(lambda ctxt, view, func, instr: cls._medium_level_il_instruction_action(view, func, instr, action)) is_valid_obj = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView), ctypes.POINTER(core.BNMediumLevelILFunction), ctypes.c_ulonglong)(lambda ctxt, view, func, instr: cls._medium_level_il_instruction_is_valid(view, func, instr, is_valid)) cls._registered_commands.append((action_obj, is_valid_obj)) @@ -469,7 +469,7 @@ class PluginCommand(with_metaclass(_PluginCommandMetaClass, object)): elif self.command.type == PluginCommandType.LowLevelILInstructionPluginCommand: if context.instruction is None: return False - if not isinstance(context.instruction, lowlevelil.LowLevelILInstruction): + if not isinstance(context.instruction, binaryninja.lowlevelil.LowLevelILInstruction): return False if not self.command.lowLevelILInstructionIsValid: return True @@ -484,7 +484,7 @@ class PluginCommand(with_metaclass(_PluginCommandMetaClass, object)): elif self.command.type == PluginCommandType.MediumLevelILInstructionPluginCommand: if context.instruction is None: return False - if not isinstance(context.instruction, mediumlevelil.MediumLevelILInstruction): + if not isinstance(context.instruction, binaryninja.mediumlevelil.MediumLevelILInstruction): return False if not self.command.mediumLevelILInstructionIsValid: return True diff --git a/suite/generator.py b/suite/generator.py index ad0d24fc..f4ee69ee 100755 --- a/suite/generator.py +++ b/suite/generator.py @@ -22,6 +22,7 @@ from collections import Counter global verbose verbose = False + class TestBinaryNinjaAPI(unittest.TestCase): # Returns a tuple of: # bool : Two lists are equal diff --git a/suite/oracle.pkl b/suite/oracle.pkl index d7ce1115..f0be17d4 100644 Binary files a/suite/oracle.pkl and b/suite/oracle.pkl differ diff --git a/suite/testcommon.py b/suite/testcommon.py index ef85c053..07ceb592 100644 --- a/suite/testcommon.py +++ b/suite/testcommon.py @@ -541,6 +541,7 @@ class TestBuilder(Builder): """Types produced different result""" file_name = os.path.join(self.test_store, "helloworld") bv = binja.BinaryViewType.get_view_of_file(file_name) + preprocessed = binja.preprocess_source(""" #ifdef nonexistant int foo = 1; @@ -550,7 +551,7 @@ class TestBuilder(Builder): long long bar1 = 2; #endif """) - source = '\n'.join([i.decode('utf-8') for i in preprocessed[0].split(b'\n') if not b'#line' in i and len(i) > 0]) + source = '\n'.join([i.decode('charmap') for i in preprocessed[0].split(b'\n') if not b'#line' in i and len(i) > 0]) typelist = bv.platform.parse_types_from_source(source) inttype = binja.Type.int(4) @@ -566,14 +567,13 @@ class TestBuilder(Builder): retinfo.append("Type equality: " + str((inttype == inttype) and not (inttype != inttype))) return retinfo - def test_Plugin_bin_info(self): """print_syscalls plugin produced different result""" file_name = os.path.join(self.test_store, "helloworld") self.unpackage_file(file_name) result = subprocess.Popen(["python", os.path.join(self.examples_dir, "bin_info.py"), file_name], stdout=subprocess.PIPE).communicate()[0] # normalize line endings and path sep - return [result.replace(b"\\", b"/").replace(b"\r\n", b"\n").decode("charmap")] + return [line for line in result.replace(b"\\", b"/").replace(b"\r\n", b"\n").decode("charmap").split("\n")] def test_linear_disassembly(self): """linear_disassembly produced different result""" diff --git a/suite/unit.py b/suite/unit.py index 9cbed511..e01b0f19 100644 --- a/suite/unit.py +++ b/suite/unit.py @@ -13,6 +13,7 @@ from collections import Counter global verbose verbose = False + class TestBinaryNinjaAPI(unittest.TestCase): # Returns a tuple of: # bool : Two lists are equal @@ -235,12 +236,6 @@ class TestBinaryNinjaAPI(unittest.TestCase): def test_binary___loop_constant_propagate(self): self.run_binary_test('suite/binaries/test_corpus/loop_constant_propagate.zip') - def test_binary___ls(self): - self.run_binary_test('suite/binaries/test_corpus/ls.zip') - - def test_binary___md5(self): - self.run_binary_test('suite/binaries/test_corpus/md5.zip') - def test_binary___partial_register_dataflow(self): self.run_binary_test('suite/binaries/test_corpus/partial_register_dataflow.zip') -- cgit v1.3.1