Created
September 27, 2019 00:45
-
-
Save Andoryuuta/2098f5d6cb6b4272c153c81b7b29c374 to your computer and use it in GitHub Desktop.
cwb_markers.py v0.1.0
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import idautils | |
import idc | |
import ida_bytes | |
from pprint import pprint, pformat | |
def demangle_name(name): | |
return idc.Demangle(name, idc.GetLongPrm(idc.INF_SHORT_DN)) or name | |
def get_primary_vtables(): | |
vtables = {} | |
for ea, name in idautils.Names(): | |
dname = idc.Demangle(name, idc.GetLongPrm(idc.INF_SHORT_DN)) | |
if dname != None and 'table' in dname: | |
if not name.endswith('_0'): # Skip secondary vtables with the _0 suffix. | |
vtables[dname] = ea | |
return vtables | |
def get_free_size_arg_from_vtable_ea(vtable_ea, typename): | |
# Read the dtor from vtable[0]. | |
dtor_ea = ida_bytes.get_qword(vtable_ea) | |
cur_ea = dtor_ea | |
# Search forwards for a call to free. | |
found_free_call = False | |
func_end_ea = idc.GetFunctionAttr(dtor_ea, idc.FUNCATTR_END) | |
while cur_ea < func_end_ea: | |
cur_ea = idc.NextHead(cur_ea) | |
print(cur_ea) | |
if idc.GetMnem(cur_ea) == "call": | |
dname = demangle_name(idc.GetOpnd(cur_ea, 0)) | |
if 'free' in dname: | |
found_free_call = True | |
break | |
if not found_free_call: | |
return -1 | |
# Now go back and find the size argument passed in (r||e)dx | |
while cur_ea > dtor_ea: | |
cur_ea = idc.PrevHead(cur_ea) | |
if idc.GetMnem(cur_ea) == "mov" and idc.GetOpnd(cur_ea, 0).endswith('dx'): | |
if idc.GetOpType(cur_ea, 1) == idc.o_imm: | |
return idc.GetOperandValue(cur_ea, 1) | |
else: | |
print("[CWT_MARKERS!] ERROR: got non-immediate operand for `free` argument. Can't parse this.") | |
print("[CWT_MARKERS!] \tAt:{:X}, for dtor: {:X}, type:{}".format(cur_ea, dtor_ea, typename)) | |
return -1 | |
return -1 | |
def main(): | |
type_to_info = {} | |
# Parse out the type name from the vftable symbol. | |
for key, value in get_primary_vtables().iteritems(): | |
typename = key | |
if typename.endswith("::`vftable'"): | |
typename = typename[:-11] | |
if typename.startswith("const "): | |
typename = typename[6:] | |
type_to_info[typename] = {'vtable_ea' :value} | |
# Get the type ctor function by finding the first xref. | |
for typename, fields in type_to_info.iteritems(): | |
for xref in idautils.XrefsTo(fields['vtable_ea'], 0): | |
type_to_info[typename]['ctor_ea'] = idc.GetFunctionAttr(xref.frm, idc.FUNCATTR_START) | |
break | |
# Try to get class size via `free` in vtable[0] dtor. | |
for typename, fields in type_to_info.iteritems(): | |
type_to_info[typename]['size'] = get_free_size_arg_from_vtable_ea(fields['vtable_ea'], typename) | |
# Print the classes and their sizes | |
for typename in sorted(type_to_info): | |
fields = type_to_info[typename] | |
if typename.startswith("cube") or typename.startswith("plasma") or typename.startswith("gfx"): | |
name_pad = ' ' * (70 - len(typename)) | |
print("Class: {}, {} Size: 0x{:X}".format(typename, name_pad, fields['size'])) | |
#pprint(type_to_info) | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment