1 """vtable_dump.py: IDAPython script to dump a linux vtable (and a reconstructed windows one) from a binary."""
4 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.
6 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:
8 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.
10 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
12 3. This notice may not be removed or altered from any source distribution.
15 __author__ = "Asher Baker"
16 __copyright__ = "Copyright 2012, Asher Baker"
17 __license__ = "zlib/libpng"
23 VTable for <1>: (<2>, <3>)
32 3: Linux This Pointer Offset
33 4: "T" if the function is a MI thunk
35 6: Windows VTable Index
45 def ExtractTypeInfo(ea, level = 0):
51 # Param needed to support old IDAPython versions
52 end = NextHead(ea, 4294967295)
58 name = Demangle("_Z" + GetString(Dword(ea)), GetLongPrm(INF_LONG_DN))
61 if classname is None and level == 0:
68 print "%*s%s" % (level, "", name)
70 if not ea < end: # Base Type
72 elif isData(GetFlags(Dword(ea))): # Single Inheritance
73 ExtractTypeInfo(Dword(ea), level + 1)
75 else: # Multiple Inheritance
79 ExtractTypeInfo(Dword(ea), level + 1)
83 #print "%*s Offset: 0x%06X" % (level, "", offset >> 8)
84 if (offset >> 8) != 0:
85 offsetdata[offset >> 8] = innerclass
87 # Source: http://stackoverflow.com/a/9147327
88 def twos_comp(val, bits):
89 """compute the 2's compliment of int value val"""
90 if (val & (1 << (bits - 1))) != 0:
91 val = val - (1 << bits)
95 SetStatus(IDA_STATUS_WORK)
97 if GetLongPrm(INF_COMPILER).id != COMP_GNU:
98 Warning("This script is for binaries compiled with GCC only.")
99 SetStatus(IDA_STATUS_READY)
104 if not isHead(GetFlags(ea)):
105 # Param needed to support old IDAPython versions
108 # Param needed to support old IDAPython versions
109 end = NextHead(ea, 4294967295)
111 name = Demangle(Name(ea), GetLongPrm(INF_LONG_DN))
112 if ea == BADADDR or name is None or not re.search(r"vf?table(?: |'\{)for", name):
113 Warning("No vtable selected!\nSelect vtable block first.")
114 SetStatus(IDA_STATUS_READY)
118 temp_windows_vtable = []
120 other_linux_vtables = {}
121 other_thunk_linux_vtables = {}
122 temp_other_windows_vtables = {}
127 offset = -twos_comp(Dword(ea), 32)
130 # Read typeinfo address
134 if offset == 0: # We only need to read this once
135 print "Inheritance Tree:"
136 ExtractTypeInfo(typeinfo)
138 while ea < end and isCode(GetFlags(Dword(ea))):
139 name = Demangle(Name(Dword(ea)), GetLongPrm(INF_LONG_DN))
142 linux_vtable.append(name)
143 temp_windows_vtable.append(name)
145 if offset not in other_linux_vtables:
146 other_linux_vtables[offset] = []
147 temp_other_windows_vtables[offset] = []
148 other_thunk_linux_vtables[offset] = []
150 if "`non-virtual thunk to'" in name:
151 other_linux_vtables[offset].append(name[22:])
152 other_thunk_linux_vtables[offset].append(name[22:])
153 temp_other_windows_vtables[offset].append(name[22:])
155 other_linux_vtables[offset].append(name)
156 temp_other_windows_vtables[offset].append(name)
158 # MI entry, strip "`non-virtual thunk to'" and remove from list
159 # But not if it's a dtor... what the hell is this.
160 if "`non-virtual thunk to'" in name and "::~" not in name:
162 #print "Stripping '%s' from windows vtable." % (name)
163 temp_windows_vtable.remove(name)
167 for i, v in enumerate(temp_windows_vtable):
169 #print "Found destructor at index %d: %s" % (i, v)
170 del temp_windows_vtable[i]
177 for v in temp_windows_vtable:
178 function = v.split("(", 1)[0]
180 if function == prev_function:
181 # If we don't have a stack, we need to push the last function on first
182 if len(overload_stack) == 0:
183 # We will have added this in the previous run, remove it again...
185 #print "Storing '%s' (!)" % (prev_symbol)
186 overload_stack.append(prev_symbol)
187 #print "Storing '%s'" % (v)
188 overload_stack.append(v)
190 # If we've moved onto something new, dump the stack first
191 while len(overload_stack) > 0:
192 windows_vtable.append(overload_stack.pop())
194 windows_vtable.append(v)
196 prev_function = function
199 # If there is anything left in the stack, dump it
200 while len(overload_stack) > 0:
201 windows_vtable.append(overload_stack.pop())
203 print "\nVTable for %s: (0, 0)" % (classname)
204 print "Lin Win Function"
205 for i, v in enumerate(linux_vtable):
206 winindex = windows_vtable.index(v) if v in windows_vtable else None
207 if winindex is not None:
208 print "%3d %3d %s" % (i, winindex, v)
210 print "%3d %s" % (i, v)
212 for k in temp_other_windows_vtables:
213 for i, v in enumerate(temp_other_windows_vtables[k]):
214 if v.find("::~") != -1:
215 #print "Found destructor at index %d: %s" % (i, v)
216 del temp_other_windows_vtables[k][i]
219 other_windows_vtables = {}
220 for k in temp_other_windows_vtables:
221 other_windows_vtables[k] = []
225 for v in temp_other_windows_vtables[k]:
226 function = v.split("(", 1)[0]
227 if function == prev_function:
228 if len(overload_stack) == 0:
229 other_windows_vtables[k].pop()
230 overload_stack.append(prev_symbol)
231 overload_stack.append(v)
233 if len(overload_stack) > 0:
234 while len(overload_stack) > 0:
235 other_windows_vtables[k].append(overload_stack.pop())
236 other_windows_vtables[k].append(v)
237 prev_function = function
240 for k in other_linux_vtables:
241 print "\nVTable for %s: (%d, %d)" % (offsetdata[k], offsetdata.keys().index(k) + 1, k)
242 print "Lin Win Function"
243 for i, v in enumerate(other_linux_vtables[k]):
244 winindex = other_windows_vtables[k].index(v)
245 if v not in other_thunk_linux_vtables[k]:
246 print "%3d %3d %s" % (i, winindex, v)
248 print "T%2d %3d %s" % (i, winindex, v)
250 SetStatus(IDA_STATUS_READY)
252 if __name__ == '__main__':