def make_basic_structs(): strucid = ida_struct.get_struc_id("Vector") if strucid == idc.BADADDR: struc = ida_struct.get_struc( ida_struct.add_struc(idc.BADADDR, "Vector")) ida_struct.add_struc_member(struc, "x", idc.BADADDR, idc.FF_FLOAT, None, 4) ida_struct.add_struc_member(struc, "y", idc.BADADDR, idc.FF_FLOAT, None, 4) ida_struct.add_struc_member(struc, "z", idc.BADADDR, idc.FF_FLOAT, None, 4) global VECTOR VECTOR = idaapi.tinfo_t() idaapi.parse_decl(VECTOR, None, "Vector;", 0) strucid = ida_struct.get_struc_id("QAngle") if strucid == idc.BADADDR: struc = ida_struct.get_struc( ida_struct.add_struc(idc.BADADDR, "QAngle")) ida_struct.add_struc_member(struc, "x", idc.BADADDR, idc.FF_FLOAT, None, 4) ida_struct.add_struc_member(struc, "y", idc.BADADDR, idc.FF_FLOAT, None, 4) ida_struct.add_struc_member(struc, "z", idc.BADADDR, idc.FF_FLOAT, None, 4)
def import_vtable(classname, struc): ea = get_vtable(classname) if ea == idc.BADADDR: return # Mildly adapted from Asherkin's vtable dumper ea = ea + 8 # Skip typeinfo and thisoffs funcs = [] while ea != idc.BADADDR: offs = idc.get_wide_dword(ea) if not ida_bytes.is_code(ida_bytes.get_full_flags(offs)): break name = idc.get_name(offs, ida_name.GN_VISIBLE) funcs.append(name) ea = ida_bytes.next_not_tail(ea) # print(funcs) if not len(funcs): return strucid = add_struc_ex(classname + "_vtbl") vstruc = ida_struct.get_struc(strucid) for i in funcs: # Gotta do a fancy demangle, it can't have special chars # and there can't be multiples of the same name, so let's just jazz around all of that demangled = idc.demangle_name(i, idc.get_inf_attr(idc.INF_SHORT_DN)) if demangled == None: demangled = i else: demangled = demangled[demangled.find("::") + 2:demangled.find("(")] demangled = demangled.replace("~", "_").replace("<", "_").replace(">", "_") while 1: error = ida_struct.add_struc_member(vstruc, demangled, idc.BADADDR, idc.FF_DWORD, None, 4) if error == 0: break demangled += "_{}".format( hex(ida_struct.get_struc_last_offset(vstruc) * 4 + 4)[2:]) # Now assign the vtable to the actual struct ti = idaapi.tinfo_t() idaapi.parse_decl(ti, None, classname + "_vtbl;", 0) ti.create_ptr(ti) ida_struct.set_member_tinfo(struc, ida_struct.get_member(struc, 0), 0, ti, 0)
def import_vtable(typename, funcs): typestrucid = add_struc_ex(typename) typestruc = ida_struct.get_struc(typestrucid) vstrucid = add_struc_ex(typename + "_vtbl") vstruc = ida_struct.get_struc(vstrucid) loffs = ida_struct.get_struc_last_offset(vstruc) if loffs != idc.BADADDR: ida_struct.del_struc_members(vstruc, 0, loffs + 4) for i in funcs: demangled = idc.demangle_name(i, idc.get_inf_attr(idc.INF_SHORT_DN)) if demangled == None: demangled = i else: demangled = demangled[demangled.find("::")+2:demangled.find("(")] # As per https://stackoverflow.com/questions/3411771/best-way-to-replace-multiple-characters-in-a-string # this isn't as slow as you'd think demangled = demangled\ .replace("~", "_")\ .replace("<", "_")\ .replace(">", "_")\ .replace(",", "_")\ .replace("*", "_")\ .replace(" ", "_")\ .replace("operator==", "__eq__")\ .replace("operator+", "__add__")\ .replace("operator-", "__sub__")\ .replace("operator*", "__mul__")\ .replace("operator/", "__div__")\ .replace("operator%", "__mod__")\ .replace("operator<<", "__lshift__")\ .replace("operator>>", "__rshift__")\ .replace("operator&", "__and__")\ .replace("operator|", "__or__")\ .replace("operator^", "__xor__")\ .replace("operator~", "__invert__") while 1: error = ida_struct.add_struc_member(vstruc, demangled, idc.BADADDR, idc.FF_DWORD, None, 4) if error == 0: break demangled += "_{}".format(hex(ida_struct.get_struc_last_offset(vstruc) + 4)[2:]) try: ti = idaapi.tinfo_t() idaapi.parse_decl(ti, None, typename + "_vtbl;", 0) ti.create_ptr(ti) ida_struct.add_struc_member(typestruc, "__vftable", 0, idc.FF_DWORD, None, 4) ida_struct.set_member_tinfo(typestruc, ida_struct.get_member(typestruc, 0), 0, ti, 0) except: print("Prevented a terrible, horrible, no good, very bad crash with {}!".format(typename))
def parse(info): '''Parse the string `info` into an ``idaapi.tinfo_t``.''' if idaapi.__version__ < 7.0: til, ti = idaapi.cvar.idati, idaapi.tinfo_t(), else: til, ti = idaapi.get_idati(), idaapi.tinfo_t(), # Convert info to a string if it's a tinfo_t info_s = "{!s}".format(info) if isinstance(info, idaapi.tinfo_t) else info # Firstly we need to ';'-terminate the type the user provided in order # for IDA's parser to understand it. terminated = info_s if info_s.endswith(';') else "{:s};".format(info_s) # Ask IDA to parse this into a tinfo_t for us. We pass the silent flag so # that we're responsible for raising an exception if there's a parsing # error of some sort. If it succeeds, then we can return our typeinfo. # Otherwise we return None because of the inability to parse it. if idaapi.__version__ < 6.9: return None if idaapi.parse_decl2(til, terminated, None, ti, idaapi.PT_SIL) is None else ti elif idaapi.__version__ < 7.0: return None if idaapi.parse_decl2(til, terminated, ti, idaapi.PT_SIL) is None else ti return None if idaapi.parse_decl(ti, til, terminated, idaapi.PT_SIL) is None else ti
def getUserDeclType(self, decl): tinfo = idaapi.tinfo_t() #logger.debug('Trying to parse declaration: %r', decl) ret = idaapi.parse_decl(tinfo, idaapi.cvar.idati, decl, idaapi.PT_TYP) #logger.debug('Return from parse_decl2: %r', ret) if ret is None: logger.info('parse_decl failed') return None return tinfo
def setData(self, column, value): if column == 0: if idaapi.is_ident(value) and self.name != value: self.name = value self.name_modified = True for parent in self.parents: parent.modified = True return True elif column == 1: tinfo = idaapi.tinfo_t() split = value.split('(') if len(split) == 2: value = split[0] + ' ' + self.name + '(' + split[1] + ';' if idaapi.parse_decl(tinfo, idaapi.cvar.idati, value, idaapi.PT_TYP) is not None: if tinfo.is_func(): tinfo.create_ptr(tinfo) if tinfo.dstr() != self.tinfo.dstr(): self.tinfo = tinfo self.tinfo_modified = True for parent in self.parents: parent.modified = True return True return False
def parse(c, struc): if c.tag == "sendtable": if c.attrib.get("name", None) and c.attrib.get("name", None).startswith("DT_"): for i in c: parse(i, struc) elif c.tag == "property": classname = c.attrib.get("name", None) if classname != None: if classname == "baseclass": for p in c: parse(p, struc) else: t = c.find("type") if t == None: return offset = c.find("offset") offset = int(offset.text) if offset != None else None if offset == None or offset == 0: return # Have to be a little special with datatables if t.text == "datatable": ida_struct.add_struc_member(struc, classname, offset, idc.FF_DWORD, None, 4) sendtable = c.find("sendtable") if sendtable != None: mycls = sendtable.attrib.get("name", None) if mycls != None: if mycls.startswith("DT_"): mycls = mycls.replace("DT_", "C", 1) strucid = ida_struct.get_struc_id(mycls) if strucid == idc.BADADDR: # If this struct didn't exist, parse it strucid = ida_struct.add_struc( idc.BADADDR, mycls) parse(sendtable, ida_struct.get_struc(strucid)) ti = idaapi.tinfo_t( ) # Assign the sendtable type to the struct idaapi.parse_decl(ti, None, mycls + ";", 0) if str( ti ) != "CAttributeList": # HACK; this one doesn't work and idk what else to try ida_struct.set_member_tinfo( struc, ida_struct.get_member(struc, offset), 0, ti, 0) else: # Iterate the array and update the struct member size, hackily flag, sizemult = get_sendtable_size(sendtable) if sizemult > 4: ida_struct.set_member_type( struc, offset, flag, None, sizemult) return sz = c.find("bits") sz = int(sz.text) if sz != None else None if sz == None: return flags, numbytes = calcszdata(sz) if t.text == "float": flags = idc.FF_FLOAT numbytes = 4 if t.text == "vector": ida_struct.add_struc_member(struc, classname, offset, idc.FF_DWORD, None, 12) global VECTOR ida_struct.set_member_tinfo( struc, ida_struct.get_member(struc, offset), 0, VECTOR, 0) else: returnval = ida_struct.add_struc_member( struc, classname, offset, flags, None, numbytes) if returnval: print("Could not add struct member {}.{}! Error {}". format(ida_struct.get_struc_name(struc.id), classname, returnval))