def import_objects(objs, add_names=True, always_thumb=True): """ Create IDA function and data according to symbol definitions. Create name if add_name is True :param add_names: Create name for symbols? """ failed_objs = [] for name, size, addr, _ in objs: if not is_data_start(addr): if size: ok = idaapi.create_data(addr, idc.FF_BYTE, size, 0) else: ok = idc.create_byte(addr) if not ok: reason = "Could not create data at {addr:#x}".format(addr=addr) data = (name, size, addr) failed_objs.append((data, reason)) """continue""" idc.set_cmt(addr, "obj.%s" % name, 0) if add_names: ok = idc.set_name(addr, name, idc.SN_CHECK) if not ok: reason = "Could not add name {name} at {addr:#x}".format( name=name, addr=addr) data = (name, size, addr) failed_objs.append((data, reason)) print("waiting for ida to finish analysis") ida_auto.auto_wait() print("ida finished import") return failed_objs
def __init__(self, arch, os): # Wait until IDA has finished analysis before we proceed, otherwise # we will end up missing code, data and cross references ida_auto.auto_wait() super(IDAProgram, self).__init__(arch, os) self._init_ctrl_flow_redirections()
def handle_dll_calls(self): dll_call_count = ida_bytes.get_dword(self.E_info_entry_ea + self.dll_call_count_offset) dll_call_lib_names_ea = ida_bytes.get_dword( self.E_info_entry_ea + self.dll_call_lib_names_offset) dll_call_func_names_ea = ida_bytes.get_dword( self.E_info_entry_ea + self.dll_call_func_names_offset) self.dll_calls = Dll_calls(dll_call_lib_names_ea, dll_call_func_names_ea, dll_call_count) ida_auto.auto_wait() ea = ida_name.get_name_ea(idaapi.BADADDR, "j__krnl_MCallDllCmd") code_fref_eas = [] fref_ea = ida_xref.get_first_fcref_to(ea) while fref_ea != idaapi.BADADDR: code_fref_eas.append(fref_ea) fref_ea = ida_xref.get_next_cref_to(ea, fref_ea) for ref_ea in code_fref_eas: #get prev mov instruction prev_ins_ea = idaapi.get_item_head(ref_ea - 1) ins = ida_lines.generate_disasm_line(prev_ins_ea, ida_lines.GENDSM_REMOVE_TAGS) if (ins.startswith("mov eax,")): index = ida_bytes.get_dword(prev_ins_ea + 1) cmt = self.dll_calls[index] ida_bytes.set_cmt(ref_ea, cmt, False)
def wait_auto_analysis(): import ida_auto try: # >= IDA Pro 7.4 ida_auto.auto_wait() except AttributeError: # < IDA Pro 7.4 ida_auto.autoWait()
def run_scatterload(debug=False): # Newly identified region may have additional scatter load procedure. Thus, # we continuously proceed until no changes left. is_changed = True while is_changed: is_changed = False tables = find_scatter_table() scatter_funcs = find_scatter_funcs() for start, end in tables.items(): print("Processing table: 0x%x to 0x%x" % (start, end)) while start < end: ida_bytes.create_dword(start, 16) ida_offset.op_offset(start, 0, idc.REF_OFF32) src = ida_bytes.get_dword(start) dst = ida_bytes.get_dword(start + 4) size = ida_bytes.get_dword(start + 8) how = ida_bytes.get_dword(start + 12) if how not in scatter_funcs: print("%x: no addr 0x%x in scatter_funcs" % (start, how)) start += 16 continue func_name = scatter_funcs[how] start += 16 print("%s: 0x%x -> 0x%x (0x%x bytes)" % (func_name, src, dst, size)) if func_name != "__scatterload_zeroinit": if not idc.is_loaded(src) or size == 0: print("0x%x is not loaded." % (src)) continue if debug: # only show information above continue if func_name == "__scatterload_copy": if add_segment(dst, size, "CODE"): memcpy(src, dst, size) is_changed = True elif func_name == "__scatterload_decompress": if add_segment(dst, size, "DATA"): decomp(src, dst, size) is_changed = True # some old firmware images have this. elif func_name == "__scatterload_decompress2": if add_segment(dst, size, "DATA"): decomp2(src, dst, size) is_changed = True elif func_name == "__scatterload_zeroinit": # No need to further proceed for zero init. if add_segment(dst, size, "DATA"): memclr(dst, size) ida_auto.auto_wait()
def set_type_single(address, type_): if isinstance(type_, fa_types.FaStruct) or \ isinstance(type_, fa_types.FaEnum): type_str = type_.get_name() else: type_str = type_ idc.SetType(address, type_str) ida_auto.auto_wait()
def command_autoanalysis(action): if action == "wait": ida_auto.auto_wait() return ( 'autoanalysis', 'done', ) else: raise RuntimeError("Invalid action received for command: " "{}".format(action))
def clean(): """Removes the background color of all the database. It can be used to remove the colors added by `apply()`, but it doesn't remove the prefixes.""" ida_auto.auto_wait() ea = idc.next_head(0) while ea != idaapi.BADADDR: idc.set_color(ea, idc.CIC_ITEM, 0xFFFFFF) ea = idc.next_head(ea) print("ANA color: Enjoy your boring database")
def clear_names(): """for some reason deleting names seems to need several takes. This will loop until all names are deleted. """ names = list(idautils.Names()) while names: for name_ea, _ in names: idc.set_name(name_ea, "") # deletes name ida_auto.auto_wait() names = list(idautils.Names())
def main(): print("Waiting for autoanalysis...") ida_auto.auto_wait() if init_hexrays(): eqty = ida_entry.get_entry_qty() if eqty: idbpath = idc.get_idb_path() cpath = idbpath[:-4] + ".c" with open(cpath, "w") as outfile: print("writing results to '%s'..." % cpath) for i in range(eqty): ea = ida_entry.get_entry(ida_entry.get_entry_ordinal(i)) decompile_func(ea, outfile) else: print("No known entrypoint. Cannot decompile.") if ida_kernwin.cvar.batch: print("All done, exiting.") ida_pro.qexit(0)
def update_idb(self): sid = ida_struct.get_struc_id(self._name) if sid != -1: sptr = ida_struct.get_struc(sid) ida_struct.del_struc(sptr) sid = ida_struct.add_struc(idc.BADADDR, self._name, 0) sptr = ida_struct.get_struc(sid) for f in self._fields: ida_struct.add_struc_member(sptr, f.name, idc.BADADDR, (idc.FF_BYTE | idc.FF_DATA) & 0xFFFFFFFF, None, 1) member_name = "{}.{}".format(self._name, f.name) idc.SetType( idaapi.get_member_by_fullname(member_name)[0].id, f.type) ida_auto.auto_wait()
def update_idb(self, delete_existing_members=True): sid = ida_struct.get_struc_id(self._name) sptr = ida_struct.get_struc(sid) if sid == idc.BADADDR: sid = ida_struct.add_struc(idc.BADADDR, self._name, 0) sptr = ida_struct.get_struc(sid) else: if delete_existing_members: ida_struct.del_struc_members(sptr, 0, 0xffffffff) for f in self._fields: ida_struct.add_struc_member(sptr, f.name, f.offset, (idc.FF_BYTE | idc.FF_DATA) & 0xFFFFFFFF, None, 1) member_name = "{}.{}".format(self._name, f.name) idc.SetType( idaapi.get_member_by_fullname(member_name)[0].id, f.type) ida_auto.auto_wait()
def run(self, args): print "[+] Welcome to Driver Buddy" auto_wait() # Wait for IDA autoanalysis to complete driver_entry = data.is_driver() if driver_entry == "": print "[-] No DriverEntry stub found" print "[-] Exiting..." return print "[+] DriverEntry found" if data.populate_data_structures() == False: print "[-] Unable to load functions" print "[-] Exiting..." return driver_type = data.get_driver_id(driver_entry) if driver_type == "": print "[-] Unable to determine driver type assuming wdm" else: print "[+] Driver type detected: " + driver_type if ioctl.find_ioctls() == False: print "[-] Unable to automatically find any IOCTLs" return
def fix_func_prolog(ea, end_ea=idc.BADADDR): global FUNC_BY_LS func_cnt = 0 func = ida_funcs.get_fchunk(ea) if func is None: func = ida_funcs.get_next_func(ea) ea = func.start_ea while func is not None and ea < end_ea: # if current function is small enough and there exists a function right # next to current function if (func.size() <= 8 and idc.get_func_attr( func.end_ea, idc.FUNCATTR_START) != idc.BADADDR): # If the next function can be connected, there must be a basic block reference. # xref.type == 21 means 'fl_F', which is an ordinary flow. if all( (func.start_ea <= xref.frm < func.end_ea) and xref.type == 21 for xref in XrefsTo(func.end_ea)): if func_cnt > 0 and func_cnt % 1000 == 0: print("%x <- %x: prolog merging (%d)." % (func.start_ea, func.end_ea, func_cnt)) ida_bytes.del_items(func.end_ea, ida_bytes.DELIT_EXPAND) ida_bytes.del_items(func.start_ea, ida_bytes.DELIT_EXPAND) ida_auto.auto_wait() status = idc.add_func(func.start_ea) if not status: print("Error merging 0x%x <- 0x%x" % (func.start_ea, func.end_ea)) else: func_cnt += 1 FUNC_BY_LS.discard(func.end_ea) ida_auto.auto_wait() func = ida_funcs.get_next_func(ea) if func: ea = func.start_ea print("Fixed %d functions" % func_cnt)
def apply(): """Provides an `apply()` method to color and mark your database and a `clean()` method to undo it. The `apply()` method colors `call`, `push` and `pop` instructions (sets background color). It also adds the prefix `>>` to`call` instructions and the number of argument to its parameters (only available if the function declaration is defined). This is useful to quickly identify function calls, their parameters and the calling convention.""" ida_auto.auto_wait() ea = idc.next_head(0) while ea != idaapi.BADADDR: instruction = idc.print_insn_mnem(ea) if instruction == "call": idc.set_color(ea, idc.CIC_ITEM, COLOR_CALL) addresses = idaapi.get_arg_addrs(ea) if addresses: _PARAMETERS.update({e: i + 1 for i, e in enumerate(addresses)}) elif instruction == "push": idc.set_color(ea, idc.CIC_ITEM, COLOR_PUSH) elif instruction == "pop": idc.set_color(ea, idc.CIC_ITEM, COLOR_POP) ea = idc.next_head(ea) ida_lines.set_user_defined_prefix(ALL_PREFIX_WIDTH, _prefix_callback) print("ANA color: Enjoy your colorful database!")
def __init__(self, arch: Arch, os: OS, maybe_base_address: Optional[int] = None): if maybe_base_address is not None: delta = abs(ida_nalt.get_imagebase() - maybe_base_address) if maybe_base_address < ida_nalt.get_imagebase(): delta = delta * -1 if ida_segment.rebase_program(delta, ida_segment.MSF_FIXONCE) != 0: raise RuntimeError("Failed to rebase the program") # Wait until IDA has finished analysis before we proceed, otherwise # we will end up missing code, data and cross references ida_auto.auto_wait() super(IDASpecification, self).__init__(arch, os) try: self._init_func_thunk_ctrl_flow() except: DEBUG( "Failed to initialize the control flow information for functin thunks" )
funcs = get_funcs() text_segm = ida_segment.get_segm_by_name(".text") for func in funcs: # we only care about functions in .text segment if not in_range(text_segm, func.start_ea): continue name = ida_funcs.get_func_name(func.start_ea) code = ida_hexrays.decompile_func(func, None) results[name] = { "name": name, "start_ea": func.start_ea, "end_ea": func.end_ea, "code": code.__str__(), "size": func.size() } print("JSON:") print(json.dumps({"time": time.time() - start, "data": results})) # wait for the analysis to complete ida_auto.auto_wait() # actually do stuff print(BEGIN_BANNER) main() print(END_BANNER) # quit to stop GUI from poping up ida_pro.qexit(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 reset_db(): idc.del_items(min_ea, size=max_ea - min_ea) idc.delete_all_segments() clear_names() ida_auto.auto_wait()
def ida_wait(): ida_auto.auto_wait()
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 autoanalyze(): ida_auto.auto_wait()
once that is done, produce a .c file containing the decompilation of all the functions in that file. Run like so: ida -A "-S...path/to/produce_c_file.py" <binary-file> where: * -A instructs IDA to run in non-interactive mode * -S holds a path to the script to run (note this is a single token; there is no space between '-S' and its path.) """ import ida_pro import ida_auto import ida_loader import ida_hexrays # derive output file name idb_path = ida_loader.get_path(ida_loader.PATH_TYPE_IDB) c_path = "%s.c" % idb_path ida_auto.auto_wait() # wait for end of auto-analysis ida_hexrays.decompile_many( # generate .c file c_path, None, ida_hexrays.VDRUN_NEWFILE | ida_hexrays.VDRUN_SILENT | ida_hexrays.VDRUN_MAYSTOP) ida_pro.qexit(0)
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
# # This example tries to load a decompiler plugin corresponding to the current # architecture (and address size) right after auto-analysis is performed, # and then tries to decompile the function at the first entrypoint. # # It is particularly suited for use with the '-S' flag. # import ida_ida import ida_auto import ida_loader import ida_hexrays import ida_idp import ida_entry ida_auto.auto_wait() ALL_DECOMPILERS = { ida_idp.PLFM_386 : ("hexrays", "hexx64"), ida_idp.PLFM_ARM : ("hexarm", "hexarm64"), ida_idp.PLFM_PPC : ("hexppc", "hexppc64"), } pair = ALL_DECOMPILERS.get(ida_idp.ph.id, None) if pair: decompiler = pair[1 if ida_ida.cvar.inf.is_64bit() else 0] if ida_loader.load_plugin(decompiler) and ida_hexrays.init_hexrays_plugin(): eqty = ida_entry.get_entry_qty() if eqty: ea = ida_entry.get_entry(ida_entry.get_entry_ordinal(0)) print("Decompiling at: %X" % ea) cf = ida_hexrays.decompile(ea) if cf: