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
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
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)
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
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
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))
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))
def SetReg(ea, reg, value): return idc.split_sreg_range(ea, reg, value, SR_user)