Beispiel #1
0
def create_section(start_ea, end_ea, perm, name, all_thumb=True):
    """
    Create section in IDA Pro

    :param start_ea: start address of secion
    :type start_ea: str

    :param end_ea: end address of secion
    :type end_ea: str

    :param perm: Permissions of section (1:ex, 2:read, 4:write)
    :type perm: int

    :param name: name of section
    :type name: str

    :return: success
    :rtype: bool
    """

    try:
        start, end = parse_range(start_ea, end_ea)
    except ValueError:
        return False
    if idc.add_segm_ex(start, end, 0, 1, 1, 0, 0):
        idc.set_segm_name(start, name)
        idc.set_segm_attr(start, idc.SEGATTR_PERM, perm)
        if all_thumb:
            idc.split_sreg_range(start, "T", 1, idc.SR_autostart)
        return True
    return False
Beispiel #2
0
def find_scatter_table():
    scatter_load_bytes = {
        "__scatterload": [
            "0A A0 90 E8 00 0C 82 44",
            "2C 00 8F E2 00 0C 90 E8 00 A0 8A E0 00 B0 8B E0",  # For 5G
        ],
    }

    tables = {}
    for name, prefixes in scatter_load_bytes.items():
        for prefix in prefixes:
            addrs = create_func_by_prefix(name, prefix, force=True)
            for addr in addrs:
                if addr == idc.BADADDR:
                    continue

                offset_addr = idc.get_operand_value(addr, 1)
                if offset_addr == -1:
                    old_flag = idc.get_sreg(addr, "T")
                    idc.split_sreg_range(addr, "T", not old_flag, idc.SR_user)
                    offset_addr = idc.get_operand_value(addr, 1)

                offset = ida_bytes.get_dword(offset_addr)
                offset2 = ida_bytes.get_dword(offset_addr + 4)
                start = (offset + offset_addr) & 0xFFFFFFFF
                end = (offset2 + offset_addr) & 0xFFFFFFFF
                if not idc.is_loaded(start):
                    continue

                tables[start] = end
                print("__scatter_table: 0x%x -> 0x%x" % (start, end))
                func_name = set_entry_name(start, "__scatter_table")

    return tables
Beispiel #3
0
    def setCodeType(self, ea_start, ea_end, code_type):
        """Set the code type for the given address range.

        Args:
            ea_start (int): effective address for the start of the range
            ea_end (int): effective address for the end of the range
            code_type (int): wanted code type for the code range
        """
        for offset in range(ea_end - ea_start):
            idc.split_sreg_range(ea_start + offset, "mips16", code_type, idc.SR_user)
Beispiel #4
0
def load_file(f, neflags, format):
    f.seek(0)

    magic = f.read(4)
    version = struct.unpack("<I", f.read(4))[0]
    flags = struct.unpack("<I", f.read(4))[0]
    memType = struct.unpack("<I", f.read(4))[0]
    serviceType = struct.unpack("<I", f.read(4))[0]
    numInstances = struct.unpack("<I", f.read(4))[0]
    uuid = struct.unpack("<IIII", f.read(16))
    driverId = struct.unpack("<I", f.read(4))[0]
    numThreads = struct.unpack("<I", f.read(4))[0]
    textVA = struct.unpack("<I", f.read(4))[0]
    textLen = struct.unpack("<I", f.read(4))[0]
    dataVA = struct.unpack("<I", f.read(4))[0]
    dataLen = struct.unpack("<I", f.read(4))[0]
    bssLen = struct.unpack("<I", f.read(4))[0]
    entry = struct.unpack("<I", f.read(4))[0]

    f.seek(MCLF_TEXT_INFO_OFFSET)

    idaapi.set_processor_type("arm", ida_idp.SETPROC_LOADER_NON_FATAL)

    # Set VA for .text and add the segment
    f.file2base(0, textVA, textVA + textLen, True)
    idaapi.add_segm(0, textVA, textVA + textLen, ".text", "CODE")

    # Set VA for .data and add the segment
    f.file2base(textLen, dataVA, dataVA + dataLen, True)
    idaapi.add_segm(0, dataVA, dataVA + dataLen, ".data", "DATA")

    # Add BSS segment after .text and .data
    idaapi.add_segm(0, dataVA + dataLen, dataVA + dataLen + bssLen, ".bss",
                    "BSS")

    if entry % 4 == 1:
        #Thumb address is always +1 to set the T bit
        idaapi.add_entry(entry - 1, entry - 1, "_entry", 1)
        idc.split_sreg_range(entry - 1, "T", 0x1, ida_segregs.SR_user)
    else:
        idaapi.add_entry(entry, entry, "_entry", 1)
        idc.split_sreg_range(entry, "T", 0x0, ida_segregs.SR_user)

    ida_bytes.create_data(tlApiLibEntry, idaapi.FF_DWORD, 4, idaapi.BADADDR)
    idc.set_name(tlApiLibEntry, "tlApiLibEntry", ida_name.SN_CHECK)
    return 1
Beispiel #5
0
def import_functions(fncs, add_names=True, always_thumb=True):

    name_counter = defaultdict(int)
    imported_functions = defaultdict(set)
    cfg_conflicts = defaultdict(set)
    symbol_defects = list()
    failed_fncs = list()

    countername_Fail = 0
    counterfct_fail = 0

    def set_name(addr, name):
        # FIXME creates unnamed_178 etc. instead of proper function name in IDA 7.2 on CYW20735
        name = name.replace("sub_", "unnamed_")
        cmt = idc.get_cmt(addr, 0)
        name_cmt = "fcn.%s" % name
        if cmt:
            name_cmt = cmt + ", " + name_cmt
        idc.set_cmt(addr, name_cmt, 0)
        if not add_names:
            return
        if name.isupper():
            try:
                # hackish way to stop warning about hex import
                # FIXME leave 'sub_' intact if it already exists
                # FIXME do not set the name to the hex prologue because it has duplicates, set it to
                #       'pp_{position}' instead
                a = int(name, 16)
                """continue"""
            except ValueError:
                pass
        ok = idc.set_name(addr, name, idc.SN_CHECK)
        if not ok:
            data = (name, size, addr)
            failed_fncs.append((data, "name"))
            print("{0:#x}: cannot add name, {1}".format(addr, name))
            countername_Fail = countername_Fail + 1
        else:
            imported_functions[addr].add(name)

    for name, size, addr, thumb in fncs:
        name_counter[name] += 1

        if not always_thumb:
            idc.split_sreg_range(addr, "T", int(thumb), idc.SR_user)
        idc.create_insn(addr)

        code = is_code(addr)
        fnc = is_func(addr)
        fnc_chnk = is_func_chunk(addr)
        data = (name, size, addr)
        start = get_func_start(addr)
        generic = fnc and func_is_generic(addr)

        # BADADDR automatically finds end of fnc.
        if size >= 2 and size % 2 == 0:
            end = addr + size
        else:
            end = idaapi.BADADDR

        if not code and not (fnc or fnc_chnk):
            symbol_defects.append(data)
        elif (fnc or fnc_chnk) and not code:
            cfg_conflicts[idc.get_func_name(addr)] = (data, start)
        elif start == addr and not generic:
            set_name(addr, name)  # duplicate symbol
        elif start == addr and generic:
            set_name(addr, name)
        elif start != addr:
            if fnc_chnk:
                idc.remove_fchunk(addr, addr)
            elif fnc:
                idc.set_func_end(addr,
                                 addr)  # force previous function to end here.
            ok = idaapi.add_func(addr, end)
            if not ok:
                failed_fncs.append((data, "fnc"))
                print("{0:#x}: cannot add fnc, {1}, {2:#x}".format(
                    addr, size, end))
                counterfct_fail = counterfct_fail + 1
            else:
                set_name(addr, name)
        else:
            failed_fncs.append((data, "unknown"))
            print(
                "{0:#x}: unknown problem - code: {1}, fnc: {2}, start {3:#x}, size {4}, end {5}"
                .format(addr, code, fnc, start, size, end))

    ida_auto.auto_wait()
    print("not added functions: {1} , not added names: {0}".format(
        countername_Fail, counterfct_fail))
    return name_counter, imported_functions, failed_fncs, cfg_conflicts, symbol_defects
