def main(): ida_auto.set_ida_state(ida_auto.st_Work) root = {} count = 0 sigcount = 0 sigattempts = 0 calc_func_segments() funcs = list(idautils.Functions(FUNCS_SEGSTART, FUNCS_SEGEND)) alltime = 0.0 avgtime = 0.0 f = ida_kernwin.ask_file(1, "*.yml", "Choose a file to save to") if not f: return skip = ida_kernwin.ask_yn(1, "Skip functions that start with \"sub_\"?") if skip == -1: return # Clean up and get rid of shitty funcs funccpy = funcs[:] for fea in funccpy: funcname = ida_funcs.get_func_name(fea) if funcname is None or funcname.startswith("nullsub"): funcs.remove(fea) continue if skip and funcname.startswith("sub"): funcs.remove(fea) continue flags = idc.get_func_attr(fea, FUNCATTR_FLAGS) if flags & ida_funcs.FUNC_LIB: funcs.remove(fea) continue funccount = len(funcs) for fea in funcs: starttime = time() func = idaapi.get_func(fea) funcname = ida_funcs.get_func_name(fea) if funcname != None: unmangled = idc.demangle_name(funcname, idc.get_inf_attr(idc.INF_SHORT_DN)) if unmangled is None: unmangled = funcname sig = makesig(func) sigattempts += 1 root[unmangled] = {"mangled": funcname, "signature": sig} if sig: sigcount += (0 if "!" in sig else 1) # Only ETA makesig() attempts, otherwise the timing is really off # Unfortunately, sigging takes progressively longer the further along the function list # this goes, as makesig() searches from up to down while functions are ordered from up to down # So this isn't really accurate but w/e multpct = 2.0 - count / float( funccount ) # Scale up a bit the lower we start at the get a halfass decent eta alltime += time() - starttime avgtime = alltime / sigattempts eta = int(avgtime * (funccount - count) * multpct) etastr = strftime("%H:%M:%S", gmtime(eta)) count += 1 update_window("Evaluated {} out of {} ({}%)\nETA: {}".format( count, funccount, floor(count / float(funccount) * 100.0 * 10.0) / 10.0, etastr)) while f.count(".yml") >= 2: f = f.replace(".yml", "", 1) if not f.endswith(".yml"): f += ".yml" with open(f, "w") as f: yaml.safe_dump(root, f, default_flow_style=False, width=999999) ida_kernwin.hide_wait_box() print("Successfully generated {} signatures from {} functions".format( sigcount, funccount)) ida_auto.set_ida_state(ida_auto.st_Ready)
def OnSelectLine(self, n): segname = self.items[n][0] seg = ida_segment.get_segm_by_name(segname + ":__text") filename = segname.replace(".", "_") + "_massbp" fp = open(filename + ".h", "w") guard = filename.upper() + "_H" fp.write("#ifndef %s\n" % guard) fp.write("#define %s\n" % guard) # Put the ID of the function inside the immediate of the BRK brk = 0xd4200000 fxnid = 0 fxns = list(idautils.Functions(seg.start_ea, seg.end_ea)) # orig instrs for sleh hook, indexed by function ID # one shot breakpoints fp.write("static uint32_t %s_orig_instrs[] = {\n" % filename) for fxnaddr in fxns: instr = int.from_bytes(idaapi.get_bytes(fxnaddr, 4, False), "little") fp.write(hex(instr) + ",\n") fp.write("};\n") fxnid = 0 fp.write("static const char *%s_fxn_names[] = {\n" % filename) for fxnaddr in fxns: fxnname = ida_funcs.get_func_name(fxnaddr) fxnname_dm = idc.demangle_name(fxnname, get_inf_attr(idc.INF_LONG_DN)) if fxnname_dm != None: fxnname = fxnname_dm fp.write("\"%s\",\n" % fxnname) fp.write("};\n") fxnid = 0 fp.write("static void %s(void){\n" % filename) for fxnaddr in fxns: brk &= 0xffe0001f brk |= (fxnid << 5) fxnaddrh = hex(fxnaddr) # print("Current function {} with ID {}, brk {}".format(fxnaddrh, fxnid, hex(brk))) fp.write("kwrite_instr({}+kernel_slide, {}); /* FUNCTION {} */\n". format(fxnaddrh, hex(brk), hex(fxnid))) fxnid += 1 fp.write("}\n") fxnid = 0 fp.write("static void %s(void){\n" % filename.replace("massbp", "undobp")) for fxnaddr in fxns: brk &= 0xffe0001f brk |= (fxnid << 5) fxnaddrh = hex(fxnaddr) # print("Current function {} with ID {}, brk {}".format(fxnaddrh, fxnid, hex(brk))) fp.write( "kwrite_instr({}+kernel_slide, {}_orig_instrs[{}]);\n".format( fxnaddrh, filename, hex(fxnid))) fxnid += 1 fp.write("}\n") fp.write("#endif\n") print("Wrote header file to %s" % os.getcwd() + "/" + fp.name) fp.close() return n
def _isFunctionMangled(ea: int) -> bool: name = get_func_name(ea) disable_mask = get_inf_attr(INF_SHORT_DN) if demangle_name(name, disable_mask) is None: return False return True
def kernelcache_find_virtual_method_overrides(classname=None, method=None): import idc import idaapi import ida_name import ida_kernelcache as kc # Define the form to ask for the arguments. class MyForm(idaapi.Form): def __init__(self): swidth = 40 idaapi.Form.__init__( self, r"""STARTITEM 0 Find virtual method overrides <#The class#Class :{classname}> <#The virtual method#Method:{method}>""", { 'classname': idaapi.Form.StringInput(tp=idaapi.Form.FT_IDENT, swidth=swidth), 'method': idaapi.Form.StringInput(tp=idaapi.Form.FT_IDENT, swidth=swidth), }) def OnFormChange(self, fid): return 1 kc.collect_class_info() if any(arg is None for arg in (classname, method)): f = MyForm() f.Compile() f.classname.value = classname or '' f.method.value = method or '' ok = f.Execute() if ok != 1: print('Cancelled') return False classname = f.classname.value method = f.method.value f.Free() if classname not in kc.class_info: print('Not a valid class: {}'.format(classname)) return False print('Subclasses of {} that override {}:'.format(classname, method)) baseinfo = kc.class_info[classname] found = False for classinfo in baseinfo.descendants(): for _, override, _ in kc.vtable.class_vtable_overrides( classinfo, superinfo=baseinfo, methods=True): name = idc.get_name( override, ida_name.GN_VISIBLE | idc.calc_gtn_flags(idc.BADADDR, override)) demangled = idc.demangle_name( name, idc.get_inf_attr(idc.INF_SHORT_DEMNAMES)) name = demangled if demangled else name if method in name: print('{:#x} {}'.format(override, classinfo.classname)) found = True if not found: print('No subclass of {} overrides {}'.format(classname, method)) return found
def demangle_func_name(mangled_name, clean=True): demangle_attr = idc.get_inf_attr(INF_SHORT_DN if clean else INF_LONG_DN) return idc.demangle_name(mangled_name, demangle_attr)
def prep_vtable(linuxtable, key, wintable, winv): if not linuxtable.get(key): return None funclist = linuxtable[key] # Compat for 2.7, strings are in unicode if version_info[0] < 3: funclist = [ i if isinstance(i, (int, long)) else str(i) for i in linuxtable[key] ] thunks, thunklist = get_thunks(winv, key, funclist) # We've got the thunks, now we don't need anything beyond another typeinfo instance = (int, long) if version_info[0] < 3 else int for i, v in enumerate(funclist): if isinstance(v, instance): funclist = funclist[:i] # Skipping thisoffs break # Get rid of extra destructor for linux for i, n in enumerate(funclist): name = idc.demangle_name(n, idc.get_inf_attr(idc.INF_SHORT_DN)) if name: if "::~" in name: del funclist[i] break # Windows does overloads backwards, reverse them # Also check for thunks while we're at it i = 0 funcoverloads = {} while i < len(funclist): # and i < len(wintable): n = funclist[i] if n.startswith("__cxa"): i += 1 continue # I shouldn't need to do this, but destructors are wonky if i == 0: demangled = idc.demangle_name(n, idc.get_inf_attr(idc.INF_SHORT_DN)) if demangled and "::~" in demangled: i += 1 continue overloadname = get_func_sname(n) shortname = get_func_postname(n) if not shortname: i += 1 continue # Windows skips the vtable function if it exists in the thunks and # the thunk does not jmp into it (because the thunk is the function) try: thunkidx = thunklist.index(shortname) delete = 1 except: thunkidx = -1 delete = 0 if i < len(wintable): if thunkidx != -1 and thunkidx < len(thunks): if not isinthunk(wintable[i], thunks[thunkidx]): currname = idc.get_name(thunks[thunkidx][0], ida_name.GN_VISIBLE) if currname and currname != funclist[ i] and EXPORT_MODE != Export_YesOnly: nameflags = ida_name.SN_FORCE if not currname.startswith("sub_"): if not USE_WEAK_NAMES: del funclist[i] continue nameflags |= ida_name.SN_WEAK elif USE_WEAK_NAMES: global FUNCS FUNCS += 1 idc.set_name(thunks[thunkidx][0], funclist[i], nameflags) del funclist[i] continue else: # Class has thunks at the end of the vtable # This doesn't change anything but it should link up the lengths of both tables if delete: del funclist[i] continue node = funcoverloads.get(overloadname, []) # Is this a half-ass decent overload go = 1 for loadnode in range(len(node)): if not any( [i - funclist.index(val) > F**K for val in node[loadnode]]): node[loadnode].append(n) go = 0 break if go: node.append([n]) funcoverloads[overloadname] = node i += 1 for k, value in get_bcompat_items(funcoverloads): # if len(value) <= 1: # continue # split = [] # # # Since subclass overloads shouldn't scoot up next to their baseclass brethren # # hackily separate overloads by classname # for mname in value: # found = 0 # # name = idc.demangle_name(mname, idc.get_inf_attr(idc.INF_SHORT_DN)) # typename = name[:name.find("::")] # # for i2 in range(len(split)): # for othermname in split[i2]: # name = idc.demangle_name(othermname, idc.get_inf_attr(idc.INF_SHORT_DN)) # othertypename = name[:name.find("::")] # # if typename == othertypename: # found = 1 # split[i2].append(mname) # break # # if found: # break # # if not found: # split.append([mname]) for v in value: if len(v) <= 1: continue lowestidx = len(funclist) for func in v: temp = funclist.index(func) if lowestidx > temp: lowestidx = temp count = 0 while len(v): k = v.pop() funclist.insert(lowestidx + count, funclist.pop(funclist.index(k))) count += 1 diff = len(funclist) - len(wintable) if diff: print("WARNING: {} vtable may be wrong! L{} - W{} = {}".format( key, len(funclist), len(wintable), diff)) return funclist
def lvar_type_changed(self, vu, v, tif): if (vu.cfunc): func_tif = ida_typeinf.tinfo_t() vu.cfunc.get_func_type(func_tif) funcdata = idaapi.func_type_data_t() got_data = func_tif.get_func_details(funcdata) if (not got_data): # self._log("Didnt get the data") pass lvars = vu.cfunc.get_lvars() for j in range(len(vu.cfunc.argidx)): # for i in vu.cfunc.argidx: i = vu.cfunc.argidx[j] if (lvars[i].name == v.name): #self._log("lvar_type_changed: function argument changed = %s, index = %s, atype = %s" % (lvars[i].name, i, funcdata[j].argloc.atype())) if (funcdata[i].argloc.atype() == 3): # self._log("lvar_type_changed: reg is : %s" %(funcdata[i].argloc.reg1())) pass if (funcdata[i].argloc.atype() != 3 or funcdata[i].argloc.reg1() != RCX_REG): break #self._log("applyName = %s" % (applyName)) firstPtrRemove = ida_typeinf.remove_pointer(tif) #self._log("type name = %s" % (firstPtrRemove._print())) #self._log("remove_pointer.is_ptr = %s" % (firstPtrRemove.is_ptr())) #self._log("remove_pointer.is_struct = %s" % (firstPtrRemove.is_struct())) if (firstPtrRemove.is_struct() and not firstPtrRemove.is_ptr()): currentFuncName = ida_name.get_ea_name( vu.cfunc.entry_ea) # self._log("before demangle current func name = %s" % (currentFuncName)) demangled = idc.demangle_name( currentFuncName, idc.get_inf_attr(idc.INF_SHORT_DN)) if (demangled != None): self._log("Overriding mangled name = %s" % (currentFuncName)) currentFuncName = demangled # self._log("after demangle current func name = %s" % (currentFuncName)) tokens = currentFuncName.split("::") if len(tokens) > 1: currentFuncName = tokens[1] currentFuncName = currentFuncName.split("(")[0] # self._log("current func name = %s" % (currentFuncName)) idc.set_name( vu.cfunc.entry_ea, firstPtrRemove._print() + "::" + currentFuncName, idc.SN_NOWARN) idaapi.auto_wait() # self._log("Decomp Res : %s" % idaapi.decompile(vu.cfunc.entry_ea)) idaapi.refresh_idaview_anyway() vu.refresh_ctext() idaapi.refresh_idaview_anyway() vu.refresh_ctext() vu.refresh_view(True) current_widget = idaapi.get_current_widget() vu1 = idaapi.get_widget_vdui(current_widget) if vu1: vu1.refresh_ctext() break #self._log("lvar_type_changed: vu=%s, v=%s, tinfo=%s" % (vu, self._format_lvar(v), tif._print())) return 1
def name(self): name = idaapi.get_name(self.address) if idaapi.is_valid_typename(name): return name name = idc.demangle_name(name, idc.get_inf_attr(idc.INF_SHORT_DN)) return common.demangled_name_to_c_str(name)
def FilterConstructors(old_identifier_constructors_dict, complete_vft_dict): identifier_constructors_dict = old_identifier_constructors_dict.copy() bad_functions = [] # Prepare bad function list for ea in idautils.Segments(): for funcaddress in idautils.Functions(idc.SegStart(ea), idc.SegEnd(ea)): f_name = idc.GetFunctionName(funcaddress) f_name_demangled = idc.demangle_name( f_name, idc.GetLongPrm(idc.INF_SHORT_DN)) if f_name_demangled != None: f_name = f_name_demangled if "(" in f_name: tmp_name = f_name[:f_name.find("(")] else: tmp_name = f_name if "free" in tmp_name.lower(): bad_functions.append(funcaddress) elif "operator delete" in tmp_name.lower(): bad_functions.append(funcaddress) # Build virtual method list virtual_methods = [] for vft_infos in complete_vft_dict.values(): for vft_info in vft_infos: virtual_methods += vft_info[2] changed = True while changed: changed = False for class_identifier, constructors in identifier_constructors_dict.items( ): new_constructors = [] for constructor in constructors: # Check virtualized function. Which means it is destructor if constructor.func_ea in virtual_methods: bad_functions.append(constructor.func_ea) changed = True continue # Count references of bad functions ref_count = 0 badref_count = 0 f = idaapi.get_func(constructor.func_ea) fc = idaapi.FlowChart(f, None, 0x4) for bbl in fc: cur_ea = bbl.startEA while cur_ea < bbl.endEA: for ref in idautils.XrefsFrom(cur_ea): if f.startEA <= ref.to and ref.to < f.endEA: continue ref_count += 1 if ref.to in bad_functions: badref_count += 1 cur_ea = idc.NextHead(cur_ea) bad_percent = (badref_count * 100.0) / ref_count if 50.0 <= bad_percent: bad_functions.append(constructor.func_ea) changed = True continue # Okay. It is not destructor! new_constructors.append(constructor) identifier_constructors_dict[class_identifier] = new_constructors return identifier_constructors_dict