def build_body(func: RawFunction): is_clean = func.joint_set == {0} print("// function:", func) print(f"fn {func.name}(cpu: Cpu, offset: usize) -> () {{") print(" let mut offset = offset;") write = lambda line, end=None, semi=";": print(f" {line}{semi}", end=end) if not is_clean: write = lambda line, end=None, semi=";": print(f" {line}{semi}", end=end) print(" loop {") print(f" pc = {conv(func.name)} + offset;") print(" match offset {") else: write("assert offset == 0") write(f"pc = {conv(func.name)} + offset") visited = set() for pc_offset in sorted(func.joint_set): pc = func.address + pc_offset while pc in func: if pc in visited: break else: visited.add(pc) new_func = text_map[pc] if func != new_func: if not new_func.name.startswith("__aeabi_") and \ new_func.name != "str_encode": print(func, text_map[pc]) assert func == text_map[pc], (func, text_map[pc], new_func) pc_offset = pc - func.address if not is_clean and pc_offset in func.joint_set: if pc_offset: print(" }") print(f" {pc_offset} => {{") try: insn = parse(pc, read_ushort(pc), read_ushort(pc + 2)) except (UnknownInstructionException, UnsupportedInstructionException): raise assert insn.op != Op.CBZ and insn.op != Op.CBNZ next_pc = (pc + 5) & THUMB_MASK if (insn.op == Op.BL) else pc + 2 target: Optional[int] = None branch_args = "" branch_link_arg = "" branch_offset = None cb_offset = None if insn.op == Op.BL or insn.op == Op.BLX or isinstance(insn, InsnSVC): cb_offset = pc_offset + (2 if insn.op != Op.BL else 4) branch_link_arg = f"{conv(func.name)} + {cb_offset} | 1, {cb_offset}" if isinstance(insn, (InsnBranch, InsnLongBranch, InsnBranchIf, InsnBranchIf2, InsnBranch2)): if isinstance(insn.dest, Offset): target = insn.dest.target new_func = text_map[target] new_func_name = f"this::{new_func.name}" if new_func != func else "null" if func == new_func: branch_offset = target - new_func.address if isinstance(insn, InsnBranchIf): branch_args = f"{target - new_func.address}" elif func == new_func or (target - new_func.address) != 0: branch_args = f"{new_func_name}, {target - new_func.address}" else: branch_args = f"{new_func_name}" if insn.op == Op.BL or insn.op == Op.BLX: branch_args += ", " + branch_link_arg if isinstance(insn, Insn2): if insn.op == Op.MOV and isinstance(insn.src, Reg): write(f"{insn.dest!r} = {insn.op}({insn.src!r})") if insn.dest == Reg.pc: if not is_clean: write("autob(pc); // auto") write("return") break elif insn.op == Op.CMP or insn.op == Op.TSTS or insn.op == Op.CMN: write(f"{insn.op}({insn.dest!r}, {insn.src!r})") elif insn.op == Op.SXTH or insn.op == Op.SXTB or \ insn.op == Op.UXTH or insn.op == Op.UXTB or \ insn.op == Op.REV or \ insn.op == Op.RSBS: write(f"{insn.dest!r} = {insn.op}({insn.src!r})") else: write(f"{insn.dest!r} = {insn.op}({insn.dest!r}, {insn.src!r})") elif isinstance(insn, Insn3): write(f"{insn.dest} = {insn.op}({insn.src!r}, {insn.offset!r})") elif isinstance(insn, InsnMem): assert isinstance(insn.dest, Reg) assert insn.dest.value <= 7 if insn.op.name.startswith("LDR"): is_zero_imm = isinstance(insn.offset, Imm) and insn.offset.value == 0 if is_zero_imm: maddr = f"{insn.base!r}" else: maddr = f"{insn.base!r} + {insn.offset!r}" if insn.base == Reg.pc: moffset10 = insn.offset.value + 4 # make ~10 assert moffset10 > 0 maddr = (pc + insn.offset.value + 4) & 0b11111111_11111111_11111111_11111101 mvalue = read_int(maddr) hvalue = f"0x{hex(mvalue)[2:].zfill(8)}" mfunc = text_map[mvalue] if mfunc is not None: if (mvalue & THUMB_MASK) == mfunc.address: write(f"{insn.dest} = mov({conv(mfunc.name)} | 1)") # write(f"hint({conv(mfunc.name)} | 1, this::{mfunc.name})") else: raise Exception("invalid memory read (?)") else: assert insn.op == Op.LDR vfunc = symbol_map[mvalue] if vfunc is not None and mvalue == vfunc.address: write( f"{insn.dest} = {insn.op}({func.name} + {maddr - func.address});" f" // {conv(vfunc.name)}") else: write(f"{insn.dest} = {insn.op}({func.name} + {maddr - func.address})") # write(f"{insn.dest} = mov({hvalue})") else: write(f"{insn.dest} = {insn.op}({maddr})") elif insn.op.name.startswith("STR"): if isinstance(insn.offset, Imm) and insn.offset.value == 0: write(f"{insn.op}({insn.base!r}, {insn.dest!r})") else: write(f"{insn.op}({insn.base!r} + {insn.offset!r}, {insn.dest!r})") else: assert False, insn.op elif isinstance(insn, InsnAddr): if insn.Rs == Reg.pc: write( f"{insn.Rd!r} = {insn.op}({insn.Rs!r}, {insn.dest_pc(pc) - pc!r});" f" // pc + {insn.soffset.value}") else: write(f"{insn.Rd!r} = {insn.op}({insn.Rs!r}, {insn.soffset!r})") elif isinstance(insn, InsnStack): assert insn.op == Op.PUSH or insn.op == Op.POP regs_s = ', '.join(map(repr, insn.pure_regs)) if insn.op == Op.POP: regs_s = regs_s.upper() if regs_s: write(f"{insn.op}({'true' if insn.R else 'false'}, {regs_s})") else: write(f"{insn.op}({'true' if insn.R else 'false'})") if insn.op == Op.POP and insn.special_reg == Reg.pc: if not is_clean: write("return") break elif isinstance(insn, InsnMemStack): regs_s = ', '.join(map(repr, insn.regs)) assert regs_s if insn.op == Op.LDMIA: regs_s = regs_s.upper() write(f"{insn.Rb} = {insn.op}({insn.Rb!r}, {regs_s})") elif isinstance(insn, (InsnBranch, InsnLongBranch)): if insn.op == Op.B and branch_offset is not None: write("step()") write(f"offset = {branch_offset}") write(f"continue") else: if target is not None: write(f"{insn.op}({branch_args})") else: if insn.op == Op.BLX: write(f"{insn.op}({insn.dest!r}, {branch_link_arg})") elif insn.op == Op.BL: # TODO: joint_set is full write("crash(); // error") else: write(f"{insn.op}({insn.dest!r})") if not is_clean: write("return") break elif isinstance(insn, InsnBranchIf): if branch_offset is not None: write(f"if ({insn.op}()) {{", semi="") write(f" offset = {branch_offset}") write(f" continue") write(f"}}", semi="") elif target is not None: write(f"if ({insn.op}({branch_args})) return") else: write(f"if ({insn.op}({insn.dest!r})) return") elif isinstance(insn, InsnBranchIf2): if target is not None: write(f"if ({insn.op}({insn.src!r}, {branch_args})) return") else: write(f"if ({insn.op}({insn.src!r}, {insn.dest!r})) return") elif isinstance(insn, InsnSVC): write(f"{insn.op}({insn.soffset!r}, {cb_offset})") else: raise Exception(repr(insn)) if pc_offset in func.stop_set: if not is_clean: write("return") break pc = next_pc else: write("crash(); // auto leave") if not is_clean: print(" }") print(" _ => {") write("crash()") print(" }") print(" }") print(" }") print("}") print()
def walk(func: RawFunction, pc, *, indent=1, visited=None, do_write): def print_indent(): print(end=" " * indent * 4) if visited is None: visited = set() lr: int = None func.joint_set.add(pc - func.address) assert 0 in func.joint_set while pc in func: if pc in visited: return insn = parse(pc, read_ushort(pc), read_ushort(pc + 2)) visited.add(pc) if do_write: print_indent() print(hex(pc - func.address), insn, sep="\t") target = None next_pc = None if insn.op == Op.B: assert isinstance(insn, InsnBranch) assert isinstance(insn.dest, Offset) target = insn.dest.target elif insn.op == Op.BL: if isinstance(insn, (InsnBranch2, InsnBranch)): if do_write: print("warning", insn) func.stop_set.add(pc - 2 - func.address) return else: assert isinstance(insn, InsnLongBranch) assert isinstance(insn.dest, Offset) target = insn.dest.target next_pc = (pc + 3 + 2) & THUMB_MASK elif insn.op == Op.BX: if insn.dest == Reg.lr: target = lr elif isinstance(insn.dest, Reg): pass elif insn.op == Op.BLX: next_pc = pc + 2 if insn.dest == Reg.lr: target = lr lr = next_pc else: func.joint_set.add(next_pc - func.address) # dup elif insn.op == Op.POP: if Reg.pc in insn.regs: if do_write: print(insn.regs) else: next_pc = pc + 2 elif isinstance(insn, InsnBranchIf): target = insn.dest.target next_pc = pc + 2 elif isinstance(insn, InsnMem): assert isinstance(insn.dest, Reg) assert insn.dest.value <= 7 if insn.op.name.startswith("LDR"): if insn.base == Reg.pc: maddr = (pc + insn.offset.value + 4) & 0b11111111_11111111_11111111_11111101 mvalue = read_int(maddr) mfunc: RawFunction = text_map[mvalue] if mfunc is not None: if (mvalue & THUMB_MASK) == mfunc.address: mfunc.has_indirect = True # referenced next_pc = pc + 2 elif isinstance(insn, InsnSVC): next_pc = pc + 2 func.joint_set.add(next_pc - func.address) # dup else: next_pc = pc + 2 if target is not None: new_func: RawFunction = text_map[target] if func == new_func: walk(func, target, indent=indent + 1, visited=visited, do_write=do_write) else: if no_return_func(new_func): next_pc = None if next_pc is not None: if do_write: print("call", new_func) if next_pc is not None: func.joint_set.add(next_pc - func.address) if insn.op == Op.BL or insn.op == Op.BLX: func.point_set.add(next_pc - func.address) if next_pc is None: func.stop_set.add(pc - func.address) return pc = next_pc continue
def load(self, pc) -> Insn: insn = parse(pc, self.read_ushort(pc), self.read_ushort(pc + 2)) return insn