def print_disassembly(address: int, inst: CsInsn): '''Print a line of disassembly honoring all the various debugging settings.''' grp = "" addr = "" rel = "" fancy = None if DebugOpts.PRINT_GROUPS: grp = f"; Groups: {list(map(inst.group_name, inst.groups))}" grp = f"{grp:30}" if DebugOpts.PRINT_ADDRESSES: addr = f"{hex(address):>18} " if DebugOpts.PRINT_RIP: for operand in inst.operands: disp = OperandTests.is_rip_relative(operand) if disp is not None: rel += f"{hex(disp + inst.size + address)} " if len(rel) > 0: rel = f"; RIP-Refs: {rel:12}" if DebugOpts.FANCY_OPERANDS: # This is overkill, of course. It just lets me mess with # the operands in case I want to do something special, and # (I hope) demonstrates how you can pull apart the operands # to an instruction. fancy = [] for operand in inst.operands: if operand.type == X86_OP_IMM: fancy.append(f"{hex(operand.value.imm)}") elif operand.type == X86_OP_REG: fancy.append(f"{inst.reg_name(operand.value.reg)}") elif operand.type == X86_OP_MEM: segment = inst.reg_name(operand.value.mem.segment) base = inst.reg_name(operand.value.mem.base) index = inst.reg_name(operand.value.mem.index) scale = str(operand.value.mem.scale) disp = operand.value.mem.disp value = f"[{base}" if index is not None: value += f" + {index}*{scale}" if disp is not None: if disp > 0: value += f" + {hex(disp)}" elif disp < 0: value += f" - {hex(abs(disp))}" if segment is not None: value = f"{segment}:" + value value += "]" fancy.append(value) else: fancy.append("???") line = f"{inst.mnemonic:10} {', '.join(fancy)}" else: line = f"{inst.mnemonic:5} {inst.op_str:30}" print(f" {addr}{line:40}{grp}{rel}")
def _operand_uses_vector_registers(cls, instruction: CsInsn, operand: Arm64Op) -> bool: if operand.type == ARM64_OP_IMM: return False if operand.type == ARM64_OP_REG: reg_name = instruction.reg_name(operand.value.reg) elif operand.type == ARM64_OP_MEM: reg_name = instruction.reg_name(operand.mem.base) else: raise RuntimeError( f"unknown operand type {operand.type} in instr at {instruction.address}" ) return ObjcInstruction.is_vector_register(reg_name)
def format_instruction_arg(instruction: CsInsn, arg: Arm64Op) -> str: if arg.type == ARM64_OP_REG: return StringPalette.REG(instruction.reg_name(arg.value.reg)) elif arg.type == ARM64_OP_IMM: return StringPalette.IMM(hex(arg.value.imm)) elif arg.type == ARM64_OP_MEM: return f"[{StringPalette.REG(instruction.reg_name(arg.mem.base))} #{StringPalette.IMM(hex(arg.mem.disp))}]" raise RuntimeError(f"unknown arg type {arg.type}")
def annotate_instruction(function_analyzer: ObjcFunctionAnalyzer, sel_args: List[str], instr: CsInsn) -> str: annotation = "\t\t" # Parse as an ObjcInstruction wrapped_instr = ObjcInstruction.parse_instruction( function_analyzer, function_analyzer.get_instruction_at_address(instr.address)) if isinstance(wrapped_instr, ObjcBranchInstruction): wrapped_branch_instr: ObjcBranchInstruction = wrapped_instr annotation += "#\t" if function_analyzer.is_local_branch(wrapped_branch_instr): annotation += StringPalette.ANNOTATION( f"jump loc_{hex(wrapped_branch_instr.destination_address)}") elif wrapped_instr.symbol: annotation += StringPalette.ANNOTATION(wrapped_instr.symbol) if not wrapped_branch_instr.selector: annotation += StringPalette.ANNOTATION("();") else: annotation += StringPalette.ANNOTATION_ARGS( f"(id, @selector({wrapped_branch_instr.selector.name})") # Figure out argument count passed to selector arg_count = wrapped_branch_instr.selector.name.count(":") for i in range(arg_count): # x0 is self, x1 is the SEL, real args start at x2 register = f"x{i + 2}" method_arg = function_analyzer.get_register_contents_at_instruction( register, wrapped_branch_instr) method_arg_string = ", " if method_arg.type == RegisterContentsType.IMMEDIATE: method_arg_string += hex(method_arg.value) else: method_arg_string += "<?>" annotation += StringPalette.STRING(method_arg_string) annotation += ");" else: annotation += StringPalette.ANNOTATION(f"({hex(instr.address)})(") arg_count = 4 for i in range(arg_count): # x0 is self, x1 is the SEL, real args start at x2 register = f"x{i}" method_arg = function_analyzer.get_register_contents_at_instruction( register, wrapped_instr) method_arg_string = f"{register}: " if method_arg.type == RegisterContentsType.IMMEDIATE: method_arg_string += hex(method_arg.value) else: method_arg_string += "<?>" annotation += StringPalette.ANNOTATION_ARGS(method_arg_string) annotation += ", " annotation += ");" else: # Try to annotate string loads # This code taken from Ethan's potential passwords check if instr.mnemonic in ["ldr", "adr", "adrp", "add"]: # Only care about general purpose registers that are being written into if not ObjcInstruction.instruction_uses_vector_registers(instr): _, instr_mutated_regs = instr.regs_access() if len(instr_mutated_regs): # Get the contents of the register (an address) register = instr.reg_name(instr_mutated_regs[0]) wrapped_instr = ObjcInstruction.parse_instruction( function_analyzer, instr) register_contents = function_analyzer.get_register_contents_at_instruction( register, wrapped_instr) if register_contents.type == RegisterContentsType.IMMEDIATE: # Try reading a string binary_str = function_analyzer.binary.read_string_at_address( VirtualMemoryPointer(register_contents.value)) if binary_str: annotation += StringPalette.STRING( f'#\t"{binary_str}"') return annotation
def is_interrupt(insn: CsInsn) -> bool: '''Determine if an instruction is an interrupt.''' return insn.group(X86_GRP_INT)
def is_privileged(insn: CsInsn) -> bool: '''Determine if an instruciton is privileged.''' return insn.group(X86_GRP_PRIVILEGE)
def is_interrupt_return(insn: CsInsn) -> bool: '''Determine if an instruction is a return from interrupt.''' return insn.group(X86_GRP_IRET)
def is_branch(insn: CsInsn) -> bool: '''Determine if an instruction is a conditional branch.''' return insn.group(X86_GRP_BRANCH_RELATIVE)
def is_ret(insn: CsInsn) -> bool: '''Determine if an instruction is a return.''' return insn.group(X86_GRP_RET)
def is_call(insn: CsInsn) -> bool: '''Determine if an instruction is a call.''' return insn.group(X86_GRP_CALL)
def is_jump(insn: CsInsn) -> bool: '''Determine if an instruction is an unconditional jump.''' return insn.group(X86_GRP_JUMP)