Updated IDAPython vtable dump script (NPOTB).
This commit is contained in:
		
							parent
							
								
									b52f696d49
								
							
						
					
					
						commit
						a8021e0d79
					
				@ -18,6 +18,79 @@ __license__ = "zlib/libpng"
 | 
			
		||||
 | 
			
		||||
import re
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
Output Format:"
 | 
			
		||||
    VTable for <1>: (<2>, <3>)
 | 
			
		||||
    Lin    Win Function
 | 
			
		||||
    <4><5> <6> <7>
 | 
			
		||||
    <4><5> <6> <7>
 | 
			
		||||
    ...
 | 
			
		||||
    <4><5> <6> <7>
 | 
			
		||||
 | 
			
		||||
1: Classname
 | 
			
		||||
2: VTable Offset
 | 
			
		||||
3: Linux This Pointer Offset
 | 
			
		||||
4: "T" if the function is a MI thunk
 | 
			
		||||
5: Linux VTable Index
 | 
			
		||||
6: Windows VTable Index
 | 
			
		||||
7: Function Signature
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
catchclass = False
 | 
			
		||||
innerclass = ""
 | 
			
		||||
 | 
			
		||||
classname = None
 | 
			
		||||
offsetdata = {}
 | 
			
		||||
 | 
			
		||||
def ExtractTypeInfo(ea, level = 0):
 | 
			
		||||
	global catchclass
 | 
			
		||||
	global innerclass
 | 
			
		||||
	global classname
 | 
			
		||||
	global offsetdata
 | 
			
		||||
	
 | 
			
		||||
	# Param needed to support old IDAPython versions
 | 
			
		||||
	end = NextHead(ea, 4294967295)
 | 
			
		||||
	
 | 
			
		||||
	# Skip vtable
 | 
			
		||||
	ea += 4
 | 
			
		||||
	
 | 
			
		||||
	# Get type name
 | 
			
		||||
	name = Demangle("_Z" + GetString(Dword(ea)), GetLongPrm(INF_LONG_DN))
 | 
			
		||||
	ea += 4
 | 
			
		||||
	
 | 
			
		||||
	if classname is None and level == 0:
 | 
			
		||||
		classname = name
 | 
			
		||||
	
 | 
			
		||||
	if catchclass:
 | 
			
		||||
		innerclass = name
 | 
			
		||||
		catchclass = False
 | 
			
		||||
	
 | 
			
		||||
	print "%*s%s" % (level, "", name)
 | 
			
		||||
	
 | 
			
		||||
	if not ea < end: # Base Type
 | 
			
		||||
		pass
 | 
			
		||||
	elif isData(GetFlags(Dword(ea))): # Single Inheritance
 | 
			
		||||
		ExtractTypeInfo(Dword(ea), level + 1)
 | 
			
		||||
		ea += 4
 | 
			
		||||
	else: # Multiple Inheritance
 | 
			
		||||
		ea += 8
 | 
			
		||||
		while ea < end:
 | 
			
		||||
			catchclass = True
 | 
			
		||||
			ExtractTypeInfo(Dword(ea), level + 1)
 | 
			
		||||
			ea += 4
 | 
			
		||||
			offset = Dword(ea)
 | 
			
		||||
			ea += 4
 | 
			
		||||
			#print "%*s Offset: 0x%06X" % (level, "", offset >> 8)
 | 
			
		||||
			if (offset >> 8) != 0:
 | 
			
		||||
				offsetdata[offset >> 8] = innerclass
 | 
			
		||||
 | 
			
		||||
# Source: http://stackoverflow.com/a/9147327
 | 
			
		||||
def twos_comp(val, bits):
 | 
			
		||||
	"""compute the 2's compliment of int value val"""
 | 
			
		||||
	if (val & (1 << (bits - 1))) != 0:
 | 
			
		||||
		val = val - (1 << bits)
 | 
			
		||||
	return val
 | 
			
		||||
 | 
			
		||||
def Analyze():
 | 
			
		||||
	SetStatus(IDA_STATUS_WORK)
 | 
			
		||||
	
 | 
			
		||||
