|
asherkin@3590
|
1 |
"""vtable_dump.py: IDAPython script to dump a linux vtable (and a reconstructed windows one) from a binary.""" |
|
asherkin@3590
|
2 |
|
|
asherkin@3590
|
3 |
""" |
|
asherkin@3590
|
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. |
|
asherkin@3590
|
5 |
|
|
asherkin@3590
|
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: |
|
asherkin@3590
|
7 |
|
|
asherkin@3590
|
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. |
|
asherkin@3590
|
9 |
|
|
asherkin@3590
|
10 |
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. |
|
asherkin@3590
|
11 |
|
|
asherkin@3590
|
12 |
3. This notice may not be removed or altered from any source distribution. |
|
asherkin@3590
|
13 |
""" |
|
asherkin@3590
|
14 |
|
|
asherkin@3590
|
15 |
__author__ = "Asher Baker" |
|
asherkin@3590
|
16 |
__copyright__ = "Copyright 2012, Asher Baker" |
|
asherkin@3590
|
17 |
__license__ = "zlib/libpng" |
|
asherkin@3590
|
18 |
|
|
asherkin@3590
|
19 |
import re |
|
asherkin@3590
|
20 |
|
|
asherkin@3599
|
21 |
""" |
|
asherkin@3599
|
22 |
Output Format:" |
|
asherkin@3599
|
23 |
VTable for <1>: (<2>, <3>) |
|
asherkin@3599
|
24 |
Lin Win Function |
|
asherkin@3599
|
25 |
<4><5> <6> <7> |
|
asherkin@3599
|
26 |
<4><5> <6> <7> |
|
asherkin@3599
|
27 |
... |
|
asherkin@3599
|
28 |
<4><5> <6> <7> |
|
asherkin@3599
|
29 |
|
|
asherkin@3599
|
30 |
1: Classname |
|
asherkin@3599
|
31 |
2: VTable Offset |
|
asherkin@3599
|
32 |
3: Linux This Pointer Offset |
|
asherkin@3599
|
33 |
4: "T" if the function is a MI thunk |
|
asherkin@3599
|
34 |
5: Linux VTable Index |
|
asherkin@3599
|
35 |
6: Windows VTable Index |
|
asherkin@3599
|
36 |
7: Function Signature |
|
asherkin@3599
|
37 |
""" |
|
asherkin@3599
|
38 |
|
|
asherkin@3599
|
39 |
catchclass = False |
|
asherkin@3599
|
40 |
innerclass = "" |
|
asherkin@3599
|
41 |
|
|
asherkin@3599
|
42 |
classname = None |
|
asherkin@3599
|
43 |
offsetdata = {} |
|
asherkin@3599
|
44 |
|
|
asherkin@3599
|
45 |
def ExtractTypeInfo(ea, level = 0): |
|
asherkin@3599
|
46 |
global catchclass |
|
asherkin@3599
|
47 |
global innerclass |
|
asherkin@3599
|
48 |
global classname |
|
asherkin@3599
|
49 |
global offsetdata |
|
asherkin@3599
|
50 |
|
|
asherkin@3599
|
51 |
# Param needed to support old IDAPython versions |
|
asherkin@3604
|
52 |
end = NextNotTail(ea) |
|
asherkin@3599
|
53 |
|
|
asherkin@3599
|
54 |
# Skip vtable |
|
asherkin@3599
|
55 |
ea += 4 |
|
asherkin@3599
|
56 |
|
|
asherkin@3599
|
57 |
# Get type name |
|
asherkin@3599
|
58 |
name = Demangle("_Z" + GetString(Dword(ea)), GetLongPrm(INF_LONG_DN)) |
|
asherkin@3599
|
59 |
ea += 4 |
|
asherkin@3599
|
60 |
|
|
asherkin@3599
|
61 |
if classname is None and level == 0: |
|
asherkin@3599
|
62 |
classname = name |
|
asherkin@3599
|
63 |
|
|
asherkin@3599
|
64 |
if catchclass: |
|
asherkin@3599
|
65 |
innerclass = name |
|
asherkin@3599
|
66 |
catchclass = False |
|
asherkin@3599
|
67 |
|
|
asherkin@3604
|
68 |
print " %*s%s" % (level, "", name) |
|
asherkin@3599
|
69 |
|
|
asherkin@3599
|
70 |
if not ea < end: # Base Type |
|
asherkin@3599
|
71 |
pass |
|
asherkin@3599
|
72 |
elif isData(GetFlags(Dword(ea))): # Single Inheritance |
|
asherkin@3599
|
73 |
ExtractTypeInfo(Dword(ea), level + 1) |
|
asherkin@3599
|
74 |
ea += 4 |
|
asherkin@3599
|
75 |
else: # Multiple Inheritance |
|
asherkin@3599
|
76 |
ea += 8 |
|
asherkin@3599
|
77 |
while ea < end: |
|
asherkin@3599
|
78 |
catchclass = True |
|
asherkin@3599
|
79 |
ExtractTypeInfo(Dword(ea), level + 1) |
|
asherkin@3599
|
80 |
ea += 4 |
|
asherkin@3599
|
81 |
offset = Dword(ea) |
|
asherkin@3599
|
82 |
ea += 4 |
|
asherkin@3599
|
83 |
#print "%*s Offset: 0x%06X" % (level, "", offset >> 8) |
|
asherkin@3599
|
84 |
if (offset >> 8) != 0: |
|
asherkin@3599
|
85 |
offsetdata[offset >> 8] = innerclass |
|
asherkin@3599
|
86 |
|
|
asherkin@3599
|
87 |
# Source: http://stackoverflow.com/a/9147327 |
|
asherkin@3599
|
88 |
def twos_comp(val, bits): |
|
asherkin@3599
|
89 |
"""compute the 2's compliment of int value val""" |
|
asherkin@3599
|
90 |
if (val & (1 << (bits - 1))) != 0: |
|
asherkin@3599
|
91 |
val = val - (1 << bits) |
|
asherkin@3599
|
92 |
return val |
|
asherkin@3599
|
93 |
|
|
asherkin@3590
|
94 |
def Analyze(): |
|
asherkin@3590
|
95 |
SetStatus(IDA_STATUS_WORK) |
|
asherkin@3590
|
96 |
|
|
asherkin@3590
|
97 |
if GetLongPrm(INF_COMPILER).id != COMP_GNU: |
|
asherkin@3590
|
98 |
Warning("This script is for binaries compiled with GCC only.") |
|
asherkin@3590
|
99 |
SetStatus(IDA_STATUS_READY) |
|
asherkin@3590
|
100 |
return |
|
asherkin@3590
|
101 |
|
|
asherkin@3590
|
102 |
ea = ScreenEA() |
|
asherkin@3590
|
103 |
|
|
asherkin@3590
|
104 |
if not isHead(GetFlags(ea)): |
|
asherkin@3599
|
105 |
# Param needed to support old IDAPython versions |
|
asherkin@3599
|
106 |
ea = PrevHead(ea, 0) |
|
asherkin@3590
|
107 |
|
|
asherkin@3604
|
108 |
end = NextNotTail(ea) |
|
asherkin@3590
|
109 |
|
|
asherkin@3590
|
110 |
name = Demangle(Name(ea), GetLongPrm(INF_LONG_DN)) |
|
asherkin@3590
|
111 |
if ea == BADADDR or name is None or not re.search(r"vf?table(?: |'\{)for", name): |
|
asherkin@3590
|
112 |
Warning("No vtable selected!\nSelect vtable block first.") |
|
asherkin@3590
|
113 |
SetStatus(IDA_STATUS_READY) |
|
asherkin@3590
|
114 |
return |
|
asherkin@3590
|
115 |
|
|
asherkin@3590
|
116 |
linux_vtable = [] |
|
asherkin@3590
|
117 |
temp_windows_vtable = [] |
|
asherkin@3590
|
118 |
|
|
asherkin@3599
|
119 |
other_linux_vtables = {} |
|
asherkin@3599
|
120 |
other_thunk_linux_vtables = {} |
|
asherkin@3599
|
121 |
temp_other_windows_vtables = {} |
|
asherkin@3599
|
122 |
|
|
asherkin@3590
|
123 |
# Extract vtable |
|
asherkin@3590
|
124 |
while ea < end: |
|
asherkin@3599
|
125 |
# Read thisoffs |
|
asherkin@3599
|
126 |
offset = -twos_comp(Dword(ea), 32) |
|
asherkin@3599
|
127 |
ea += 4 |
|
asherkin@3590
|
128 |
|
|
asherkin@3599
|
129 |
# Read typeinfo address |
|
asherkin@3599
|
130 |
typeinfo = Dword(ea) |
|
asherkin@3599
|
131 |
ea += 4 |
|
asherkin@3590
|
132 |
|
|
asherkin@3599
|
133 |
if offset == 0: # We only need to read this once |
|
asherkin@3599
|
134 |
print "Inheritance Tree:" |
|
asherkin@3599
|
135 |
ExtractTypeInfo(typeinfo) |
|
asherkin@3590
|
136 |
|
|
asherkin@3590
|
137 |
while ea < end and isCode(GetFlags(Dword(ea))): |
|
asherkin@3604
|
138 |
name = Name(Dword(ea)) |
|
asherkin@3604
|
139 |
demangled = Demangle(name, GetLongPrm(INF_LONG_DN)) |
|
asherkin@3604
|
140 |
#print "Name: %s, Demangled: %s" % (name, demangled) |
|
asherkin@3604
|
141 |
|
|
asherkin@3604
|
142 |
name = demangled if demangled else name |
|
asherkin@3590
|
143 |
|
|
asherkin@3590
|
144 |
if offset == 0: |
|
asherkin@3590
|
145 |
linux_vtable.append(name) |
|
asherkin@3590
|
146 |
temp_windows_vtable.append(name) |
|
asherkin@3590
|
147 |
else: |
|
asherkin@3599
|
148 |
if offset not in other_linux_vtables: |
|
asherkin@3599
|
149 |
other_linux_vtables[offset] = [] |
|
asherkin@3599
|
150 |
temp_other_windows_vtables[offset] = [] |
|
asherkin@3599
|
151 |
other_thunk_linux_vtables[offset] = [] |
|
asherkin@3599
|
152 |
|
|
asherkin@3599
|
153 |
if "`non-virtual thunk to'" in name: |
|
asherkin@3599
|
154 |
other_linux_vtables[offset].append(name[22:]) |
|
asherkin@3599
|
155 |
other_thunk_linux_vtables[offset].append(name[22:]) |
|
asherkin@3599
|
156 |
temp_other_windows_vtables[offset].append(name[22:]) |
|
asherkin@3599
|
157 |
else: |
|
asherkin@3599
|
158 |
other_linux_vtables[offset].append(name) |
|
asherkin@3599
|
159 |
temp_other_windows_vtables[offset].append(name) |
|
asherkin@3599
|
160 |
|
|
asherkin@3590
|
161 |
# MI entry, strip "`non-virtual thunk to'" and remove from list |
|
asherkin@3590
|
162 |
# But not if it's a dtor... what the hell is this. |
|
asherkin@3599
|
163 |
if "`non-virtual thunk to'" in name and "::~" not in name: |
|
asherkin@3590
|
164 |
name = name[22:] |
|
asherkin@3590
|
165 |
#print "Stripping '%s' from windows vtable." % (name) |
|
asherkin@3590
|
166 |
temp_windows_vtable.remove(name) |
|
asherkin@3590
|
167 |
|
|
asherkin@3590
|
168 |
ea += 4 |
|
asherkin@3590
|
169 |
|
|
asherkin@3590
|
170 |
for i, v in enumerate(temp_windows_vtable): |
|
asherkin@3599
|
171 |
if "::~" in v: |
|
asherkin@3590
|
172 |
#print "Found destructor at index %d: %s" % (i, v) |
|
asherkin@3590
|
173 |
del temp_windows_vtable[i] |
|
asherkin@3590
|
174 |
break |
|
asherkin@3590
|
175 |
|
|
asherkin@3590
|
176 |
windows_vtable = [] |
|
asherkin@3590
|
177 |
overload_stack = [] |
|
asherkin@3590
|
178 |
prev_function = "" |
|
asherkin@3590
|
179 |
prev_symbol = "" |
|
asherkin@3590
|
180 |
for v in temp_windows_vtable: |
|
asherkin@3590
|
181 |
function = v.split("(", 1)[0] |
|
asherkin@3590
|
182 |
|
|
asherkin@3590
|
183 |
if function == prev_function: |
|
asherkin@3590
|
184 |
# If we don't have a stack, we need to push the last function on first |
|
asherkin@3590
|
185 |
if len(overload_stack) == 0: |
|
asherkin@3590
|
186 |
# We will have added this in the previous run, remove it again... |
|
asherkin@3590
|
187 |
windows_vtable.pop() |
|
asherkin@3590
|
188 |
#print "Storing '%s' (!)" % (prev_symbol) |
|
asherkin@3590
|
189 |
overload_stack.append(prev_symbol) |
|
asherkin@3590
|
190 |
#print "Storing '%s'" % (v) |
|
asherkin@3590
|
191 |
overload_stack.append(v) |
|
asherkin@3590
|
192 |
else: |
|
asherkin@3590
|
193 |
# If we've moved onto something new, dump the stack first |
|
asherkin@3599
|
194 |
while len(overload_stack) > 0: |
|
asherkin@3599
|
195 |
windows_vtable.append(overload_stack.pop()) |
|
asherkin@3590
|
196 |
|
|
asherkin@3590
|
197 |
windows_vtable.append(v) |
|
asherkin@3590
|
198 |
|
|
asherkin@3590
|
199 |
prev_function = function |
|
asherkin@3590
|
200 |
prev_symbol = v |
|
asherkin@3590
|
201 |
|
|
asherkin@3599
|
202 |
# If there is anything left in the stack, dump it |
|
asherkin@3599
|
203 |
while len(overload_stack) > 0: |
|
asherkin@3599
|
204 |
windows_vtable.append(overload_stack.pop()) |
|
asherkin@3599
|
205 |
|
|
asherkin@3599
|
206 |
print "\nVTable for %s: (0, 0)" % (classname) |
|
asherkin@3604
|
207 |
print " Lin Win Function" |
|
asherkin@3590
|
208 |
for i, v in enumerate(linux_vtable): |
|
asherkin@3604
|
209 |
if "__cxa_pure_virtual" in v: |
|
asherkin@3604
|
210 |
print "P%3d" % (i) |
|
asherkin@3604
|
211 |
continue |
|
asherkin@3604
|
212 |
|
|
asherkin@3590
|
213 |
winindex = windows_vtable.index(v) if v in windows_vtable else None |
|
asherkin@3590
|
214 |
if winindex is not None: |
|
asherkin@3604
|
215 |
print "%4d %4d %s" % (i, winindex, v) |
|
asherkin@3590
|
216 |
else: |
|
asherkin@3604
|
217 |
print "%4d %s" % (i, v) |
|
asherkin@3590
|
218 |
|
|
asherkin@3599
|
219 |
for k in temp_other_windows_vtables: |
|
asherkin@3599
|
220 |
for i, v in enumerate(temp_other_windows_vtables[k]): |
|
asherkin@3599
|
221 |
if v.find("::~") != -1: |
|
asherkin@3599
|
222 |
#print "Found destructor at index %d: %s" % (i, v) |
|
asherkin@3599
|
223 |
del temp_other_windows_vtables[k][i] |
|
asherkin@3599
|
224 |
break |
|
asherkin@3599
|
225 |
|
|
asherkin@3599
|
226 |
other_windows_vtables = {} |
|
asherkin@3599
|
227 |
for k in temp_other_windows_vtables: |
|
asherkin@3599
|
228 |
other_windows_vtables[k] = [] |
|
asherkin@3599
|
229 |
overload_stack = [] |
|
asherkin@3599
|
230 |
prev_function = "" |
|
asherkin@3599
|
231 |
prev_symbol = "" |
|
asherkin@3599
|
232 |
for v in temp_other_windows_vtables[k]: |
|
asherkin@3599
|
233 |
function = v.split("(", 1)[0] |
|
asherkin@3599
|
234 |
if function == prev_function: |
|
asherkin@3599
|
235 |
if len(overload_stack) == 0: |
|
asherkin@3599
|
236 |
other_windows_vtables[k].pop() |
|
asherkin@3604
|
237 |
#print "Storing '%s' (!)" % (prev_symbol) |
|
asherkin@3599
|
238 |
overload_stack.append(prev_symbol) |
|
asherkin@3604
|
239 |
#print "Storing '%s'" % (v) |
|
asherkin@3599
|
240 |
overload_stack.append(v) |
|
asherkin@3599
|
241 |
else: |
|
asherkin@3599
|
242 |
if len(overload_stack) > 0: |
|
asherkin@3599
|
243 |
while len(overload_stack) > 0: |
|
asherkin@3599
|
244 |
other_windows_vtables[k].append(overload_stack.pop()) |
|
asherkin@3599
|
245 |
other_windows_vtables[k].append(v) |
|
asherkin@3599
|
246 |
prev_function = function |
|
asherkin@3599
|
247 |
prev_symbol = v |
|
asherkin@3599
|
248 |
|
|
asherkin@3599
|
249 |
for k in other_linux_vtables: |
|
asherkin@3599
|
250 |
print "\nVTable for %s: (%d, %d)" % (offsetdata[k], offsetdata.keys().index(k) + 1, k) |
|
asherkin@3604
|
251 |
print " Lin Win Function" |
|
asherkin@3599
|
252 |
for i, v in enumerate(other_linux_vtables[k]): |
|
asherkin@3604
|
253 |
if "__cxa_pure_virtual" in v: |
|
asherkin@3604
|
254 |
print "P%3d" % (i) |
|
asherkin@3604
|
255 |
continue |
|
asherkin@3604
|
256 |
|
|
asherkin@3599
|
257 |
winindex = other_windows_vtables[k].index(v) |
|
asherkin@3599
|
258 |
if v not in other_thunk_linux_vtables[k]: |
|
asherkin@3604
|
259 |
print "%4d %4d %s" % (i, winindex, v) |
|
asherkin@3599
|
260 |
else: |
|
asherkin@3604
|
261 |
print "T%3d %4d %s" % (i, winindex, v) |
|
asherkin@3599
|
262 |
|
|
asherkin@3590
|
263 |
SetStatus(IDA_STATUS_READY) |
|
asherkin@3590
|
264 |
|
|
asherkin@3590
|
265 |
if __name__ == '__main__': |
|
asherkin@3599
|
266 |
Analyze() |