"""dump_vtable.py [n_slots=16] Read N vtable slots from a live process and dump first 16 bytes of each. Useful for fingerprinting classes — compare slot patterns to known vtables.""" import ctypes, ctypes.wintypes as wt, sys PROCESS_VM_READ = 0x10 PROCESS_QUERY_INFORMATION = 0x400 k = ctypes.windll.kernel32 k.OpenProcess.argtypes = [wt.DWORD, wt.BOOL, wt.DWORD] k.OpenProcess.restype = wt.HANDLE k.CloseHandle.argtypes = [wt.HANDLE] k.CloseHandle.restype = wt.BOOL k.ReadProcessMemory.argtypes = [wt.HANDLE, wt.LPCVOID, wt.LPVOID, ctypes.c_size_t, ctypes.POINTER(ctypes.c_size_t)] k.ReadProcessMemory.restype = wt.BOOL def read(h, va, n): buf = (ctypes.c_ubyte * n)() sz = ctypes.c_size_t(0) if not k.ReadProcessMemory(h, va, buf, n, ctypes.byref(sz)): return None return bytes(buf[:sz.value]) def main(): pid = int(sys.argv[1]) vt_va = int(sys.argv[2], 0) n_slots = int(sys.argv[3]) if len(sys.argv) > 3 else 16 h = k.OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, False, pid) if not h: print(f"OpenProcess pid={pid} err={ctypes.get_last_error()}") sys.exit(2) # Read N slots (4 bytes each) slot_bytes = read(h, vt_va, n_slots * 4) if not slot_bytes: print(f"ReadProcessMemory @ vtable 0x{vt_va:08x} err={ctypes.get_last_error()}") sys.exit(3) print(f"vtable @ 0x{vt_va:08x} in pid {pid}") slots = [] for i in range(n_slots): slot = int.from_bytes(slot_bytes[i*4:i*4+4], 'little') slots.append(slot) for i, slot in enumerate(slots): prologue = read(h, slot, 16) if slot else None if prologue: hexs = prologue.hex(' ') hints = [] if prologue[:1] == b'\x55': hints.append("push ebp") if prologue[:1] == b'\x56': hints.append("push esi") if prologue[:1] == b'\x53': hints.append("push ebx") if prologue[:3] == b'\xb0\x01\xc3': hints.append("mov al,1;ret (return-true stub)") if prologue[:3] == b'\xb0\x00\xc3': hints.append("mov al,0;ret (return-false stub)") if prologue[:3] == b'\x33\xc0\xc3': hints.append("xor eax,eax;ret (return-0 stub)") if prologue[:1] == b'\xc3': hints.append("bare ret (pure no-op)") if prologue[:1] == b'\xc2': hints.append(f"retn imm16") if prologue[:2] == b'\xe9\x00' or prologue[:1] == b'\xe9': hints.append("jmp (thunk)") hint_str = " -- " + "; ".join(hints) if hints else "" print(f" slot[{i:2d}] = 0x{slot:08x} {hexs}{hint_str}") else: print(f" slot[{i:2d}] = 0x{slot:08x} (unreadable)") k.CloseHandle(h) if __name__ == '__main__': main()