Exemplo n.º 1
0
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()
Exemplo n.º 2
0
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
Exemplo n.º 3
0
    def load(self, pc) -> Insn:
        insn = parse(pc, self.read_ushort(pc), self.read_ushort(pc + 2))

        return insn