""" read_function_bytes.py [bytes] Reads N bytes from a remote process at a given address. Used to inspect what function actually lives at a vtable slot's target. """ import ctypes, ctypes.wintypes as wt, sys PROCESS_VM_READ = 0x0010 PROCESS_QUERY_INFORMATION = 0x0400 k32 = ctypes.windll.kernel32 OpenProcess = k32.OpenProcess OpenProcess.argtypes = [wt.DWORD, wt.BOOL, wt.DWORD]; OpenProcess.restype = wt.HANDLE CloseHandle = k32.CloseHandle CloseHandle.argtypes = [wt.HANDLE]; CloseHandle.restype = wt.BOOL ReadProcessMemory = k32.ReadProcessMemory ReadProcessMemory.argtypes = [wt.HANDLE, wt.LPCVOID, wt.LPVOID, ctypes.c_size_t, ctypes.POINTER(ctypes.c_size_t)] ReadProcessMemory.restype = wt.BOOL pid = int(sys.argv[1]) addr = int(sys.argv[2], 0) n = int(sys.argv[3]) if len(sys.argv) > 3 else 64 h = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, False, pid) if not h: print(f"OpenProcess failed: err={ctypes.get_last_error()}"); sys.exit(2) buf = (ctypes.c_ubyte * n)() sz = ctypes.c_size_t(0) if not ReadProcessMemory(h, addr, buf, n, ctypes.byref(sz)): print(f"ReadProcessMemory failed: err={ctypes.get_last_error()}"); CloseHandle(h); sys.exit(3) print(f"PID {pid} addr 0x{addr:08x} bytes read: {sz.value}") data = bytes(buf[:sz.value]) for i in range(0, len(data), 16): chunk = data[i:i+16] hexs = " ".join(f"{b:02x}" for b in chunk) asci = "".join(chr(b) if 32 <= b < 127 else "." for b in chunk) print(f" 0x{addr+i:08x} {hexs:<47} {asci}") CloseHandle(h)