def load_file(li, neflags, format):
    # ensure we are not wrongly called
    if not format.startswith("Accessory Firmware Update"):
        return 0

    li.seek(0)
    data = li.read(0x14)
    (magic, xxx1, fw_type, fw_ver, fw_len, unk1, product_id,
     hw_rev_id) = struct.unpack("<HHHHIIHH", data)

    li.seek(0x20)
    AFU_signature_header_data = li.read(24)

    (sig_magic, unknown1, unknown2, digest_type, digest_len, digest_offset,
     sig_type, sig_len, sig_offset) = struct.unpack("<IHHHHIHHI",
                                                    AFU_signature_header_data)

    idaapi.set_processor_type("ARM:ARMv7-M", ida_idp.SETPROC_ALL)

    if product_id == 0x312:  # Apple Pencil
        fw_base = 0x8006080
        msp_base = fw_base
    elif product_id == 0x14c:  # Apple Pencil 2
        if fw_type == 1:
            fw_base = 0x08000980
            msp_base = fw_base + 0x180
        if fw_type == 0x20:
            fw_base = 0x0
            msp_base = fw_base
        if fw_type == 0x30:
            fw_base = 0x0
            msp_base = fw_base
        if fw_type == 0x50:
            fw_base = 0x08000000
            msp_base = fw_base
    elif product_id == 0x26d:  # Siri Remote 2
        if fw_type == 1:
            fw_base = 0x8008080
            msp_base = fw_base + 0x180
        if fw_type == 0xb0:
            fw_base = 0x280
            msp_base = fw_base + 0x180
    elif product_id == 0x268:  # Smart Keyboard 12.9"
        fw_base = 0x08002600
        msp_base = fw_base
    elif product_id == 0x26A:  # Smart Keyboard 9.7"
        fw_base = 0x08002600
        msp_base = fw_base
    elif product_id == 0x26B:  # Smart Keyboard 10.5"
        fw_base = 0x08002600  # don't really know, haven't seen an OTA so far
        msp_base = fw_base
    elif product_id == 0x292:  # Smart Keyboard Folio 11"
        fw_base = 0x08000980  # seems to work
        msp_base = fw_base + 0x180
    elif product_id == 0x293:  # Smart Keyboard Folio 12.9"
        fw_base = 0x08000980  # seems to work
        msp_base = fw_base + 0x180
    else:
        return 0

    # for now a heuristic
    show_header = True
    if fw_type != 1 or fw_base == 0:
        show_header = False

    if show_header:
        li.file2base(0, fw_base - 0x80, fw_base, 1)
    li.file2base(0x80, fw_base, fw_base + fw_len, 1)

    if show_header:
        idaapi.add_segm(0, fw_base - 0x80, fw_base, "HEADER", "DATA")
    idaapi.add_segm(0, fw_base, fw_base + fw_len, "__TEXT", "CODE")
    idaapi.add_segm(0, 0xE000E000, 0xE000F000, "__SYSREG", "DATA")
    idaapi.add_segm(0, SRAM_BASE, SRAM_BASE + SRAM_SIZE, "__SRAM", "DATA")

    if show_header:
        idc.split_sreg_range(fw_base - 0x80, "T", 1)
    idc.split_sreg_range(fw_base, "T", 1)

    # register the structures
    register_structs()

    # apply the structure
    if show_header:
        idc.set_name(fw_base - 0x80, "AFU_HEADER")
        idc.create_struct(fw_base - 0x80, -1, "afu_full_header")
        ida_nalt.unhide_item(fw_base - 0x80 + 1)

    # handle the digest and signature

    if sig_magic == 0x61E34724:

        # apply the structure
        if show_header:
            idc.set_name(fw_base - 0x80 + 0x20, "AFU_SIG_HEADER")
        #idc.create_struct(fw_base - 0x80 + 0x20, -1, "afu_sig_header")
        #ida_nalt.unhide_item(fw_base - 0x80 + 0x20 + 1)

        # first handle the digest
        base = fw_base + fw_len
        li.file2base(digest_offset, base, base + digest_len, 1)
        idaapi.add_segm(0, base, base + digest_len, "__DIGEST", "DATA")
        idc.create_byte(base)
        idc.make_array(base, digest_len)
        idc.set_name(base, "AFU_DIGEST")

        # now handle the signature
        base += digest_len
        li.file2base(sig_offset, base, base + sig_len, 1)
        idaapi.add_segm(0, base, base + sig_len, "__SIGNATURE", "DATA")
        idc.create_byte(base)
        idc.make_array(base, sig_len)
        idc.set_name(base, "AFU_SIGNATURE")

    # check if __TEXT starts with an SRAM address
    # this is the initial MSP that is followed by exception vectors
    initMSP = idc.Dword(msp_base)
    print "initMSP 0x%x" % initMSP
    if (initMSP >= SRAM_BASE) and initMSP <= (SRAM_BASE + SRAM_SIZE):

        idc.set_name(msp_base, "init_MSP")
        idc.create_dword(msp_base)
        idc.op_plain_offset(msp_base, -1, 0)
        idc.set_cmt(msp_base, "Initial MSP value", 0)

        # these are now the exception vectors

        # determine how many exception vectors there are
        cnt = 0
        handlers = {}
        last_multi = None
        multi = False

        while cnt < 255:
            ptr = idc.Dword(msp_base + 4 + 4 * cnt)
            if ptr != 0:

                # must be inside __TEXT
                if (ptr < fw_base) or (ptr > fw_base + fw_len):
                    break

                if (ptr & 1) == 0:  # must be thumb mode
                    break

            # convert into a dword + offset
            idc.create_dword(msp_base + 4 + 4 * cnt)
            if ptr != 0:
                idc.op_offset(msp_base + 4 + 4 * cnt, 0, idc.REF_OFF32, -1, 0,
                              0)
            idc.set_cmt(
                msp_base + 4 + 4 * cnt,
                "exception %d: %s" % (cnt + 1, exception_table[cnt + 1]), 0)

            # should only RESET vector be our entrypoint?
            idc.add_entry(ptr & ~1, ptr & ~1, "", 1)

            # remember how often we see each handler
            if ptr != 0:
                if handlers.has_key(ptr):
                    handlers[ptr] += 1
                    if last_multi != None:
                        if last_multi != ptr:
                            multi = True
                    last_multi = ptr
                else:
                    handlers[ptr] = 1

            cnt += 1

        print "cnt: %d" % cnt

        if cnt > 0:
            i = 1
            while i <= cnt:
                ptr = idc.Dword(msp_base + 4 * i)

                if ptr != 0:
                    # ensure this is
                    if handlers[ptr] == 1:
                        idc.set_name(
                            ptr & ~1,
                            "%s_%s" % (EXCEPTION_PREFIX, exception_table[i]))

                    elif not multi:
                        idc.set_name(ptr & ~1,
                                     "%s_%s" % (EXCEPTION_PREFIX, "UNIMPL"))

                i += 1

    return 1
Beispiel #7
0
def analyze_func_ptr(start_ea, end_ea):
    global FUNC_BY_PTR, FUNC_BY_PTR_TIME
    if "FUNC_BY_PTR" not in globals() or len(FUNC_BY_PTR) == 0:
        FUNC_BY_PTR = set()

    ea = start_ea
    func_cnt = 0
    name_cnt = 0
    start_time = time.time()

    while ea != idc.BADADDR and ea <= end_ea:
        status, ea = is_assigned(ea)
        if status:
            continue

        # now check function pointer
        func_ptr = ida_bytes.get_dword(ea)

        # TODO: skip other segments that are not code.

        # for those already assigned functions, we need to check the segment range.
        if not (start_ea <= func_ptr < end_ea):
            ea = next_addr_aligned(ea, 4)
            continue

        # we only target thumb function to reduce false positives
        if func_ptr & 1 == 0:
            ea = next_addr_aligned(ea, 4)
            continue

        func_ptr = func_ptr - 1
        func_start = idc.get_func_attr(func_ptr, idc.FUNCATTR_START)
        if func_start != idc.BADADDR and func_start != func_ptr:
            # this is not a proper function pointer
            ea = next_addr_aligned(ea, 4)
            continue

        # new thumb function has been found!
        if func_start == idc.BADADDR:
            old_flag = idc.get_sreg(func_ptr, "T")
            idc.split_sreg_range(func_ptr, "T", 1, idc.SR_user)
            status = idc.add_func(func_ptr)
            if not status:
                # IDA automatically make code, and this remains even
                # though add_func fails.
                ida_bytes.del_items(func_ptr, ida_bytes.DELIT_EXPAND)
                idc.split_sreg_range(func_ptr, "T", old_flag, idc.SR_user)

                ea = next_addr_aligned(ea, 4)
                continue

            func_cnt += 1
            FUNC_BY_PTR.add(ea)
            if func_cnt % 10000 == 0:
                print("%x: %d functions has been found. (%0.3f secs)" %
                      (ea, func_cnt, time.time() - start_time))

        # If we find a function, we try to assign a name. The name may be
        # derived by C++ structure.
        if analyze_funcname(ea, func_ptr):
            name_cnt += 1
            func_name = idc.get_func_name(func_ptr)
            if name_cnt % 10000 == 0:
                print("%x: %d names has been found. (%0.3f secs)" %
                      (ea, name_cnt, time.time() - start_time))
            # print("%x: %x => %s" % (ea, func_ptr, func_name))

        ea = next_addr_aligned(ea, 4)

    FUNC_BY_PTR_TIME = time.time() - start_time
    print("Found %d functions, renamed %d functions (%0.3f secs)" %
          (len(FUNC_BY_PTR), name_cnt, FUNC_BY_PTR_TIME))