@ -29,7 +102,8 @@ def Analyze():
 | 
			
		||||
	ea = ScreenEA()
 | 
			
		||||
	
 | 
			
		||||
	if not isHead(GetFlags(ea)):
 | 
			
		||||
		ea = PrevHead(ea)
 | 
			
		||||
		# Param needed to support old IDAPython versions
 | 
			
		||||
		ea = PrevHead(ea, 0)
 | 
			
		||||
	
 | 
			
		||||
	# Param needed to support old IDAPython versions
 | 
			
		||||
	end = NextHead(ea, 4294967295)
 | 
			
		||||
@ -43,18 +117,23 @@ def Analyze():
 | 
			
		||||
	linux_vtable = []
 | 
			
		||||
	temp_windows_vtable = []
 | 
			
		||||
	
 | 
			
		||||
	other_linux_vtables = {}
 | 
			
		||||
	other_thunk_linux_vtables = {}
 | 
			
		||||
	temp_other_windows_vtables = {}
 | 
			
		||||
	
 | 
			
		||||
	# Extract vtable
 | 
			
		||||
	while ea < end:
 | 
			
		||||
		offset = Dword(ea)
 | 
			
		||||
		# Read thisoffs
 | 
			
		||||
		offset = -twos_comp(Dword(ea), 32)
 | 
			
		||||
		ea += 4
 | 
			
		||||
		
 | 
			
		||||
		# A vtable starts with some metadata, if it's missing...
 | 
			
		||||
		if isCode(GetFlags(offset)):
 | 
			
		||||
			Warning("Something went wrong!")
 | 
			
		||||
			SetStatus(IDA_STATUS_READY)
 | 
			
		||||
			return
 | 
			
		||||
		# Read typeinfo address
 | 
			
		||||
		typeinfo = Dword(ea)
 | 
			
		||||
		ea += 4
 | 
			
		||||
		
 | 
			
		||||
		# Skip thisoffs and typeinfo address
 | 
			
		||||
		ea += 8
 | 
			
		||||
		if offset == 0: # We only need to read this once
 | 
			
		||||
			print "Inheritance Tree:"
 | 
			
		||||
			ExtractTypeInfo(typeinfo)
 | 
			
		||||
		
 | 
			
		||||
		while ea < end and isCode(GetFlags(Dword(ea))):
 | 
			
		||||
			name = Demangle(Name(Dword(ea)), GetLongPrm(INF_LONG_DN))
 | 
			
		||||
@ -63,9 +142,22 @@ def Analyze():
 | 
			
		||||
				linux_vtable.append(name)
 | 
			
		||||
				temp_windows_vtable.append(name)
 | 
			
		||||
			else:
 | 
			
		||||
				if offset not in other_linux_vtables:
 | 
			
		||||
					other_linux_vtables[offset] = []
 | 
			
		||||
					temp_other_windows_vtables[offset] = []
 | 
			
		||||
					other_thunk_linux_vtables[offset] = []
 | 
			
		||||
				
 | 
			
		||||
				if "`non-virtual thunk to'" in name:
 | 
			
		||||
					other_linux_vtables[offset].append(name[22:])
 | 
			
		||||
					other_thunk_linux_vtables[offset].append(name[22:])
 | 
			
		||||
					temp_other_windows_vtables[offset].append(name[22:])
 | 
			
		||||
				else:
 | 
			
		||||
					other_linux_vtables[offset].append(name)
 | 
			
		||||
					temp_other_windows_vtables[offset].append(name)
 | 
			
		||||
				
 | 
			
		||||
				# MI entry, strip "`non-virtual thunk to'" and remove from list
 | 
			
		||||
				#     But not if it's a dtor... what the hell is this.
 | 
			
		||||
				if (name.find("`non-virtual thunk to'") != -1) and name.find("::~") == -1:
 | 
			
		||||
				if "`non-virtual thunk to'" in name and "::~" not in name:
 | 
			
		||||
					name = name[22:]
 | 
			
		||||
					#print "Stripping '%s' from windows vtable." % (name)
 | 
			
		||||
					temp_windows_vtable.remove(name)
 | 
			
		||||
@ -73,7 +165,7 @@ def Analyze():
 | 
			
		||||
			ea += 4
 | 
			
		||||
	
 | 
			
		||||
	for i, v in enumerate(temp_windows_vtable):
 | 
			
		||||
		if v.find("::~") != -1:
 | 
			
		||||
		if "::~" in v:
 | 
			
		||||
			#print "Found destructor at index %d: %s" % (i, v)
 | 
			
		||||
			del temp_windows_vtable[i]
 | 
			
		||||
			break
 | 
			
		||||
