Updated IDAPython vtable dump script (NPOTB).
authorAsher Baker <asherkin@gmail.com>
Tue Aug 07 02:11:07 2012 +0100 (2012-08-07)
changeset 35998dea4d560ca5
parent 3598 315486a898b8
child 3600 8b38b6996600
Updated IDAPython vtable dump script (NPOTB).
tools/ida_scripts/vtable_dump.py
     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()