Updated IDAPython vtable dump script (NPOTB).
Updated IDAPython vtable dump script (NPOTB).
1.1 --- a/tools/ida_scripts/vtable_dump.py Sat Aug 04 13:53:51 2012 -0700
1.2 +++ b/tools/ida_scripts/vtable_dump.py Tue Aug 07 02:11:07 2012 +0100
1.3 @@ -18,6 +18,79 @@
1.4
1.5 import re
1.6
1.7 +"""
1.8 +Output Format:"
1.9 + VTable for <1>: (<2>, <3>)
1.10 + Lin Win Function
1.11 + <4><5> <6> <7>
1.12 + <4><5> <6> <7>
1.13 + ...
1.14 + <4><5> <6> <7>
1.15 +
1.16 +1: Classname
1.17 +2: VTable Offset
1.18 +3: Linux This Pointer Offset
1.19 +4: "T" if the function is a MI thunk
1.20 +5: Linux VTable Index
1.21 +6: Windows VTable Index
1.22 +7: Function Signature
1.23 +"""
1.24 +
1.25 +catchclass = False
1.26 +innerclass = ""
1.27 +
1.28 +classname = None
1.29 +offsetdata = {}
1.30 +
1.31 +def ExtractTypeInfo(ea, level = 0):
1.32 + global catchclass
1.33 + global innerclass
1.34 + global classname
1.35 + global offsetdata
1.36 +
1.37 + # Param needed to support old IDAPython versions
1.38 + end = NextHead(ea, 4294967295)
1.39 +
1.40 + # Skip vtable
1.41 + ea += 4
1.42 +
1.43 + # Get type name
1.44 + name = Demangle("_Z" + GetString(Dword(ea)), GetLongPrm(INF_LONG_DN))
1.45 + ea += 4
1.46 +
1.47 + if classname is None and level == 0:
1.48 + classname = name
1.49 +
1.50 + if catchclass:
1.51 + innerclass = name
1.52 + catchclass = False
1.53 +
1.54 + print "%*s%s" % (level, "", name)
1.55 +
1.56 + if not ea < end: # Base Type
1.57 + pass
1.58 + elif isData(GetFlags(Dword(ea))): # Single Inheritance
1.59 + ExtractTypeInfo(Dword(ea), level + 1)
1.60 + ea += 4
1.61 + else: # Multiple Inheritance
1.62 + ea += 8
1.63 + while ea < end:
1.64 + catchclass = True
1.65 + ExtractTypeInfo(Dword(ea), level + 1)
1.66 + ea += 4
1.67 + offset = Dword(ea)
1.68 + ea += 4
1.69 + #print "%*s Offset: 0x%06X" % (level, "", offset >> 8)
1.70 + if (offset >> 8) != 0:
1.71 + offsetdata[offset >> 8] = innerclass
1.72 +
1.73 +# Source: http://stackoverflow.com/a/9147327
1.74 +def twos_comp(val, bits):
1.75 + """compute the 2's compliment of int value val"""
1.76 + if (val & (1 << (bits - 1))) != 0:
1.77 + val = val - (1 << bits)
1.78 + return val
1.79 +
1.80 def Analyze():
1.81 SetStatus(IDA_STATUS_WORK)
1.82
1.83 @@ -29,7 +102,8 @@
1.84 ea = ScreenEA()
1.85
1.86 if not isHead(GetFlags(ea)):
1.87 - ea = PrevHead(ea)
1.88 + # Param needed to support old IDAPython versions
1.89 + ea = PrevHead(ea, 0)
1.90
1.91 # Param needed to support old IDAPython versions
1.92 end = NextHead(ea, 4294967295)
1.93 @@ -43,18 +117,23 @@
1.94 linux_vtable = []
1.95 temp_windows_vtable = []
1.96
1.97 + other_linux_vtables = {}
1.98 + other_thunk_linux_vtables = {}
1.99 + temp_other_windows_vtables = {}
1.100 +
1.101 # Extract vtable
1.102 while ea < end:
1.103 - offset = Dword(ea)
1.104 + # Read thisoffs
1.105 + offset = -twos_comp(Dword(ea), 32)
1.106 + ea += 4
1.107
1.108 - # A vtable starts with some metadata, if it's missing...
1.109 - if isCode(GetFlags(offset)):
1.110 - Warning("Something went wrong!")
1.111 - SetStatus(IDA_STATUS_READY)
1.112 - return
1.113 + # Read typeinfo address
1.114 + typeinfo = Dword(ea)
1.115 + ea += 4
1.116
1.117 - # Skip thisoffs and typeinfo address
1.118 - ea += 8
1.119 + if offset == 0: # We only need to read this once
1.120 + print "Inheritance Tree:"
1.121 + ExtractTypeInfo(typeinfo)
1.122
1.123 while ea < end and isCode(GetFlags(Dword(ea))):
1.124 name = Demangle(Name(Dword(ea)), GetLongPrm(INF_LONG_DN))
1.125 @@ -63,9 +142,22 @@
1.126 linux_vtable.append(name)
1.127 temp_windows_vtable.append(name)
1.128 else:
1.129 + if offset not in other_linux_vtables:
1.130 + other_linux_vtables[offset] = []
1.131 + temp_other_windows_vtables[offset] = []
1.132 + other_thunk_linux_vtables[offset] = []
1.133 +
1.134 + if "`non-virtual thunk to'" in name:
1.135 + other_linux_vtables[offset].append(name[22:])
1.136 + other_thunk_linux_vtables[offset].append(name[22:])
1.137 + temp_other_windows_vtables[offset].append(name[22:])
1.138 + else:
1.139 + other_linux_vtables[offset].append(name)
1.140 + temp_other_windows_vtables[offset].append(name)
1.141 +
1.142 # MI entry, strip "`non-virtual thunk to'" and remove from list
1.143 # But not if it's a dtor... what the hell is this.
1.144 - if (name.find("`non-virtual thunk to'") != -1) and name.find("::~") == -1:
1.145 + if "`non-virtual thunk to'" in name and "::~" not in name:
1.146 name = name[22:]
1.147 #print "Stripping '%s' from windows vtable." % (name)
1.148 temp_windows_vtable.remove(name)
1.149 @@ -73,7 +165,7 @@
1.150 ea += 4
1.151
1.152 for i, v in enumerate(temp_windows_vtable):
1.153 - if v.find("::~") != -1:
1.154 + if "::~" in v:
1.155 #print "Found destructor at index %d: %s" % (i, v)
1.156 del temp_windows_vtable[i]
1.157 break
1.158 @@ -96,16 +188,19 @@
1.159 overload_stack.append(v)
1.160 else:
1.161 # If we've moved onto something new, dump the stack first
1.162 - if len(overload_stack) > 0:
1.163 - #print overload_stack
1.164 - while len(overload_stack) > 0:
1.165 - windows_vtable.append(overload_stack.pop())
1.166 + while len(overload_stack) > 0:
1.167 + windows_vtable.append(overload_stack.pop())
1.168
1.169 windows_vtable.append(v)
1.170
1.171 prev_function = function
1.172 prev_symbol = v
1.173
1.174 + # If there is anything left in the stack, dump it
1.175 + while len(overload_stack) > 0:
1.176 + windows_vtable.append(overload_stack.pop())
1.177 +
1.178 + print "\nVTable for %s: (0, 0)" % (classname)
1.179 print "Lin Win Function"
1.180 for i, v in enumerate(linux_vtable):
1.181 winindex = windows_vtable.index(v) if v in windows_vtable else None
1.182 @@ -114,7 +209,45 @@
1.183 else:
1.184 print "%3d %s" % (i, v)
1.185
1.186 + for k in temp_other_windows_vtables:
1.187 + for i, v in enumerate(temp_other_windows_vtables[k]):
1.188 + if v.find("::~") != -1:
1.189 + #print "Found destructor at index %d: %s" % (i, v)
1.190 + del temp_other_windows_vtables[k][i]
1.191 + break
1.192 +
1.193 + other_windows_vtables = {}
1.194 + for k in temp_other_windows_vtables:
1.195 + other_windows_vtables[k] = []
1.196 + overload_stack = []
1.197 + prev_function = ""
1.198 + prev_symbol = ""
1.199 + for v in temp_other_windows_vtables[k]:
1.200 + function = v.split("(", 1)[0]
1.201 + if function == prev_function:
1.202 + if len(overload_stack) == 0:
1.203 + other_windows_vtables[k].pop()
1.204 + overload_stack.append(prev_symbol)
1.205 + overload_stack.append(v)
1.206 + else:
1.207 + if len(overload_stack) > 0:
1.208 + while len(overload_stack) > 0:
1.209 + other_windows_vtables[k].append(overload_stack.pop())
1.210 + other_windows_vtables[k].append(v)
1.211 + prev_function = function
1.212 + prev_symbol = v
1.213 +
1.214 + for k in other_linux_vtables:
1.215 + print "\nVTable for %s: (%d, %d)" % (offsetdata[k], offsetdata.keys().index(k) + 1, k)
1.216 + print "Lin Win Function"
1.217 + for i, v in enumerate(other_linux_vtables[k]):
1.218 + winindex = other_windows_vtables[k].index(v)
1.219 + if v not in other_thunk_linux_vtables[k]:
1.220 + print "%3d %3d %s" % (i, winindex, v)
1.221 + else:
1.222 + print "T%2d %3d %s" % (i, winindex, v)
1.223 +
1.224 SetStatus(IDA_STATUS_READY)
1.225
1.226 if __name__ == '__main__':
1.227 - Analyze()
1.228 \ No newline at end of file
1.229 + Analyze()