@ -96,16 +188,19 @@ def Analyze():
 | 
			
		||||
			overload_stack.append(v)
 | 
			
		||||
		else:
 | 
			
		||||
			# If we've moved onto something new, dump the stack first
 | 
			
		||||
			if len(overload_stack) > 0:
 | 
			
		||||
				#print overload_stack
 | 
			
		||||
				while len(overload_stack) > 0:
 | 
			
		||||
					windows_vtable.append(overload_stack.pop())
 | 
			
		||||
			while len(overload_stack) > 0:
 | 
			
		||||
				windows_vtable.append(overload_stack.pop())
 | 
			
		||||
			
 | 
			
		||||
			windows_vtable.append(v)
 | 
			
		||||
		
 | 
			
		||||
		prev_function = function
 | 
			
		||||
		prev_symbol = v
 | 
			
		||||
	
 | 
			
		||||
	# If there is anything left in the stack, dump it
 | 
			
		||||
	while len(overload_stack) > 0:
 | 
			
		||||
		windows_vtable.append(overload_stack.pop())
 | 
			
		||||
	
 | 
			
		||||
	print "\nVTable for %s: (0, 0)" % (classname)
 | 
			
		||||
	print "Lin Win Function"
 | 
			
		||||
	for i, v in enumerate(linux_vtable):
 | 
			
		||||
		winindex = windows_vtable.index(v) if v in windows_vtable else None
 | 
			
		||||
@ -114,7 +209,45 @@ def Analyze():
 | 
			
		||||
		else:
 | 
			
		||||
			print "%3d     %s" % (i, v)
 | 
			
		||||
	
 | 
			
		||||
	for k in temp_other_windows_vtables:
 | 
			
		||||
		for i, v in enumerate(temp_other_windows_vtables[k]):
 | 
			
		||||
			if v.find("::~") != -1:
 | 
			
		||||
				#print "Found destructor at index %d: %s" % (i, v)
 | 
			
		||||
				del temp_other_windows_vtables[k][i]
 | 
			
		||||
				break
 | 
			
		||||
	
 | 
			
		||||
	other_windows_vtables = {}
 | 
			
		||||
	for k in temp_other_windows_vtables:
 | 
			
		||||
		other_windows_vtables[k] = []
 | 
			
		||||
		overload_stack = []
 | 
			
		||||
		prev_function = ""
 | 
			
		||||
		prev_symbol = ""
 | 
			
		||||
		for v in temp_other_windows_vtables[k]:
 | 
			
		||||
			function = v.split("(", 1)[0]
 | 
			
		||||
			if function == prev_function:
 | 
			
		||||
				if len(overload_stack) == 0:
 | 
			
		||||
					other_windows_vtables[k].pop()
 | 
			
		||||
					overload_stack.append(prev_symbol)
 | 
			
		||||
				overload_stack.append(v)
 | 
			
		||||
			else:
 | 
			
		||||
				if len(overload_stack) > 0:
 | 
			
		||||
					while len(overload_stack) > 0:
 | 
			
		||||
						other_windows_vtables[k].append(overload_stack.pop())
 | 
			
		||||
				other_windows_vtables[k].append(v)
 | 
			
		||||
			prev_function = function
 | 
			
		||||
			prev_symbol = v
 | 
			
		||||
	
 | 
			
		||||
	for k in other_linux_vtables:
 | 
			
		||||
		print "\nVTable for %s: (%d, %d)" % (offsetdata[k], offsetdata.keys().index(k) + 1, k)
 | 
			
		||||
		print "Lin Win Function"
 | 
			
		||||
		for i, v in enumerate(other_linux_vtables[k]):
 | 
			
		||||
			winindex = other_windows_vtables[k].index(v)
 | 
			
		||||
			if v not in other_thunk_linux_vtables[k]:
 | 
			
		||||
				print "%3d %3d %s" % (i, winindex, v)
 | 
			
		||||
			else:
 | 
			
		||||
				print "T%2d %3d %s" % (i, winindex, v)
 | 
			
		||||
	
 | 
			
		||||
	SetStatus(IDA_STATUS_READY)
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
	Analyze()
 | 
			
		||||
	Analyze()
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user