Repository: nccgroup/SusanRTTI Branch: master Commit: a0baf99937cd Files: 8 Total size: 26.3 KB Directory structure: gitextract_r9sq1w8f/ ├── .gitignore ├── LICENSE.txt ├── classdiagram.py ├── classinformer.py ├── gcc.py ├── msvc.py ├── readme.md └── utils.py ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ *.pyproj *.sln .vs/* idaapi.py .env .vscode *.pyc .DS_Store ================================================ FILE: LICENSE.txt ================================================ This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: classdiagram.py ================================================ from idaapi import GraphViewer, ask_file # The below will only be displayed as bases ignore_namespaces = ("std", "type_info") class ClassDiagram(GraphViewer): def __init__(self, title, classes): self.classes = self.transitive_reduction(classes) GraphViewer.__init__(self, title) def transitive_reduction(self, graph): for u in graph.keys(): print('u node: ' + u + '(parents: ' + ', '.join([v for v in graph[u]]) + ')') for v in graph[u]: # Compute the dfs from v print('DFS from v node: ' + v) dfs = self.dfs_paths(graph, v) for node in dfs: print(' - DFS node: ' + str(node)) if v != node and node in graph[u]: graph[u].remove(node) print(' # Removed ' + u + ' -> ' + node) return graph def dfs_paths(self, graph, start, path=None): if path is None: path = [start] # check for leaf if start not in graph.keys() or len(graph[start])==0: #print(' + ' + str(len(graph[start]))) #print(start + ': []') for p in path: yield p else: #print(start + ': [' + ', '.join([s for s in graph[start]]) + ']') print(' + RECUR: ' + ', '.join(set(graph[start]) - set(path))) for next in set(graph[start]) - set(path): for p in self.dfs_paths(graph, next, path + [next]): print(' + ' + p) yield p def add_node(self, class_name): if class_name is None: return if not class_name.startswith(ignore_namespaces): print("Adding node: " + class_name) new_node = self.AddNode(class_name) self.name_to_node[class_name] = new_node def OnRefresh(self): self.Clear() self.name_to_node = {} # Create nodes for class_name in self.classes.keys(): # Skipping common namespaces self.add_node(class_name) # Create edges for class_name in self.name_to_node.keys(): print("Adding edges for: " + class_name) node = self.name_to_node[class_name] print("bases: " + str(self.classes[class_name])) for base_name in self.classes[class_name]: if base_name not in self.name_to_node: # Add originally skipped base node self.add_node(base_name) base = self.name_to_node.get(base_name) if base is not None: self.AddEdge(base, node) return True def OnGetText(self, node_id): return self[node_id] # dot file export modified from http://joxeankoret.com def OnCommand(self, cmd_id): if self.cmd_dot == cmd_id: fname = ask_file(1, "*.dot", "Export DOT file") if fname: f = open(fname, "wt") buf = "digraph G {\n graph [overlap=scale]; node [fontname=Courier]; rankdir=\"LR\";\n\n" keyList = list(self.classes.keys()) for c in keyList: n = keyList.index(c) buf += ' a%s [shape=box, label = "%s", color="blue"]\n' % (n, c) buf += "\n" for c in keyList: class_index = keyList.index(c) for base in self.classes[c]: if base in keyList: base_index = keyList.index(base) buf += ' a%s -> a%s [style = bold]\n' % (class_index, base_index) buf += "}" f.write(buf) f.close() def Show(self): if not GraphViewer.Show(self): return False self.cmd_dot = self.AddCommand("Export DOT", "F2") return True ================================================ FILE: classinformer.py ================================================ # ClassInformer python # Nicolas Guigo / NCC Group # Tyler Colgan / NCC Group # 03/2017 import idaapi from idc import * from ida_search import find_text idaapi.require("utils") idaapi.require("msvc") idaapi.require("gcc") idaapi.require("classdiagram") from idaapi import auto_is_ok from msvc import run_msvc from gcc import run_gcc from classdiagram import ClassDiagram def show_classes(classes): c = ClassDiagram("Class Diagram", classes) c.Show() def isGcc(): gcc_info = find_text(0x0, 0, 0, "N10__cxxabiv117__class_type_infoE", SEARCH_CASE|SEARCH_DOWN) return gcc_info != BADADDR def main(): print("Starting ClassInformerPython") if auto_is_ok(): classes = run_gcc() if isGcc() else run_msvc() print(classes) show_classes(classes) else: print("Take it easy, man") print("Done") if __name__ == '__main__': main() ================================================ FILE: gcc.py ================================================ # Modified from GCC RTTI parsing code originally written by Igor Skochinsky. # See http://www.hexblog.com/?p=704 for the original version of his code. import idaapi from idaapi import BADADDR from idc import * from utils import utils u = utils() all_classes = {} ti_names = [ "St9type_info", "N10__cxxabiv117__class_type_infoE", "N10__cxxabiv120__si_class_type_infoE", "N10__cxxabiv121__vmi_class_type_infoE", ] TI_TINFO = 0 TI_CTINFO = 1 TI_SICTINFO = 2 TI_VMICTINFO = 3 class BaseClass: def __init__(self, ti, offset, flags): self.ti = ti self.offset = offset self.flags = flags class ClassDescriptor: def __init__(self, vtable, namestr): self.vtable = vtable self.namestr = namestr self.bases = [] def add_base(self, base, offset=0, flags=0): self.bases.append(BaseClass(base, offset, flags)) def tinfo2class(tiname): s = demangle_name(tiname, 0) if s is None: return s return s.replace("`typeinfo for'","") def classname(namestr): return tinfo2class("__ZTI" + namestr) # dd `vtable for'std::type_info+8 # dd `typeinfo name for'std::type_info def format_type_info(ea): # get the class name string tis = u.get_ptr(ea + u.PTR_SIZE) if u.is_bad_addr(tis): return BADADDR bname = get_strlit_contents(tis) if bname == None or len(bname) == 0: return BADADDR # looks good, let's do it name = bname.decode('UTF-8') ea2 = u.format_struct(ea, "vp") u.force_name(tis, "__ZTS" + name) u.force_name(ea, "__ZTI" + name) # find our vtable # 0 followed by ea pat = u.ptr_to_bytes(0) + " " + u.ptr_to_bytes(ea) vtb = find_binary(0, SEARCH_CASE|SEARCH_DOWN, pat) if not u.is_bad_addr(vtb): print("vtable for %s at %08X" % (name, vtb)) u.format_struct(vtb, "pp") u.force_name(vtb, u.vtname(name)) else: vtb = BADADDR all_classes[ea] = ClassDescriptor(vtb, name) return ea2 # dd `vtable for'__cxxabiv1::__si_class_type_info+8 # dd `typeinfo name for'MyClass # dd `typeinfo for'BaseClass def format_si_type_info(ea): ea2 = format_type_info(ea) pbase = u.get_ptr(ea2) all_classes[ea].add_base(pbase) ea2 = u.format_struct(ea2, "p") return ea2 # dd `vtable for'__cxxabiv1::__si_class_type_info+8 # dd `typeinfo name for'MyClass # dd flags # dd base_count # (base_type, offset_flags) x base_count def format_vmi_type_info(ea): ea2 = format_type_info(ea) ea2 = u.format_struct(ea2, "ii") base_count = create_data(ea2-4, FF_DWORD, 4, ida_idaapi.BADADDR) clas = all_classes[ea] if base_count > 100: print("%08X: over 100 base classes?!" % ea) return BADADDR for i in range(base_count): base_ti = u.get_ptr(ea2) flags_off = u.get_ptr(ea2 + u.PTR_SIZE) off = u.SIGNEXT(flags_off>>8, 24) clas.add_base(base_ti, off, flags_off & 0xFF) ea2 = u.format_struct(ea2, "pl") return ea2 def find_type_info(idx): name = ti_names[idx] ea = u.find_string(name) if ea != BADADDR: xrefs = u.xref_or_find(ea) if xrefs: ti_start = xrefs[0] - u.PTR_SIZE if not u.is_bad_addr(ti_start): print("found %d at %08X" % (idx, ti_start)) ea2 = format_type_info(ti_start) if idx >= TI_CTINFO: u.format_struct(ea2, "p") def handle_classes(idx, formatter): name = u.vtname(ti_names[idx]) ea = get_name_ea_simple(name) if ea == BADADDR: # try single underscore name = name[1:] ea = get_name_ea_simple(name) if ea == BADADDR: print("Could not find vtable for %s" % ti_names[idx]) return idx = 0 handled = set() while ea != BADADDR: print("Looking for refs to vtable %08X" % ea) if idaapi.is_spec_ea(ea): xrefs = u.xref_or_find(ea, True) ea += u.PTR_SIZE*2 xrefs.extend(u.xref_or_find(ea, True)) else: ea += u.PTR_SIZE*2 xrefs = u.xref_or_find(ea, True) for x in xrefs: if not u.is_bad_addr(x) and not x in handled: print("found %s at %08X" % (name, x)) ea2 = formatter(x) handled.add(x) ea = get_name_ea_simple("%s_%d" % (name, idx)) idx += 1 def run_gcc(): classes = {} # turn on GCC3 demangling idaapi.cvar.inf.demnames |= idaapi.DEMNAM_GCC3 print("Looking for standard type info classes") find_type_info(TI_TINFO) find_type_info(TI_CTINFO) find_type_info(TI_SICTINFO) find_type_info(TI_VMICTINFO) print("Looking for simple classes") handle_classes(TI_CTINFO, format_type_info) print("Looking for single-inheritance classes") handle_classes(TI_SICTINFO, format_si_type_info) print("Looking for multiple-inheritance classes") handle_classes(TI_VMICTINFO, format_vmi_type_info) for i in range(len(all_classes)): tiaddr = u.num2key(all_classes)[i] klass = all_classes[tiaddr] name = classname(klass.namestr) ti = "%08X" % tiaddr vt = "%08X" % klass.vtable basestr = [] for b in klass.bases: if b.ti in all_classes: bklass = all_classes[b.ti] basename = classname(bklass.namestr) elif idaapi.is_spec_ea(b.ti): nm = get_name(b.ti, ida_name.GN_VISIBLE) basename = tinfo2class(nm) else: print("Base %08X not found for class %08X!" % (b.ti, tiaddr)) basename = "ti_%08X" % b.ti basestr.append(basename) classes[name] = basestr print("basestr: \"%s\"" % basestr) return classes ================================================ FILE: msvc.py ================================================ from idaapi import get_struc_id, BADADDR, del_struc, get_struc, add_struc, add_struc_member, FF_DATA, FF_DWORD, FF_0OFF, get_struc_size, FF_STRLIT, del_items, DELIT_DELNAMES, create_struct, get_member_by_name, get_32bit, get_strlit_contents, demangle_name, create_dword, op_offset from idc import * from utils import utils u = utils() classes = {} class RTTIStruc: tid = 0 struc = 0 size = 0 def strip(name): if name.startswith("class ") and name.endswith("`RTTI Type Descriptor'"): return name[6:-23] elif name.startswith("struct ") and name.endswith("`RTTI Type Descriptor'"): return name[7:-23] else: return name class RTTICompleteObjectLocator(RTTIStruc): # Init class statics msid = get_struc_id("RTTICompleteObjectLocator") if msid != BADADDR: del_struc(msid) msid = add_struc(0xFFFFFFFF, "RTTICompleteObjectLocator", False) add_struc_member(msid, "signature", BADADDR, FF_DATA|FF_DWORD, -1, 4) add_struc_member(msid, "offset", BADADDR, FF_DATA|FF_DWORD, -1, 4) add_struc_member(msid, "cdOffset", BADADDR, FF_DATA|FF_DWORD, -1, 4) add_struc_member(msid, "pTypeDescriptor", BADADDR, FF_DATA|FF_DWORD|FF_0OFF, u.mt_rva().tid, 4) add_struc_member(msid, "pClassDescriptor", BADADDR, FF_DATA|FF_DWORD|FF_0OFF, u.mt_rva().tid, 4) if u.x64: add_struc_member(msid, "pSelf", BADADDR, FF_DATA|FF_DWORD|FF_0OFF, u.mt_rva().tid, 4) tid = msid struc = get_struc(tid) size = get_struc_size(tid) print("Completed Registering RTTICompleteObjectLocator") def __init__(self, ea, vtable): del_items(ea, DELIT_DELNAMES, self.size) if ida_bytes.create_struct(ea, self.size, self.tid): # Get adress of type descriptor from CompleteLocator print("Complete Object Locator at: 0x%x" % ea) offset = get_member_by_name(self.struc, "pTypeDescriptor").soff typeDescriptor = get_32bit(ea+offset) + u.x64_imagebase() print("Looking for type Descriptor at: 0x%x" % typeDescriptor) rtd = RTTITypeDescriptor(typeDescriptor) if rtd.class_name: print("Type Descriptor at: 0x%x" % typeDescriptor) offset = get_member_by_name(self.struc, "pClassDescriptor").soff classHierarchyDes = get_32bit(ea+offset) + u.x64_imagebase() rchd = RTTIClassHierarchyDescriptor(classHierarchyDes) # filter out None entries rchd.bases = filter(lambda x: x, rchd.bases) classes[strip(rtd.class_name)] = [strip(b) for b in rchd.bases] set_name(vtable, "vtable__" + strip(rtd.class_name), SN_NOWARN) else: # if the RTTITypeDescriptor doesn't have a valid name for us to # read, then this wasn't a valid RTTICompleteObjectLocator del_items(ea, self.size, DELIT_SIMPLE) class RTTITypeDescriptor(RTTIStruc): class_name = None msid = get_struc_id("RTTITypeDescriptor") if msid != BADADDR: del_struc(msid) msid = add_struc(0xFFFFFFFF, "RTTITypeDescriptor", False) add_struc_member(msid, "pVFTable", BADADDR, FF_DATA|u.PTR_TYPE|FF_0OFF, u.mt_address().tid, u.PTR_SIZE) add_struc_member(msid, "spare", BADADDR, FF_DATA|u.PTR_TYPE, -1, u.PTR_SIZE) add_struc_member(msid, "name", BADADDR, FF_DATA|FF_STRLIT, u.mt_ascii().tid, 0) tid = msid struc = get_struc(tid) size = get_struc_size(tid) print("Completed Registering RTTITypeDescriptor") def __init__(self, ea): name = ea + get_member_by_name(get_struc(self.tid), "name").soff strlen = u.get_strlen(name) if strlen is None: # not a real vtable return self.size = self.size + strlen bmangled = get_strlit_contents(name, strlen, 0) if bmangled is None: # not a real function name return mangled = bmangled.decode('UTF-8') print("Mangled: " + mangled) demangled = demangle_name('??_R0' + mangled[1:] , 0) if demangled: del_items(ea, DELIT_DELNAMES, self.size) if ida_bytes.create_struct(ea, self.size, self.tid): print(" Made td at 0x%x: %s" % (ea, demangled)) self.class_name = demangled return print(" FAIL :(") return class RTTIClassHierarchyDescriptor(RTTIStruc): bases = None msid = get_struc_id("RTTIClassHierarchyDescriptor") if msid != BADADDR: del_struc(msid) msid = add_struc(0xFFFFFFFF, "RTTIClassHierarchyDescriptor", False) add_struc_member(msid, "signature", BADADDR, FF_DWORD|FF_DATA, -1, 4) add_struc_member(msid, "attribute", BADADDR, FF_DWORD|FF_DATA, -1, 4) add_struc_member(msid, "numBaseClasses", BADADDR, FF_DWORD|FF_DATA, -1, 4) add_struc_member(msid, "pBaseClassArray", BADADDR, FF_DATA|FF_DWORD|FF_0OFF, u.mt_rva().tid, 4) tid = msid struc = get_struc(tid) print("Completed Registering RTTIClassHierarchyDescriptor") def __init__(self, ea): print("Processing Class Hierarchy Descriptor at 0x%x" % ea) del_items(ea, DELIT_DELNAMES, get_struc_size(self.tid)) if ida_bytes.create_struct(ea, get_struc_size(self.tid), self.tid): baseClasses = get_32bit(ea+get_member_by_name(get_struc(self.tid), "pBaseClassArray").soff) + u.x64_imagebase() nb_classes = get_32bit(ea+get_member_by_name(get_struc(self.tid), "numBaseClasses").soff) print("Baseclasses array at 0x%x" % baseClasses) # Skip the first base class as it is itself (could check) self.bases = [] for i in range(1, nb_classes): baseClass = get_32bit(baseClasses+i*4) + u.x64_imagebase() print("base class 0x%x" % baseClass) ida_bytes.create_dword(baseClasses+i*4, 4) op_offset(baseClasses+i*4, -1, u.REF_OFF|REFINFO_RVA, -1, 0, 0) ida_bytes.create_struct(baseClass, RTTIBaseClassDescriptor.size, RTTIBaseClassDescriptor.tid) typeDescriptor = get_32bit(baseClass) + u.x64_imagebase() self.bases.append(RTTITypeDescriptor(typeDescriptor).class_name) class RTTIBaseClassDescriptor(RTTIStruc): msid = get_struc_id("RTTIBaseClassDescriptor") if msid != BADADDR: del_struc(msid) msid = add_struc(0xFFFFFFFF, "RTTIBaseClassDescriptor", False) add_struc_member(msid, "pTypeDescriptor", BADADDR, FF_DATA|FF_DWORD|FF_0OFF, u.mt_rva().tid, 4) add_struc_member(msid, "numContainerBases", BADADDR, FF_DWORD|FF_DATA, -1, 4) add_struc_member(msid, "PMD", BADADDR, FF_DATA|FF_DWORD|FF_0OFF, u.mt_rva().tid, 4) add_struc_member(msid, "attributes", BADADDR, FF_DWORD|FF_DATA, -1, 4) tid = msid struc = get_struc(tid) size = get_struc_size(tid) print("Completed Registering RTTIBaseClassDescriptor") def run_msvc(): start = u.rdata.start_ea end = u.rdata.end_ea rdata_size = end-start for offset in range(0, rdata_size-u.PTR_SIZE, u.PTR_SIZE): vtable = start+offset if u.isVtable(vtable): print("vtable at : " + hex(vtable)) col = u.get_ptr(vtable-u.PTR_SIZE) if u.within(col, u.valid_ranges): rcol = RTTICompleteObjectLocator(col, vtable) u.add_missing_classes(classes) return classes ================================================ FILE: readme.md ================================================ # SusanRTTI # #### Another RTTI Parsing IDA plugin #### ### Features ### * All ida-python * Class based design, error logging * RTTI parsing algorithm scanning for vtables first (instead of bruteforcing the entire rdata/data sections) * Graphing of class hierarchy (using transitive reduction for clarity) * Export functionality to GraphViz (.dot) format * Handles RTTI and C++ name demangling for: * X86 GCC * X86 MSVC * X64 GCC * X64 MSVC ### Usage ### Simple: First load your binary in IDA. Then run script `classinformerpython.py` To export the dot file, either right click on the class diagram and select `export` or just hit F2. ### References ### GraphView reference: http://www.graphviz.org/ Online viewer: http://www.webgraphviz.com/ ================================================ FILE: utils.py ================================================ import struct import idaapi from idc import * from idaapi import get_segm_by_name, has_xref, get_full_flags, opinfo_t, refinfo_t,\ get_32bit, get_64bit, get_imagebase, get_byte import idautils from idautils import DataRefsTo # Segments within = lambda x, rl: any([True for r in rl if r[0]<=x<=r[1]]) class utils(object): text = 0 data = 0 rdata = 0 valid_ranges = [] within = lambda self, x, rl: any([True for r in rl if r[0]<=x<=r[1]]) REF_OFF = 0 x64 = 0 PTR_TYPE = 0 PTR_SIZE = 0 def __init__(self): self.text = get_segm_by_name(".text") self.data = get_segm_by_name(".data") self.rdata = get_segm_by_name(".rdata") # try to use rdata if there actually is an rdata segment, otherwise just use data if self.rdata is not None: self.valid_ranges = [(self.rdata.start_ea, self.rdata.end_ea), (self.data.start_ea, self.data.end_ea)] else: self.valid_ranges = [(self.data.start_ea, self.data.end_ea)] self.x64 = (idaapi.getseg(here()).bitness == 2) if self.x64: self.PTR_TYPE = FF_QWORD self.REF_OFF = REF_OFF64 self.PTR_SIZE = 8 self.get_ptr = get_64bit else: self.PTR_TYPE = FF_DWORD self.REF_OFF = REF_OFF32 self.PTR_SIZE = 4 self.get_ptr = get_32bit # for 32-bit binaries, the RTTI structs contain absolute addresses, but for # 64-bit binaries, they're offsets from the image base. def x64_imagebase(self): if self.x64: return get_imagebase() else: return 0 def mt_rva(self): ri = refinfo_t() ri.flags = self.REF_OFF ri.target = 0 mt = opinfo_t() mt.ri = ri return mt def mt_address(self): ri = refinfo_t() ri.flags = self.REF_OFF ri.target = 0 mt = opinfo_t() mt.ri = ri return mt def mt_ascii(self): ri = refinfo_t() ri.flags = STRTYPE_C ri.target = -1 mt = opinfo_t() mt.ri = ri return mt def get_strlen(self, addr): strlen = 0 while get_byte(addr+strlen) != 0x0 and strlen < 50: strlen+=1 #assume no names will ever be longer than 50 bytes if strlen == 50: return None return strlen def isVtable(self, addr): function = self.get_ptr(addr) # Check if vtable has ref and its first pointer lies within code segment if has_xref(get_full_flags(addr)) and function >= self.text.start_ea and function <= self.text.end_ea: return True return False # helper for bin search def ptr_to_bytes(self, val): if self.x64: sv = struct.pack(" 1 and not allow_many: print("too many xrefs to %08X" % addr) return [] lrefs = [r for r in lrefs if not is_code(get_full_flags(r))] return lrefs def find_string(self, s, afrom=0): print("searching for %s" % s) ea = find_binary(afrom, SEARCH_CASE|SEARCH_DOWN, '"' + s + '"') if ea != BADADDR: print("Found at %08X" % ea) return ea def ForceDword(self, ea): if ea != BADADDR and ea != 0: if not is_dword(get_full_flags(ea)): del_items(ea, 4, DELIT_SIMPLE) create_data(ea, FF_DWORD, 4, BADADDR) if is_off0(get_full_flags(ea)) and get_fixup_target_type(ea) == -1: # remove the offset op_hex(ea, 0) def ForceQword(self, ea): if ea != BADADDR and ea != 0: if not is_qword(get_full_flags(ea)): del_items(ea, 8, DELIT_SIMPLE) create_data(ea, FF_QWORD, 8, BADADDR) if is_off0(get_full_flags(ea)) and get_fixup_target_type(ea) == -1: # remove the offset op_hex(ea, 0) def ForcePtr(self, ea, delta = 0): if self.x64: self.ForceQword(ea) else: self.ForceDword(ea) if get_fixup_target_type(ea) != -1 and is_off0(get_full_flags(ea)): # don't touch fixups return pv = self.get_ptr(ea) if pv != 0 and pv != BADADDR: # apply offset again if idaapi.is_spec_ea(pv): delta = 0 op_offset(ea, 0, [REF_OFF32, REF_OFF64][self.x64], -1, 0, delta) # p pointer # v vtable pointer (delta ptrsize*2) # i integer (32-bit) # l integer (32 or 64-bit) def format_struct(self, ea, fmt): for f in fmt: if f in ['p', 'v']: if f == 'v': delta = self.PTR_SIZE*2 else: delta = 0 self.ForcePtr(ea, delta) ea += self.PTR_SIZE elif f == 'i': self.ForceDword(ea) ea += 4 elif f == 'l': if self.x64: self.ForceQword(ea) ea += 8 else: self.ForceDword(ea) ea += 4 return ea def force_name(self, ea, name): if is_tail(get_full_flags(ea)): del_items(ea, 1, DELIT_SIMPLE) set_name(ea, name, SN_NOWARN) def is_bad_addr(self, ea): return ea == 0 or ea == BADADDR or idaapi.is_spec_ea(ea) or not is_loaded(ea) def vtname(self, name): return "__ZTV" + name # sign extend b low bits in x # from "Bit Twiddling Hacks" def SIGNEXT(self, x, b): m = 1 << (b - 1) x = x & ((1 << b) - 1) return (x ^ m) - m def xref_or_find(self, addr, allow_many = False): lrefs = list(DataRefsTo(addr)) if len(lrefs) == 0: lrefs = list(idautils.refs(addr, self.ptrfirst, self.ptrnext)) if len(lrefs) > 1 and not allow_many: print("too many xrefs to %08X" % addr) return [] lrefs = [r for r in lrefs if not is_code(get_full_flags(r))] return lrefs def num2key(self, all_classes): return [k for k in all_classes] def add_missing_classes(self, classes): missing = [] for c, parents in classes.items(): for parent in parents: if parent not in classes.keys(): missing.append(parent) for m in missing: classes[m] = []