Beispiel #8
0
def analyze_linear_sweep(start_ea, end_ea=idc.BADADDR):
    global FUNC_BY_LS, FUNC_BY_LS_TIME
    if "FUNC_BY_LS" not in globals() or len(FUNC_BY_LS) == 0:
        FUNC_BY_LS = set()

    cand_cnt = 0
    func_cnt = 0
    ea = start_ea
    start_time = time.time()
    while ea < end_ea and ea != idc.BADADDR:
        ea, mode = find_next_func_cand(ea, end_ea)
        if ea == idc.BADADDR:
            break

        cand_cnt += 1
        if cand_cnt % 10000 == 0:
            print("%x: %d/%d function has been found (%d secs)" %
                  (ea, func_cnt, cand_cnt, time.time() - start_time))

        # set IDA segment register to specify ARM mode
        old_flag = idc.get_sreg(ea, "T")
        if mode == THUMB:
            idc.split_sreg_range(ea, "T", 1, idc.SR_user)
        elif mode == ARM:
            idc.split_sreg_range(ea, "T", 0, idc.SR_user)
        else:
            print("Unknown mode")
            raise NotImplemented

        # add_func ignores the existing function, but existing function is
        # already filtered when finding the candidate
        status = idc.add_func(ea)
        if status:
            func_cnt += 1
            FUNC_BY_LS.add(ea)

            # Wait IDA's auto analysis
            ida_auto.auto_wait()

            # even though add_func succeed, it may not be correct.
            # TODO: how to check the correctness? we may check the function end?
            func_end = idc.get_func_attr(ea, idc.FUNCATTR_END)
            if func_end > ea:
                ea = func_end
            else:
                # sometimes, ida returns wrong addr
                ea = next_addr_aligned(ea)

        else:
            if idc.get_func_attr(ea, idc.FUNCATTR_START) == idc.BADADDR:
                # IDA automatically make code, and this remains even though
                # add_func fails.
                ida_bytes.del_items(ea, ida_bytes.DELIT_EXPAND)

                # reset IDA segment register to previous ARM mode
                idc.split_sreg_range(ea, "T", old_flag, idc.SR_user)

                # Wait IDA's auto analysis
                ida_auto.auto_wait()

            ea = next_addr_aligned(ea)

    # linear sweep may choose wrong prologs. We merge the prologs of two
    # adjacent functions.
    if func_cnt > 0:
        fix_func_prolog(start_ea, end_ea)

    FUNC_BY_LS_TIME = time.time() - start_time
    print("Found %d/%d functions. (%d sec)" %
          (len(FUNC_BY_LS), cand_cnt, FUNC_BY_LS_TIME))
Beispiel #9
0
def SetReg(ea, reg, value):
    return idc.split_sreg_range(ea, reg, value, SR_user)