def __init__(self, ea): self.ea = ea self.funcname_or_segname = "" self.text = "" if not ida_bytes.is_code(ida_bytes.get_flags(ea)): ida_ua.create_insn(ea) # text t = ida_lines.generate_disasm_line(ea) if t: self.text = ida_lines.tag_remove(t) # funcname_or_segname n = ida_funcs.get_func_name(ea) \ or ida_segment.get_segm_name(ida_segment.getseg(ea)) if n: self.funcname_or_segname = n
def handle_operand(self, insn, op, isRead): flags = ida_bytes.get_flags(insn.ea) is_offs = ida_bytes.is_off(flags, op.n) dref_flag = ida_xref.dr_R if isRead else ida_xref.dr_W def_arg = ida_bytes.is_defarg(flags, op.n) optype = op.type itype = insn.itype # create code xrefs if optype == ida_ua.o_imm: makeoff = False if itype in [self.itype_ncall, self.itype_call]: insn.add_cref(op.value, op.offb, ida_xref.fl_CN) makeoff = True #elif itype == self.itype_mov: # e.g., mov #addr, PC # insn.add_cref(op.value, op.offb, ida_xref.fl_JN) # makeoff = True if makeoff and not def_arg: otype = ida_offset.get_default_reftype(insn.ea) ida_offset.op_offset(insn.ea, op.n, otype, ida_idaapi.BADADDR, insn.cs) is_offs = True if is_offs: insn.add_off_drefs(op, ida_xref.dr_O, 0) elif optype == ida_ua.o_near: if insn.itype in [self.itype_ncall, self.itype_call]: fl = ida_xref.fl_CN else: fl = ida_xref.fl_JN insn.add_cref(op.addr, op.offb, fl) # create data xrefs elif optype == ida_ua.o_mem: insn.create_op_data(op.addr, op.offb, op.dtype) insn.add_dref(op.addr, op.offb, dref_flag) '''
def activate(self, ctx): cur_ea = ida_kernwin.get_screen_ea() pfn = ida_funcs.get_func(cur_ea) if pfn: v = ida_kernwin.get_current_viewer() result = ida_kernwin.get_highlight(v) if result: stkvar_name, _ = result frame = ida_frame.get_frame(cur_ea) sptr = ida_struct.get_struc(frame.id) mptr = ida_struct.get_member_by_name(sptr, stkvar_name) if mptr: fii = ida_funcs.func_item_iterator_t() ok = fii.set(pfn) while ok: ea = fii.current() F = ida_bytes.get_flags(ea) for n in range(ida_ida.UA_MAXOP): if not ida_bytes.is_stkvar(F, n): continue insn = ida_ua.insn_t() if not ida_ua.decode_insn(insn, ea): continue v = ida_frame.calc_stkvar_struc_offset( pfn, insn, n) if v >= mptr.soff and v < mptr.eoff: print("Found xref at 0x%08x, operand #%d" % (ea, n)) ok = fii.next_code() else: print("No stack variable named \"%s\"" % stkvar_name) else: print("Please position the cursor within a function")
def _data_type_enum(self) -> int: """The data type as a IDA enum""" if self.is_stack: flags = self._member.flag else: flags = ida_bytes.get_flags(self.addr) return flags & idc.DT_TYPE
def dump(): ret = [] for addr, name in idautils.Names(): flags = ida_bytes.get_flags(addr) # The 'a' heuristic is fairly bad but we need to filter out IDA's default # naming for strings if ida_bytes.has_dummy_name(flags) or ida_bytes.has_auto_name(flags) or not ida_bytes.is_data(flags) or name[0] == 'a': continue # Sometimes the auto-generated names don't actually usually have the # right flags set, so skip these auto-looking names. if any(name.startswith(s) for s in ['byte_', 'word_', 'dword_', 'unk_', 'jpt_']): continue sz = ida_bytes.get_item_size(addr) if ida_bytes.is_struct(flags): ti = ida_nalt.opinfo_t() ida_bytes.get_opinfo(ti, addr, 0, flags) typ = ida_struct.get_struc_name(ti.tid) else: typ = None ret.append(filter_none({ 'address': addr, 'name': name, 'type': typ, 'sz': sz, 'flags': flags, })) return ret
def restore_x(unique_name=None, start=None): ea = ida_kernwin.get_screen_ea() # signature if not unique_name: if not start: seg = ida_segment.getseg(ea) start = seg.start_ea sig_bytes = ida_bytes.get_bytes(start, SIGNATURE_SIZE) sig_hash = hashlib.md5(sig_bytes).hexdigest() unique_name = sig_hash if not start: seg = ida_segment.getseg(ea) start = seg.start_ea if MD5_hash_data_file and os.path.isfile(MD5_hash_data_file): with open(MD5_hash_data_file, "rb") as ifile: received_data = pickle.loads(ifile.read()) saved_data = received_data print("dumpDyn::restore\n\ Name: {}\n\ Restore address: {}\n".format(unique_name, hex(start))) # (start_addr, end_addr, names, comms, bpts, funcs) if unique_name in saved_data: current_data = saved_data[unique_name] # restore names names = current_data[2] for name in names: # names: (rel_addr, name, is_code) ida_name.set_name(start + name[0], name[1]) flags = ida_bytes.get_flags(start + name[0]) if name[2] and not ida_bytes.is_code(flags): ida_auto.auto_make_code(start + name[0]) # restore comments # comms: (rel_addr, TYPE, comment) comms = current_data[3] for comm in comms: # 0:MakeComm and 1:MakeRptCmt ida_bytes.set_cmt(start + comm[0], comm[2], comm[1]) # restore breakpoints # bpts: (rel_addr, size, type) bpts = current_data[4] for bpt in bpts: ida_dbg.add_bpt(start + bpt[0], bpt[1], bpt[2]) # restore functions funcs_addr = current_data[5] for addr in funcs_addr: ida_auto.auto_make_proc(start + addr) # make code & func
def getBlocks(self, function_offset): blocks = [] function_chart = ida_gdl.FlowChart(ida_funcs.get_func(function_offset)) for block in function_chart: extracted_block = [] for instruction in idautils.Heads(block.start_ea, block.end_ea): if ida_bytes.is_code(ida_bytes.get_flags(instruction)): extracted_block.append(instruction) if extracted_block: blocks.append(extracted_block) return sorted(blocks)
def Callees(ea): pfn = ida_funcs.get_func(ea) callees = [] if pfn: for item in pfn: F = ida_bytes.get_flags(item) if ida_bytes.is_code(F): insn = ida_ua.insn_t() if ida_ua.decode_insn(insn, item): if ida_idp.is_call_insn(insn): if insn.ops[0].type in [ida_ua.o_near, ida_ua.o_far]: callees.append(insn.ops[0].addr) return list(dict.fromkeys(callees))
def op_type_changed(self, ea, n): flags = ida_bytes.get_flags(ea) self.log("op_type_changed(ea=0x%08X, n=%d). Flags now: 0x%08X" % (ea, n, flags)) buf = ida_nalt.opinfo_t() opi = ida_bytes.get_opinfo(buf, ea, n, flags) if opi: if ida_bytes.is_struct(flags): self.log("New struct: 0x%08X (name=%s)" % ( opi.tid, ida_struct.get_struc_name(opi.tid))) elif ida_bytes.is_strlit(flags): encidx = ida_nalt.get_str_encoding_idx(opi.strtype) if encidx == ida_nalt.STRENC_DEFAULT: encidx = ida_nalt.get_default_encoding_idx(ida_nalt.get_strtype_bpu(opi.strtype)) encname = ida_nalt.get_encoding_name(encidx) strlen = ida_bytes.get_max_strlit_length( ea, opi.strtype, ida_bytes.ALOPT_IGNHEADS | ida_bytes.ALOPT_IGNCLT) raw = ida_bytes.get_strlit_contents(ea, strlen, opi.strtype) or b"" self.log("New strlit: 0x%08X, raw hex=%s (encoding=%s)" % ( opi.strtype, binascii.hexlify(raw), encname)) elif ida_bytes.is_off(flags, n): self.log("New offset: refinfo={target=0x%08X, base=0x%08X, tdelta=0x%08X, flags=0x%X}" % ( opi.ri.target, opi.ri.base, opi.ri.tdelta, opi.ri.flags)) elif ida_bytes.is_enum(flags, n): self.log("New enum: 0x%08X (enum=%s), serial=%d" % ( opi.ec.tid, ida_enum.get_enum_name(opi.ec.tid), opi.ec.serial)) pass elif ida_bytes.is_stroff(flags, n): parts = [] for i in range(opi.path.len): tid = opi.path.ids[i] parts.append("0x%08X (name=%s)" % (tid, ida_struct.get_struc_name(tid))) self.log("New stroff: path=[%s] (len=%d, delta=0x%08X)" % ( ", ".join(parts), opi.path.len, opi.path.delta)) elif ida_bytes.is_custom(flags) or ida_bytes.is_custfmt(flags, n): self.log("New custom data type") # unimplemented else: print("Cannot retrieve opinfo_t")
def iter_dynamic_functions() -> Iterable[Tuple[int, str]]: """ Iterates the dynamically resolved function signatures. :yield: (ea, name) """ # Look for data elements in the .data segment in which IDA has placed # a function signature element on. data_segment = ida_segment.get_segm_by_name(".data") for ea in idautils.Heads(start=data_segment.start_ea, end=data_segment.end_ea): flags = ida_bytes.get_flags(ea) if idc.is_data(flags) and not idc.is_strlit(flags) and _is_func_type( ea): yield ea, ida_name.get_name(ea)
def show_microcode(): """Generates and displays microcode for an address range. An address range can be a selection of code or that of the current function.""" sel, sea, eea = kw.read_range_selection(None) pfn = ida_funcs.get_func(kw.get_screen_ea()) if not sel and not pfn: return (False, "Position cursor within a function or select range") if not sel and pfn: sea = pfn.start_ea eea = pfn.end_ea addr_fmt = "%016x" if ida_ida.inf_is_64bit() else "%08x" fn_name = (ida_funcs.get_func_name(pfn.start_ea) if pfn else "0x%s-0x%s" % (addr_fmt % sea, addr_fmt % eea)) F = ida_bytes.get_flags(sea) if not ida_bytes.is_code(F): return (False, "The selected range must start with an instruction") text, mmat, mba_flags = ask_desired_maturity() if text is None and mmat is None: return (True, "Cancelled") if not sel and pfn: mbr = hr.mba_ranges_t(pfn) else: mbr = hr.mba_ranges_t() mbr.ranges.push_back(ida_range.range_t(sea, eea)) hf = hr.hexrays_failure_t() ml = hr.mlist_t() mba = hr.gen_microcode(mbr, hf, ml, hr.DECOMP_WARNINGS, mmat) if not mba: return (False, "0x%s: %s" % (addr_fmt % hf.errea, hf.desc())) vp = printer_t() mba.set_mba_flags(mba_flags) mba._print(vp) mcv = microcode_viewer_t() if not mcv.Create( mba, "0x%s-0x%s (%s)" % (addr_fmt % sea, addr_fmt % eea, text), text, fn_name, vp.get_mc()): return (False, "Error creating viewer") mcv.Show() return (True, "Successfully generated microcode for 0x%s..0x%s" % (addr_fmt % sea, addr_fmt % eea))
def get_custom_viewer_hint(self, view, place): try: widget = ida_kernwin.get_current_widget() if ida_kernwin.get_widget_type(widget) != ida_kernwin.BWN_DISASM: return None curline = ida_kernwin.get_custom_viewer_curline(view, True) # sometimes get_custom_viewer_place() returns [x, y] and sometimes [place_t, x, y]. # we want the place_t. viewer_place = ida_kernwin.get_custom_viewer_place(view, True) if len(viewer_place) != 3: return None _, x, y = viewer_place ea = place.toea() # "color" is a bit of misnomer: its the type of the symbol currently hinted color = get_color_at_char(curline, x) if color != ida_lines.COLOR_ADDR: return None # grab the FAR references to code (not necessarilty a branch/call/jump by itself) far_code_references = [ xref.to for xref in idautils.XrefsFrom(ea, ida_xref.XREF_FAR) if ida_bytes.is_code(ida_bytes.get_flags(xref.to)) ] if len(far_code_references) != 1: return None fva = far_code_references[0] # ensure its actually a function if not idaapi.get_func(fva): return None # this magic constant is the number of "important lines" to display by default. # the remaining lines get shown if you scroll down while the hint is displayed, revealing more lines. return render_function_hint(fva), DEFAULT_IMPORTANT_LINES_NUM except Exception as e: logger.warning( 'unexpected exception: %s. Get in touch with @williballenthin.', e, exc_info=True) return None
def Heads(start=None, end=None): """ Get a list of heads (instructions or data) @param start: start address (default: inf.min_ea) @param end: end address (default: inf.max_ea) @return: list of heads between start and end """ if not start: start = ida_ida.cvar.inf.min_ea if not end: end = ida_ida.cvar.inf.max_ea ea = start if not idc.is_head(ida_bytes.get_flags(ea)): ea = ida_bytes.next_head(ea, end) while ea != ida_idaapi.BADADDR: yield ea ea = ida_bytes.next_head(ea, end)
def get_custom_viewer_hint(self, viewer, place): widget_type = ida_kernwin.get_widget_type(viewer) if widget_type != ida_kernwin.BWN_DISASM: return flags = ida_bytes.get_flags(place.toea()) if place else 0 if not ida_bytes.is_code(flags): return item = AMIE.extract_item(viewer) if not item: return tag, val = item hint = self.arch.hint(place.toea(), tag, val) if not hint: return return AMIE.format_hint(*hint)
def main(): typelib = None for typelib_name in DEFAULT_TYPELIBS: typelib = ida_typeinf.load_til(typelib_name) if typelib: break if not typelib: print(f"Unable to load mssdk") return # print(f"Loaded mssdk type library: {typelib.name}") status_enum = ida_typeinf.import_type(typelib, -1, "MACRO_STATUS") if status_enum == ida_netnode.BADNODE: print(f"Unable to load type MACRO_STATUS") # print(f"Loaded MACRO_STATUS") # Grab the enum members enum_map = get_enum_map(status_enum) cur_member_id = ida_enum.get_first_enum_member(status_enum) while cur_member_id: cur_value = idaapi.get_const_value(cur_member_id) enum_map[cur_value] = cur_member_id cur_member_id = ida_enum.get_next_enum_member(cur_member_id) # Iterate over all untyped immediates imm_ea = ida_ida.cvar.inf.min_ea while (imm_ea != idaapi.BADADDR): imm_ea = ida_search.find_notype( imm_ea, ida_search.SEARCH_NEXT | ida_search.SEARCH_DOWN)[0] if ida_bytes.is_code(ida_bytes.get_flags(imm_ea)): enum_value = idc.get_operand_value(imm_ea, 1) # Only handle likely error types right now.. if (enum_value & DWORD_MASK) > 0xC0000000: try: cid = enum_map[enum_value] # print(f"Applying {hex(enum_value & DWORD_MASK)} at {hex(imm_ea)}") # We don't need the member, we can just apply the enum type to the address ida_bytes.op_enum(imm_ea, 1, status_enum, 0) except KeyError: continue
def dbg_trace(self, tid, ip): if ip in self._visited_addrs: return 1 self._visited_addrs.add(ip) if idc.is_unknown(ida_bytes.get_flags(ip)): ida_ua.create_insn(ip) else: idc.msg( 'Skipping explored EA at address 0x{0:X}\n'.format(ip) ) # print idc.generate_disasm_line(ip, 0) if ida_ua.decode_insn(ip) > 0: if 'ret' in ida_ua.cmd.get_canon_mnem().lower(): idc.msg('Found ret instruction, suspending execution\n') ida_dbg.suspend_process() else: idc.msg( 'Unable to decode instruction at address 0x{0:X}\n'.format(ip) ) ida_dbg.suspend_process() return 0
def dump(): ret = [] for addr, name in idautils.Names(): flags = ida_bytes.get_flags(addr) if ida_bytes.has_dummy_name(flags) or ida_bytes.has_auto_name( flags) or not ida_bytes.is_data(flags): print('skip auto:', name) continue # Sometimes the auto-generated names don't actually usually have the # right flags set, so skip these auto-looking names. if any( name.startswith(s) for s in ['byte_', 'word_', 'dword_', 'unk_', 'jpt_']): continue # print('%08x' % addr, '%08x' % flags, name, # ida_bytes.is_data(flags)) sz = ida_bytes.get_item_size(addr) if ida_bytes.is_struct(flags): ti = ida_nalt.opinfo_t() ida_bytes.get_opinfo(ti, addr, 0, flags) # itemsize = ida_bytes.get_data_elsize(addr, flags, ti) typ = ida_struct.get_struc_name(ti.tid) else: # itemsize = ida_bytes.get_item_size(addr) typ = None ret.append({ 'address': addr, 'name': name, 'type': typ, 'sz': sz, 'flags': flags, }) return ret
def color_head(ea): flags = ida_bytes.get_flags(ea) if not ida_bytes.is_code(flags): return mnem = ida_ua.print_insn_mnem(ea) if mnem == 'call': logger.debug('call: 0x%x', ea) idc.set_color(ea, idc.CIC_ITEM, CALL_COLOR) elif mnem == 'xor': if idc.get_operand_value(ea, 0) != idc.get_operand_value(ea, 1): logger.debug('non-zero xor: 0x%x', ea) idc.set_color(ea, idc.CIC_ITEM, ENCRYPT_COLOR) elif mnem in ('sdit', 'sgdt', 'sldt', 'smsw', 'str', 'in', 'cpuid'): logger.debug('anti-vm: 0x%x', ea) idc.set_color(ea, idc.CIC_ITEM, ANTIANALYSIS_COLOR) elif mnem == 'in': if idc.get_operand_value(ea, 0) in ("3", "2D"): logger.debug('anti-debug: 0x%x', ea) idc.set_color(ea, idc.CIC_ITEM, ANTIANALYSIS_COLOR) elif mnem in ('rdtsc', 'icebp'): logger.debug('anti-debug: 0x%x', ea) idc.set_color(ea, idc.CIC_ITEM, ANTIANALYSIS_COLOR)
def apply_signatures(self, start_addr: int, end_addr: int) -> None: total_objs = len(self._signatures) idaapi.msg('Applying obj symbols...\n') objs_list = dict() for sig in self._signatures: bytes_data = sig.get_sig() low_entropy = (not bytes_data.is_bios_call()) and (sig.get_entropy() < self.min_entropy) labels = sig.get_labels() search_addr = start_addr while search_addr < end_addr: addr, _ = masked_search(search_addr, end_addr, bytes_data.get_bytes(), bytes_data.get_masks()) if addr == idaapi.BADADDR: break if not sig.is_applied(): objs_list[sig.get_name()] = (addr, sig.get_entropy()) for lb in labels: lb_name = lb[0] lb_offs = lb[1] if lb_name == '': # removed label continue lb_addr = addr + lb_offs if ida_bytes.is_unknown(ida_bytes.get_flags(lb_addr) and not low_entropy and not (sig.is_applied() and self.only_first)): ida_auto.auto_make_code(lb_addr) is_func = not lb_name.startswith('loc_') new_name = '%s_' % sig.get_name().replace('.', '_') new_lb_name = lb_name.replace('text_', new_name).replace('loc_', new_name) new_lb_name = ('_%s' % new_lb_name) if ('0' <= new_lb_name[0] <= '9') else new_lb_name if not low_entropy and not (sig.is_applied() and self.only_first) and not self.has_non_default_name(lb_addr, new_lb_name): SigApplier.set_function(lb_addr, new_lb_name, is_func, False) idaapi.msg('Symbol %s at 0x%08X\n' % (new_lb_name, lb_addr)) else: prev_comment = ida_bytes.get_cmt(lb_addr, False) prev_comment = ('%s\n' % prev_comment) if prev_comment else '' new_comment = 'Possible %s/%s' % (sig.get_name(), new_lb_name) if prev_comment.find(new_comment) == -1: ida_bytes.set_cmt(lb_addr, '%s%s' % (prev_comment, new_comment), False) idaapi.msg('Possible symbol %s at 0x%08X\n' % (new_lb_name, lb_addr)) sig.set_applied(True) search_addr = addr + 4 idaapi.msg('Applied OBJs for %s: %d/%d:\n' % (self.short_lib_name, len(objs_list), total_objs)) for k, v in objs_list.items(): idaapi.msg('\t0x%08X: %s, %.02f entropy\n' % (v[0], k, v[1]))
def show_xrefs(ea, gco, xrefs, ndefs): title = "xrefs to %s at %08x" % (gco.name, ea) xc = xref_chooser_t(xrefs, title, ndefs, ea, gco) i = xc.Show(True) if i >= 0: ida_kernwin.jumpto(xrefs[i]) if ida_hexrays.init_hexrays_plugin(): ea = ida_kernwin.get_screen_ea() pfn = ida_funcs.get_func(ea) w = ida_kernwin.warning if pfn: F = ida_bytes.get_flags(ea) if ida_bytes.is_code(F): gco = ida_hexrays.gco_info_t() if ida_hexrays.get_current_operand(gco): # generate microcode hf = ida_hexrays.hexrays_failure_t() mbr = ida_hexrays.mba_ranges_t(pfn) mba = ida_hexrays.gen_microcode(mbr, hf, None, ida_hexrays.DECOMP_WARNINGS, ida_hexrays.MMAT_PREOPTIMIZED) if mba: merr = mba.build_graph() if merr == ida_hexrays.MERR_OK: ncalls = mba.analyze_calls(ida_hexrays.ACFL_GUESS) if ncalls < 0: print(
movs = ["mov", "xchg"] ida_api_is_new = False try: import ida_auto ida_api_is_new = True except ImportError: pass user_pos = ida_kernwin.get_screen_ea() after_function_before_user_pos = ida_funcs.get_prev_func(user_pos).end_ea + 1 before_function_after_user_pos = ida_funcs.get_next_func(user_pos).start_ea - 1 hopefully_a_sane_start_of_function = (after_function_before_user_pos & ~0x0F) + 0x10 make_fun_pos = None if ida_bytes.is_code(ida_bytes.get_flags(hopefully_a_sane_start_of_function)): make_fun_pos = hopefully_a_sane_start_of_function def re_analyze_relevant_range(): ida_bytes.del_items( after_function_before_user_pos, 0, before_function_after_user_pos - after_function_before_user_pos) if not make_fun_pos is None: assume_code_at(make_fun_pos) else: run_autoanalysis(after_function_before_user_pos, before_function_after_user_pos) def run_autoanalysis(start, end=None):
def CallStackWalk(nn): class Result: """ Class holding the result of one call stack item Each call stack item instance has the following attributes: caller = ea of caller displ = display string sp = stack pointer """ def __init__(self, caller, sp): self.caller = caller self.sp = sp f = ida_funcs.get_func(caller) self.displ = "%08x: " % caller if f: self.displ += ida_funcs.get_func_name(caller) t = caller - f.start_ea if t > 0: self.displ += "+" + hex(t) else: self.displ += hex(caller) self.displ += " [" + hex(sp) + "]" def __str__(self): return self.displ # get stack pointer sp = idautils.cpu.Esp seg = ida_segment.getseg(sp) if not seg: return (False, "Could not locate stack segment!") stack_seg = Seg(seg) word_size = 2 ** (seg.bitness + 1) callers = [] sp = idautils.cpu.Esp - word_size while sp < stack_seg.end_ea: sp += word_size ptr = next(idautils.GetDataList(sp, 1, word_size)) seg = ida_segment.getseg(ptr) # only accept executable segments if (not seg) or ((seg.perm & ida_segment.SEGPERM_EXEC) == 0): continue # try to find caller caller = IsPrevInsnCall(ptr) # we have no recognized caller, skip! if caller is None: continue # do we have a debug name that is near? if nn: ret = nn.find(caller) if ret: ea = ret[0] # function exists? f = ida_funcs.get_func(ea) if not f: # create function ida_funcs.add_func(ea) # get the flags f = ida_bytes.get_flags(caller) # no code there? if not ida_bytes.is_code(f): ida_ua.create_insn(caller) callers.append(Result(caller, sp)) # return (True, callers)
def load_file(fd, neflags, format): global prologues size = 0 base_addr = 0 ea = 0 nfunc = 0 idaapi.set_processor_type("arm", idaapi.SETPROC_LOADER) idaapi.get_inf_structure().lflags |= idaapi.LFLG_64BIT if (neflags & idaapi.NEF_RELOAD) != 0: return 1 fd.seek(0, idaapi.SEEK_END) size = fd.tell() segm = idaapi.segment_t() segm.bitness = 2 # 64-bit segm.start_ea = 0 segm.end_ea = size idaapi.add_segm_ex(segm, "iBoot", "CODE", idaapi.ADDSEG_OR_DIE) fd.seek(0) fd.file2base(0, 0, size, False) idaapi.add_entry(0, 0, "start", 1) idaapi.add_func(ea, idaapi.BADADDR) print("[+] Marked as code") # heuristic while (True): mnemonic = idaapi.ua_mnem(ea) if mnemonic == None: mnemonic = '' if "LDR" in mnemonic: base_str = idc.print_operand(ea, 1) base_addr = int(base_str.split("=")[1], 16) break ea += 4 print("[+] Rebasing to address 0x%x" % (base_addr)) idaapi.rebase_program(base_addr, idc.MSF_NOFIX) ida_auto.auto_wait() segment_start = base_addr segment_end = idc.get_segm_attr(segment_start, idc.SEGATTR_END) ea = segment_start print("[+] Searching and defining functions") for prologue in prologues: while ea != idc.BADADDR: ea = ida_search.find_binary(ea, idc.ida_search.SEARCH_DOWN, prologue, 16, ida_search.SEARCH_DOWN) if ea != idc.BADADDR: ea = ea - 2 if (ea % 4) == 0 and ida_bytes.get_flags(ea) < 0x200: print("[+] Defining a function at 0x%x" % (ea)) idaapi.add_func(ea, idaapi.BADADDR) nfunc = nfunc + 1 ea = ea + 4 ida_auto.plan_and_wait(segment_start, segment_end) print("[+] Identified %d new functions" % (nfunc)) print("[+] Looking for interesting functions") find_interesting(segment_start) return 1 # EOF
def is_str_literal(addr): return ida_bytes.is_strlit(ida_bytes.get_flags(addr))
def save_x(unique_name=None, start=None, size=None): ea = ida_kernwin.get_screen_ea() # signature if not unique_name: if not start: seg = ida_segment.getseg(ea) start = seg.start_ea sig_bytes = ida_bytes.get_bytes(start, SIGNATURE_SIZE) sig_hash = hashlib.md5(sig_bytes).hexdigest() unique_name = sig_hash if not start or not size: seg = ida_segment.getseg(ea) start = seg.start_ea size = seg.size() # (start_addr, end_addr, names, comms) saved_data = {} if MD5_hash_data_file and os.path.isfile(MD5_hash_data_file): with open(MD5_hash_data_file, "rb") as ifile: received_data = pickle.loads(ifile.read()) if received_data: saved_data = received_data # save names (func_names, labels, etc) # (addr, name, is_code) names_addr_name = [] names = idautils.Names() for addr, name in names: if start <= addr <= start + size: flags = ida_bytes.get_flags(addr) names_addr_name.append( (addr - start, name, ida_bytes.is_code(flags))) # save comments comms_addr_type_comm = [] # (addr, TYPE, comment) # type 0:comment 1:rpt_comment end = start + size for i in range(start, end + 1): if ida_bytes.get_cmt(i, 0): # 0 Comment comms_addr_type_comm.append((i - start, 0, ida_bytes.get_cmt(i, 0))) if ida_bytes.get_cmt(i, 1): # 1 RptCmt comms_addr_type_comm.append((i - start, 1, ida_bytes.get_cmt(i, 1))) # breakpoints bpts_addr_size_type = [] bpt = ida_dbg.bpt_t() global remove_on_exit_bpts for i in range(start, end + 1): if ida_dbg.get_bpt(i, bpt): bpts_addr_size_type.append((i - start, bpt.size, bpt.type)) remove_on_exit_bpts.append(i) # functions funcs_addr = [] flag = ida_bytes.get_flags(start) if ida_bytes.is_func(flag): funcs_addr.append(0) # start addr next_func = ida_funcs.get_next_func(start) while next_func: funcs_addr.append(next_func.start_ea - start) next_func = ida_funcs.get_next_func(next_func.start_ea) # SAVE saved_data[unique_name] = (start, start + end, names_addr_name, comms_addr_type_comm, bpts_addr_size_type, funcs_addr) if MD5_hash_data_file: with open(MD5_hash_data_file, "wb") as ifile: serial_data = pickle.dumps(saved_data) ifile.write(serial_data) print("dumpDyn::save:\n\ Name: {}\n\ Start address: {}".format(unique_name, hex(start)))
import idautils import ida_bytes import idaapi for ea in idautils.Heads(): if ida_bytes.is_code(ida_bytes.get_flags(ea)) and idaapi.is_call_insn(ea): idaapi.set_item_color(ea, 0xFFFFFFA0)
def _is_string(self, ea): head = get_item_head(ea) flags = get_flags(head) return is_strlit(flags)
def main(): global til til = ida_typeinf.get_idati() capstone_init() unicorn_init() all_names = list(idautils.Names()) # First, get inheritance tree for all classes # We do this in two passes: # First pass: for each class, gather class name, parent class name, # and superclass name, and connect them # Second pass: for each inheritance hierarchy object, we check # if the parent pointer is None (signaling a top-level # class), and add that to the `ihs` list # We can do this whole thing by processing xrefs to OSMetaClass::OSMetaClass OSMetaClass_ctor = get_OSMetaClass_ctor() if OSMetaClass_ctor == 0: print("Could not find OSMetaClass::OSMetaClass") return print("Got OSMetaClass::OSMetaClass at {}".format(hex(OSMetaClass_ctor))) # key,value pairs of all inheritance hierarchy objects ih_dict = {} # only those inheritance hierarchy objects which represent # a top-level parent class (aka inheriting from OSObject) ihs = [] xrefs = list(idautils.XrefsTo(OSMetaClass_ctor)) num = 0 for xref in xrefs: frm = xref.frm # test # frm = 0x63920 # print("xref from {}".format(hex(frm))) args = get_OSMetaClass_ctor_args(frm) pname = args[0] cname = args[1] if cname == "OSMetaClassBase": print("xref from {}".format(hex(frm))) print(args) if pname == cname: continue csz = args[2] # if pname == "AUAUnitDictionary" and cname == "AUAMixerUnitDictionary": # print(args) # return new_parent = pname is not None and pname not in ih_dict new_child = cname not in ih_dict if new_parent: ih_dict[pname] = InheritanceHierarchy(None, pname, csz, csz) if new_child: ih_dict[cname] = InheritanceHierarchy(None, cname, csz) else: # Update class size for only child classes ih_dict[cname].sz = csz if pname == None: # If this class has no superclass, it must be parent class, # so make its InheritanceHierarchy object ih_dict[cname] = InheritanceHierarchy(None, cname, csz) else: child_ih = ih_dict[cname] parent_ih = ih_dict[pname] parent_ih.add_child(child_ih) child_ih.parent = parent_ih child_ih.totsz = child_ih.sz + parent_ih.totsz # if cname == "AUAUnitDictionary": # print("AUAUnitDictionary sz: {}".format(ih_dict[pname].sz)) # print(args) # return # if cname == "AUAMixerUnitDictionary": # print("AUAMixerUnitDictionary sz: {}".format(ih_dict[cname].sz)) # print(args) # return num += 1 # if num == 10: # break print("First pass: {} classes processed".format(num)) num = 0 # Second pass for ih in ih_dict.values(): if ih.parent == None: # print("Adding {} to the ihs list".format(ih.name)) num += 1 ihs.append(ih) print("Second pass: {} classes added to ihs list".format(num)) num = 0 wants_class_hierarchy = ida_kernwin.ask_yn(0, "Dump class hierarchy?") if wants_class_hierarchy: hierch_file = open( "{}/iOS/Scripts/iokit_hier.txt".format(str(Path.home())), "w") for ih in ihs: dump_hierarchy_to_file(hierch_file, ih, 0) print("File written to {}".format(hierch_file.name)) hierch_file.close() return vtables = [] for cur_name in all_names: ea = cur_name[0] name = cur_name[1] if "ZTV" in name: vtables.append(cur_name) struct_file = open( "{}/iOS/Scripts/structs_from_vtabs.h".format(str(Path.home())), "w") is_standalone_kext = ida_kernwin.ask_yn(0, "Standalone kext?") if is_standalone_kext: # If this is from a standalone kext, I need to write some # definitions for common objects that follow my struct format, # otherwise, things get really screwed struct_file.write( "struct __cppobj ExpansionData {};\n\n" "struct __cppobj OSMetaClassBase_vtbl;\n\n" "struct __cppobj OSMetaClassBase_mbrs {};\n\n" "struct __cppobj OSMetaClassBase {\n" "\tOSMetaClassBase_vtbl *__vftable /*VFT*/;\n" "\tOSMetaClassBase_mbrs m;\n" "};\n\n" "struct __cppobj OSObject_mbrs : OSMetaClassBase_mbrs {\n" "\tint retainCount;\n" "};\n\n" "struct __cppobj OSObject_vtbl : OSMetaClassBase_vtbl {};\n\n" "struct __cppobj OSObject {\n" "\tOSObject_vtbl *__vftable;\n" "\tOSObject_mbrs m;\n" "};\n\n" "struct __cppobj OSMetaClass_mbrs : OSMetaClassBase_mbrs {\n" "\tExpansionData *reserved;\n" "\tconst OSMetaClass *superClassLink;\n" "\tconst OSSymbol *className;\n" "\tunsigned int classSize;\n" "\tunsigned int instanceCount;\n" "};\n\n" "struct __cppobj OSMetaClass {\n" "\tOSMetaClassBase_vtbl *__vftable;\n" "\tOSMetaClass_mbrs m;\n" "};\n\n" "struct __cppobj OSCollection_vtbl;\n" "struct __cppobj OSCollection_mbrs : OSObject_mbrs {\n" "\tunsigned int updateStamp;\n" "\tunsigned int fOptions;\n" "};\n\n" "struct __cppobj OSCollection {\n" "\tOSCollection_vtbl *__vftable;\n" "\tOSCollection_mbrs m;\n" "};\n\n" "struct __cppobj OSArray_vtbl;\n" "struct __cppobj OSArray_mbrs : OSCollection_mbrs {\n" "\tunsigned int count;\n" "\tunsigned int capacity;\n" "\tunsigned int capacityIncrement;\n" "\tvoid *array;\n" "};\n\n" "struct __cppobj OSArray {\n" "\tOSArray_vtbl *__vftable;\n" "\tOSArray_mbrs m;\n" "};\n\n" "struct __cppobj OSDictionary::dictEntry {\n" "\tconst OSSymbol *key;\n" "\tconst OSMetaClassBase *value;\n" "};\n\n" "struct __cppobj OSDictionary_vtbl;\n" "struct __cppobj OSDictionary_mbrs : OSCollection_mbrs {\n" "\tunsigned int count;\n" "\tunsigned int capacity;\n" "\tunsigned int capacityIncrement;\n" "\tOSDictionary::dictEntry *dict;\n" "};\n\n" "struct __cppobj OSDictionary {\n" "\tOSDictionary_vtbl *__vftable;\n" "\tOSDictionary_mbrs m;\n" "};\n\n" "struct __cppobj OSSet_vtbl;\n" "struct __cppobj OSSet_mbrs : OSCollection_mbrs {\n" "\tOSArray *members;\n" "};\n\n" "struct __cppobj OSSet {\n" "\tOSSet_vtbl *__vftable;\n" "\tOSSet_mbrs m;\n" "};\n\n" "struct __cppobj OSString_mbrs : OSObject_mbrs {\n" "\tunsigned __int32 flags : 14;\n" "\tunsigned __int32 length : 18;\n" "\tchar *string;\n" "};\n\n" "struct __cppobj OSString {\n" "\tOSObject_vtbl *__vftable;\n" "\tOSString_mbrs m;\n" "};\n\n" "struct __cppobj OSSymbol : OSString {};\n\n") num_failed_get_type = 0 cnt = 0 for vtable in vtables: demangled_name = idc.demangle_name(vtable[1], get_inf_attr(idc.INF_LONG_DN)) # unless this is a vtable for OSMetaClassBase, OSMetaClassMeta, # or OSMetaClass, skip anything metaclass related if "::MetaClass" in demangled_name: continue class_name = ida_name.extract_name(demangled_name, len("`vtable for'")) if class_name in BLACKLIST: continue ea = vtable[0] #+ 8; while ida_bytes.get_qword(ea) == 0: ea += 8 # print("EA: {}".format(hex(ea))) if is_unknown(ida_bytes.get_flags(ea)): continue # if class_name != "IOSkywalkPacket": # continue # if class_name != "AHTHSBufferStatic": # continue # if class_name != "HSMSPITest": # continue # if class_name != "AppleMesa": # continue # if class_name != "AppleUSBHostController": # continue # if class_name != "AppleEmbeddedPCIE": # continue # if class_name != "SimpleEval": # continue # if class_name != "AppleUSBCDCControl": # continue # if class_name != "IOHDCP2TransmitterAuthSession": # continue # if class_name != "IOAVService::DisplayIDParser::readDisplayID": # continue # if class_name != "IOMFB::TypedProp": # continue # if class_name != "IOMFB::UPBlock_GenPipe_v2": # continue # if class_name != "AppleMesaSEPDriver": # continue # if class_name != "IOAVController": # continue # if class_name != "AppleConvergedIPCICEBBBTIInterface": # continue # if class_name != "ApplePPM": # continue cnt += 1 # print("{}".format(class_name)) # skip NULL pointers until we hit a function while ida_bytes.get_qword(ea) == 0: ea += 8 num_virts = 0 num_dtors = 0 num_untyped = 0 num_noname = 0 fxn_name_list = [] fxn_name_dict = {} struct_fields = [] fwd_decls = set() fwd_decls.add(class_name) svt = SymbolicatedVtable(class_name) ioc = IOKitClass(svt) ioc.svt = svt # vtables seem to be NULL terminated while True: fxn_ea = ida_bytes.get_qword(ea) # end of vtable for this class if fxn_ea == 0: break # print("Type for {}/{} @ {}: {}".format(hex(fxn_ea), fxn_name, hex(ea), fxn_type)) # if fxn_type == None: # num_failed_get_type += 1 # ea += 8 # continue # default to this for ___cxa_pure_virtual fxn_args = "void" fxn_call_conv = "" fxn_mangled_name = ida_name.get_ea_name(fxn_ea) fxn_name = ida_name.demangle_name(fxn_mangled_name, get_inf_attr(idc.INF_LONG_DN)) if fxn_name == None: # ___cxa_pure_virtual fxn_name = ida_name.get_ea_name(fxn_ea) # some other thing? if len(fxn_name) == 0: fxn_name = "noname{}".format(num_noname) else: fxn_type = idc.get_type(fxn_ea) if fxn_type == None: # sometimes this happens, don't know why # the only thing fxn_type would have provided was # the calling convention, so we need to manually # reconstruct parameter list, and assume calling # convention is __fastcall # if mangled_fxn_name == "__ZN25IOGeneralMemoryDescriptor7doUnmapEP7_vm_mapyy": # exit(0) fxn_return_type = "__int64" fxn_call_conv = "__fastcall" fxn_args_string = fxn_name[fxn_name.find("(") + 1:fxn_name.rfind(")")] # if fxn_args_string == "IOService *, unsigned int, void *, void (*)(OSObject *, AppleUSBCDCControl*, void *, USBCDCNotification *)": # # print("Hello") # fxn_args_string = "IOService *, unsigned int, void *, void (*)(OSObject *, void (*)(TestType *, AppleUSBCDCControl*, void *, USBCDCNotification *), AppleUSBCDCControl*, void *, USBCDCNotification *)" # print("fxn args: {}".format(fxn_args_string)) # if fxn_args_string == "OSObject *, void (*)(OSObject *, IOHDCPAuthSession *), IOHDCPMessageTransport *, IOHDCPInterface *": # fxn_args_string = "OSObject *, void (*)(OSObject *, IOHDCPAuthSession *), IOHDCPMessageTransport *, IOHDCPInterface *, unsigned long long" fxn_args_string = fxn_args_string.replace( "{block_pointer}", "*") if fxn_args_string.find(",") != -1: # print("More than one arg: {}".format(fxn_args_list)) # extra comma makes the parser happy fxn_args_types_list = get_arg_type_list( fxn_args_string + ",") # print("More than one arg for {}: {}".format(fxn_name, fxn_args_types_list)) # print() fxn_args = "" argnum = 0 # print(type(fxn_args_types_list)) to_fwd_decl = get_fwd_decls(fxn_args_types_list) if len(to_fwd_decl) > 0: fwd_decls.update(to_fwd_decl) for arg_type in fxn_args_types_list: if argnum == 0: fxn_args += "{} *__hidden this, ".format( class_name) else: fxn_args += "{}, ".format(fix_type(arg_type)) argnum += 1 fxn_args = fxn_args[:-2] else: fxn_args = "{} *__hidden this".format(class_name) arg_type = fxn_name[fxn_name.find("(") + 1:fxn_name.rfind(")")] # print("Only one arg for {}: {}".format(fxn_name, arg_type)) arg_type_list = [arg_type] # print("Only one arg: {}".format(arg_type_list)) to_fwd_decl = get_fwd_decls(arg_type_list) if len(to_fwd_decl) > 0: fwd_decls.update(to_fwd_decl) if arg_type != "void" and len(arg_type) > 0: fxn_args += ", {}".format(fix_type(arg_type)) else: all_except_args = fxn_type[:fxn_type.find("(")] # first, if there's no spaces, there's no calling # convention specifed if all_except_args.find(" ") == -1: fxn_return_type = all_except_args # Also, this having no spaces could mean IDA messed # up, so we should use the demangled name instead # and parse that fxn_type = "(" + fxn_name[fxn_name.find("(") + 1:] # print("No spaces in args, using {} as fxn_type".format(fxn_type)) else: double_underscore = all_except_args.rfind("__") if double_underscore != -1: fxn_return_type = all_except_args[: double_underscore] fxn_call_conv = all_except_args[double_underscore:] else: fxn_return_type = all_except_args # get args # print("fxn_type: {}".format(fxn_type)) fxn_args = fxn_type[fxn_type.find("(") + 1:fxn_type.rfind(")")] fxn_args_type_list = get_arg_type_list(fxn_args + ",") fixed_fxn_args_type_list = [] # Fix up args for arg_type in fxn_args_type_list: # Remove __hidden arg_type = arg_type.replace("__hidden", "") # Check for a pointer. This is an easy case, we # just delete everything from the first * star = arg_type.find("*") if star != -1: arg_type = arg_type[0:star] else: # Otherwise, find the last space, and delete # from there # But in case there was no name for this # parameter, check if the token after the last # space is not an IDA type or primitive type lspace = arg_type.rfind(" ") if lspace != -1: token = arg_type[lspace:].replace(" ", "") if not is_primitive_type( token) and not is_ida_type(token): arg_type = arg_type[0:lspace] # print("arg_type: {}".format(arg_type)) fixed_fxn_args_type_list.append(arg_type) # to_fwd_decl = get_fwd_decls(fxn_args_type_list) to_fwd_decl = get_fwd_decls(fixed_fxn_args_type_list) if len(to_fwd_decl) > 0: fwd_decls.update(to_fwd_decl) # print("fxn_type is not None for {}: fxn args: {}".format(fxn_name, fxn_args_type_list)) # print("fxn_type is not None: will fwd declare: {}".format(to_fwd_decl)) # get function name # remove 'classname::' and params fxn_name = fxn_name[fxn_name.find("::") + 2:fxn_name.find("(") + 1] fxn_name = fxn_name[:fxn_name.find("(")] # replace any '~' fxn_name = fxn_name.replace("~", "DTOR{}_".format(num_dtors)) # remove any < and > fxn_name = fxn_name.replace("<", "") fxn_name = fxn_name.replace(">", "") if fxn_name in list(fxn_name_dict.keys()): fxn_name_dict[fxn_name] += 1 fxn_name += "_{}".format(fxn_name_dict[fxn_name]) else: fxn_name_dict[fxn_name] = -1 if "DTOR" in fxn_name: num_dtors += 1 curfield = "" if fxn_name == "___cxa_pure_virtual": # struct_fields.append("\tvoid __noreturn (__cdecl *___cxa_pure_virtual{})({});".format(num_virts, # fxn_args)) curfield = "\tvoid __noreturn (__cdecl *___cxa_pure_virtual{})({});".format( num_virts, fxn_args) num_virts += 1 else: # struct_fields.append("\t{} ({} *{})({});".format(fxn_return_type, # fxn_call_conv, fxn_name, fxn_args)) curfield = "\t{} ({} *{})({});".format(fxn_return_type, fxn_call_conv, fxn_name, fxn_args) svt.add(curfield) ea += 8 # return # Some classes won't have xrefs to OSMetaClass::OSMetaClass, # like OSMetaClassBase if class_name in ih_dict: ih_dict[class_name].ioc = ioc # Just write forward decls for now for decl in fwd_decls: struct_file.write("struct __cppobj {};\n".format(decl)) struct_file.write("\n") # cnt += 1 # if cnt == 5: # break print("{} IOKit vtables".format(len(vtables))) # Now create the header file to import into IDA # for ih in ihs: # dump_ih(ih, 0) generate_header_file(struct_file, ihs) # fixup_header_file(struct_file) struct_file.close()
def dump_heads(out): # out is a file like object, sys.stdout or an acual file object # There doesn't seem to a good way to determine what function a # particular address is in. Almost everyone recommends iterating # of the functions, and then getting the bytes out of each, but # that's not really what we want because it skips non function # bytes and reports them in the wrong order. It appears that the # best we can do is to build a map in advance. :-( a2fmap = build_a2fmap() min_ea = idaapi.cvar.inf.minEA max_ea = idaapi.cvar.inf.maxEA ea = min_ea while ea != ida_idaapi.BADADDR: ida_auto.show_addr(ea) isize = ida_bytes.get_item_size(ea) ibytes = ida_bytes.get_bytes(ea, isize) ihexbytes = binascii.b2a_hex(ibytes).upper() iflags = ida_bytes.get_flags(ea) # Skip the PE header? if not ida_bytes.is_head(iflags): ea = ida_bytes.next_head(ea, max_ea) continue # Loop up this address in the address-to-function map. if ea in a2fmap: faddrs = a2fmap[ea] else: faddrs = [ea] tcode = "ERROR" imnem = "???" iops = "???" if ida_bytes.is_code(iflags): tcode = "INSN" imnem = "???" iops = "???" insn = idautils.DecodeInstruction(ea) if insn == None: imnem = "BAD" iops = "" elif not insn.is_canon_insn(): imnem = "NCAN" iops = "" else: imnem = insn.get_canon_mnem() sops = [] for n in range(8): ostr = ida_ua.print_operand(ea, n) if ostr is not None: ostrnt = ida_lines.tag_remove(ostr) if ostrnt != '': sops.append(ostrnt) iops = ', '.join(sops) elif ida_bytes.is_data(iflags): tcode = "DATA" imnem = "db" iops = "???" if ida_bytes.is_align(iflags): tcode += "_ALIGN" #elif ida_bytes.is_struct(iflags): # tcode += "_STRUCT" #elif ida_bytes.is_char(iflags): # tcode += "_STR" # There are other types that IDA recognizes. elif ida_bytes.is_unknown(iflags): tcode = "UNK-%08X" % iflags imnem = "???" iops = "???" for faddr in sorted(faddrs): out.write('"PART",0x%08X,"%s",0x%08X,"%s","%s","%s"\n' % (ea, tcode, faddr, ihexbytes, imnem, iops)) ea = ida_bytes.next_head(ea, max_ea) print "Analysis complete!"