def main(): for segstart, segend, segname in enum_segments(): if segname not in ('.rdata', 'UPX1'): continue print(segname) for src, dst, size in find_pointers(segstart, segend): if idc.get_segm_name(dst) not in (".text", "UPX0"): continue if is_code(dst): continue print("new function pointer: 0x%x -> 0x%x" % (src, dst)) ida_auto.auto_make_code(dst) ida_auto.auto_make_proc(dst) ida_bytes.del_items(src, size) ida_bytes.create_data(src, idc.FF_QWORD if size == 8 else idc.FF_DWORD, size, idc.BADADDR) # this doesn't seem to always work :-( idc.op_plain_offset(src, -1, 0) ida_name.set_name(src, "j_%s_%x" % (src, dst))
def resolvePointers(fileRange, pointerRange, verbose=True): """ :param fileRange: tuple of (int, int) representing the file to resolve pointers in :param pointerRange: tuple of (int, int) representing range of pointers to resolve :param verbose: if True, all changes are printed :return: """ if verbose: print("(%07X, %07X): expand unknown arrays" % (fileRange[0], fileRange[1])) expandUnkArrays(*fileRange, verbose=False) ea = fileRange[0] while ea < fileRange[1]: ea = next.unkptr(ea, fileRange[1], rom=True, ui=False, hexOut=False) if ea != idaapi.BADADDR: # get data at ea chars = idc.get_bytes(ea, 4) dword = 0 for i in range(len(chars)): dword += ord(chars[i]) << 8 * i if pointerRange[0] <= dword < pointerRange[1]: if verbose: print('%07X: %07X' % (ea, dword)) idc.op_plain_offset(ea, 0, 0)
def req_patch(self, hash): addr, value, length = hash['addr'], hash['value'], hash['len'] if length != 4 and length != 8: print("[x] unsupported length: %d" % length) return if length == 4: prev_value = Dword(addr) if MakeDword(addr) != 1: print('[x] MakeDword failed') if PatchDword(addr, value) != 1: print('[x] PatchDword failed') if not idc.op_plain_offset(addr, 0, 0): print('[x] op_plain_offset failed') elif length == 8: prev_value = Qword(addr) if MakeQword(addr) != 1: print('[x] MakeQword failed') if PatchQword(addr, value) != 1: print('[x] PatchQword failed') if not idc.op_plain_offset(addr, 0, 0): print('[x] op_plain_offset failed') print("[*] patched 0x%x = 0x%x (previous was 0x%x)" % (addr, value, prev_value))
def req_patch(self, hash): addr, value, length = hash['addr'], hash['value'], hash['len'] if length == 4: prev_value = idc.get_wide_dword(addr) if not ida_bytes.create_data(ea, FF_DWORD, 4, ida_idaapi.BADADDR): rs_log('[x] ida_bytes.create_data FF_DWORD failed') if not ida_bytes.patch_dword(addr, value): rs_log('[x] patch_dword failed') if not idc.op_plain_offset(addr, 0, 0): rs_log('[x] op_plain_offset failed') elif length == 8: prev_value = idc.get_qword(addr) if not ida_bytes.create_data(addr, FF_QWORD, 8, ida_idaapi.BADADDR): rs_log('[x] ida_bytes.create_data FF_QWORD failed') if not ida_bytes.patch_qword(addr, value): rs_log('[x] patch_qword failed') if not idc.op_plain_offset(addr, 0, 0): rs_log('[x] op_plain_offset failed') else: rs_log("[x] unsupported length: %d" % length) return rs_log("patched 0x%x = 0x%x (previous was 0x%x)" % (addr, value, prev_value))
def set_offset(self, base=0): """ Set this operand as being an offset. .. todo:: this should be a setter on a property ? .. todo:: doc base arg """ idc.op_plain_offset(self.instr.ea, self.opnum, base)
def convert_pointer_to_offset(ea): # If this is code, skip it. flags = idc.get_full_flags(ea) if idc.is_code(flags): return # If the value at this address does not point into the kernelcache, skip it. value = idc.get_qword(ea) if not is_mapped(value, 8): return # Convert this value to a qword (in case it's unaligned) and then convert it into an # offset. idc.create_qword(ea) idc.op_plain_offset(ea, 0, 0)
def convert_vtable_to_offsets(vtable, length=None): """Convert a vtable into a sequence of offsets. Arguments: vtable: The address of the virtual method table. Options: length: The length of the vtable, if known. Returns: True if the data was successfully converted into offsets. """ if length is None: length = vtable_length(vtable) if not length: _log(0, 'Address {:#x} is not a vtable', vtable) return False successful = True for address in idau.Addresses(vtable, length=length, step=idau.WORD_SIZE): # Handle addresses that are part of ALIGN directives if idaapi.is_align_insn(address) != 0: head = idaapi.get_item_head(address) idaapi.del_items(head) if not idc.op_plain_offset(address, 0, 0): _log(0, 'Could not change address {:#x} into an offset', address) successful = False return successful
def _process_offset(offset, ea, next_offset): """Process an offset in a __got section.""" # Convert the address containing the offset into an offset in IDA, but continue if it fails. if not idc.op_plain_offset(ea, 0, 0): _log(1, 'Could not convert {:#x} into an offset', ea) # Get the name to which the offset refers. name = idau.get_ea_name(offset, user=True) if not name: _log(3, 'Offset at address {:#x} has target {:#x} without a name', ea, offset) return False # Make sure this isn't an offset to another stub or to a jump function to another stub. See the # comment in _symbolicate_stub. if stub.symbol_references_stub(name): _log( 1, 'Offset at address {:#x} has target {:#x} (name {}) that references a stub', ea, offset, name) return False # Set the new name for the offset. symbol = next_offset(name) if symbol is None: _log(0, 'Could not generate offset symbol for {}: names exhausted', name) return False if not idau.set_ea_name(ea, symbol, auto=True): _log(2, 'Could not set name {} for offset at {:#x}', symbol, ea) return False return True
def main(): for segstart, segend, segname in enum_segments(): if segname not in ('.rdata', 'UPX1' ): continue for src, dst, psize in find_pointers(segstart, segend): if idc.get_segm_name(dst) not in (".rdata", "UPX0"): continue if psize == 8: size = ida_bytes.get_qword(src + 0x8) else: size = ida_bytes.get_dword(src + 0x4) if size > 0x100: continue if size <= 2: continue buf = ida_bytes.get_bytes(dst, size) if not buf: continue if b"\x00" in buf: continue try: s = buf.decode("ascii") except UnicodeDecodeError: continue print("string pointer: 0x%x -> 0x%x: %s" % (src, dst, s)) ida_bytes.del_items(src, 1) ida_bytes.set_cmt(dst, s, True) # pointer ida_bytes.del_items(src, psize) ida_bytes.create_data(src, idc.FF_QWORD if size == 8 else idc.FF_DWORD, psize, idc.BADADDR) # this doesn't seem to always work :-( idc.op_plain_offset(src, -1, 0) ida_name.set_name(src, "s_%x" % (src)) ida_bytes.set_cmt(src, s, True) # size ida_bytes.del_items(src + psize, psize) ida_bytes.create_data(src + psize, idc.FF_QWORD if size == 8 else idc.FF_DWORD, psize, idc.BADADDR)
def initialize_data_offsets(): """Convert offsets in data segments into offsets in IDA. Segment names must be initialized with segments.initialize_segments() first. """ # Normally, for user-space programs, this operation would be dangerous because there's a good # chance that a valid userspace address would happen to show up in regular program data that is # not actually an address. However, since kernel addresses are numerically much larger, the # chance of this happening is much less. for seg in idautils.Segments(): name = idc.get_segm_name(seg) if not (name.endswith('__DATA_CONST.__const') or name.endswith('__got') or name.endswith('__DATA.__data') or name.endswith('__DATA_CONST.__auth_ptr')): continue for word, ea in idau.ReadWords(seg, idc.get_segm_end(seg), addresses=True): if idau.is_mapped(word, value=False): idc.op_plain_offset(ea, 0, 0)
def init_vtable(self): my_start_idx = self.vtable_start_idx() # Fix: support 64bit work if idc.__EA64__: pointer_size = idaapi.DEF_ADDRSIZE pfn_make_ptr = lambda x: ida_bytes.create_data(x, idc.FF_QWORD, 8, idaapi.BADADDR) #MakeQword pfn_get_ptr_value = ida_bytes.get_qword else: pointer_size = idaapi.DEF_ADDRSIZE pfn_make_ptr = lambda x: ida_bytes.create_data(x, idc.FF_DWORD, 4, idaapi.BADADDR) #ida_bytes.MakeDword pfn_get_ptr_value = ida_bytes.get_dword for idx, ea in enumerate(range(self.vtable_start, self.vtable_end, pointer_size)): pfn_make_ptr(ea) idc.op_plain_offset(ea, 0, 0) dst = pfn_get_ptr_value(ea) if idx < my_start_idx: base_method = self.base.vmethods[idx] if base_method.is_dst_equal(dst): # Method from base class self.vmethods.append(self.base.vmethods[idx]) elif Method.s_is_pure_virtual_dst(dst): # New pure virtual override opvm = PureVirtualOverrideMethod(self, base_method, idx) opvm.refresh() self.vmethods.append(opvm) elif Method.s_is_deleted_virtual_dst(dst): # New deleted override dom = DeletedOverrideMethod(self, base_method, idx) dom.refresh() self.vmethods.append(dom) else: # New override om = OverrideMethod(dst, self, base_method, idx) om.refresh() self.vmethods.append(om) elif Method.s_is_pure_virtual_dst(dst): # New pure virtual pvm = PureVirtualMethod(self, 'vf%X' % (idx*4), idx) pvm.refresh() self.vmethods.append(pvm) elif Method.s_is_deleted_virtual_dst(dst): # New deleted virtual pvm = DeletedVirtualMethod(self, 'vf%X' % (idx*4), idx) pvm.refresh() self.vmethods.append(pvm) else: # New virtual vm = VirtualMethod(dst, self, 'vf%X' % (idx*4), idx) vm.refresh() self.vmethods.append(vm)
def make_date_ref(): ea = 0x9561C max_ea = ida_ida.inf_get_max_ea() min_ea = ida_ida.inf_get_min_ea() while True: ea = ida_search.find_unknown(ea, idc.SEARCH_DOWN | idc.SEARCH_NEXT) if ea > max_ea: break size = idc.get_item_size(ea) print(hex(ea)) val = idc.get_wide_dword(ea) # if 0xfff38 < val < 0x188544 or 0x1f000000 < val < 0x1ffa3fd9 or 0x20000000 < val < 0x2001ffff: # idc.OpOff(ea, 0, 0) if min_ea < val < max_ea: idc.op_plain_offset(ea, 0, 0)
def main(): for segstart, segend, segname in enum_segments(): if segname not in ('.text', '.data'): continue for src, dst in find_pointers(segstart, segend): if is_code(src): # ignore instructions like: # # call ds:__vbaGenerateBoundsError #print('code pointer: 0x%x -> 0x%x' % (src, dst)) continue if is_in_string(src): # for example, the following contains 0x444974 (a common valid offset): # # text:004245B0 aRequestid db 'requestID', # # enable or disable this behavior as you wish print('string pointer: 0x%x -> 0x%x' % (src, dst)) pass #continue print('pointer from 0x%x to 0x%x' % (src, dst)) if is_unknown(dst): print('destination unknown, making byte: 0x%x' % (dst)) ida_bytes.create_data(dst, FF_BYTE, 1, ida_idaapi.BADADDR) elif is_head(dst): # things are good pass else: # need to undefine head, and make byte head_va = get_head(dst) print('destination overlaps with head: 0x%x' % (head_va)) ida_bytes.del_items(head_va, dst - head_va) ida_bytes.create_data(head_va, FF_BYTE, 1, ida_idaapi.BADADDR) ida_bytes.create_data(dst, FF_BYTE, 1, ida_idaapi.BADADDR) ida_bytes.del_items(src, 4) ida_bytes.create_data(src, FF_DWORD, 4, ida_idaapi.BADADDR) # this doesn't seem to always work :-( idc.op_plain_offset(src, -1, 0)
def load(infos): insn = ida_ua.insn_t() for info in infos: # Find or create struct. struct_id = ida_struct.get_struc_id(info['name']) if struct_id == BADADDR: print('[IDA-Sync] Creating new struct %s.' % info['name']) struct_id = ida_struct.add_struc(info['idx'], info['name']) struct = ida_struct.get_struc(struct_id) ida_struct.set_struc_idx(struct, info['idx']) # Create struct members. for member in info['members']: ida_struct.add_struc_member( struct, member['name'], member['offset'], # flag 0, # opinfo_t instance... maybe it should sometimes be # something? None, member['size'], ) # Create xrefs to members of the struct as offsets. for xref in info['xrefs']: typ = xref['type'] # Offset xref. if typ == 1: # TODO figure out what second argument does. idc.op_plain_offset(xref['from'], 1, xref['offset']) # Read/write xrefs. elif typ in [2, 3]: ida_ua.create_insn(xref['from'], insn) idc.op_stroff(insn, 1, struct.id, 0) # TODO do the other cases come up? else: pass
def convert_vtable_to_offsets(vtable, length=None): """Convert a vtable into a sequence of offsets. Arguments: vtable: The address of the virtual method table. Options: length: The length of the vtable, if known. Returns: True if the data was successfully converted into offsets. """ if length is None: length = vtable_length(vtable) if not length: _log(0, 'Address {:#x} is not a vtable', vtable) return False successful = True for address in idau.Addresses(vtable, length=length, step=idau.WORD_SIZE): if not idc.op_plain_offset(address, 0, 0): _log(0, 'Could not change address {:#x} into an offset', address) successful = False return successful
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 OpOffset(ea, base): return idc.op_plain_offset(ea, -1, base)
def ida_make_offset(f, ea): if f.armv7: idaapi.create_data(ea, idc.FF_DWORD, 4, idaapi.BADADDR) else: idaapi.create_data(ea, idc.FF_QWORD, 8, idaapi.BADADDR) idc.op_plain_offset(ea, 0, 0)
def untag_pointer(ea, tp): _log(4, 'Untagging pointer at {:x}', ea) idau.patch_word(ea, tagged_pointer_untag(tp)) idc.op_plain_offset(ea, 0, 0)
def sync_data_definitions_in(source_units, address_space, ea): from edit_source.include.definitions import DATA_TYPES def create_array_of(ea, count, size, op, error=False, verbose=False): ops.delete_items(ea, size) status = op(ea) if count != 1: status = idc.make_array(ea, count) if verbose: if status is False: print('error: {:07X}: failed to perform operation'.format(ea)) if error: if status is False: raise Exception('{:07X}: failed to perform operation'.format(ea)) return status unit = source_unit.get_physical_unit(source_units, ea) UNIT_IDS = asm_file.AsmFile.UNIT_IDS if unit['unit']['id'] is UNIT_IDS.DATA: print('{:07X} <{}>'.format(unit['ea'], unit['name'])) cur_ea = ea for t in unit['types']: idc.jumpto(cur_ea) data_type = t[0] count = t[1] print('{} {:07X} {}'.format(DATA_TYPES.STR[data_type], cur_ea, count)) if data_type is DATA_TYPES.BYTE: size = count create_array_of(cur_ea, count, size, idc.create_byte, error=True) cur_ea += size elif data_type is DATA_TYPES.WORD: size = count * 4 create_array_of(cur_ea, count, size, idc.create_dword, error=True) cur_ea += size elif data_type is DATA_TYPES.HWORD: size = count * 2 create_array_of(cur_ea, count, size, idc.create_word, error=True) cur_ea += size elif data_type is DATA_TYPES.OFF: xrefs = unit['xrefs_from'] first_label_ea = xrefs[0][0] size = count * 4 if not (gba_address.is_ewram_address(first_label_ea) or gba_address.is_iwram_address( first_label_ea) or gba_address.is_rom_address(first_label_ea)): # a constant, don't point. # TODO: actually include the constant in IDA create_array_status = create_array_of(cur_ea, count, size, idc.create_dword, error=True) idc.set_array_params(ea, 0, 1, 0) pass else: ops.delete_items(cur_ea, size) idc.create_dword(cur_ea) idc.op_plain_offset(cur_ea, 0, 0) create_array_status = True if count != 1: create_array_status = idc.make_array(cur_ea, count) idc.set_array_params(cur_ea, 0, 1, 0) if create_array_status is False: raise Exception('could not create array at {:07X} with count {}'.format(cur_ea, count)) # TODO: check if it has an offset and fix that cur_ea += size elif data_type is DATA_TYPES.ALIGN: pass elif data_type is DATA_TYPES.SPACE: pass elif data_type is DATA_TYPES.INCBIN: pass idc.jumpto(cur_ea)