"""position_host_v2.py -- Position is 72 bytes (not 96). For each Position vt hit: - Detect array adjacency at stride 72. - Look at offsets -56..-4 for host vtables. - Bucket: (array-element, ppv-wrapper, host-embedded, solo). """ import ctypes, ctypes.wintypes as wt, struct, sys from collections import Counter POSITION_VT = 0x00797910 SIZEOF_POSITION = 72 # vt + objcell_id + Frame(64) ACMIN, ACMAX = 0x00400000, 0x00900000 PROCESS_VM_READ = 0x10 PROCESS_QUERY_INFORMATION = 0x400 class MBI(ctypes.Structure): _fields_ = [('BaseAddress', ctypes.c_void_p), ('AllocationBase', ctypes.c_void_p), ('AllocationProtect', wt.DWORD), ('PartitionId', wt.WORD), ('RegionSize', ctypes.c_size_t), ('State', wt.DWORD), ('Protect', wt.DWORD), ('Type', wt.DWORD)] 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 k.VirtualQueryEx.argtypes = [wt.HANDLE, ctypes.c_void_p, ctypes.POINTER(MBI), ctypes.c_size_t] k.VirtualQueryEx.restype = ctypes.c_size_t def classify(data, off, pos_set): # array adjacency at stride 72 if off >= SIZEOF_POSITION and (off - SIZEOF_POSITION) in pos_set: return ('array_inner', None) if (off + SIZEOF_POSITION) in pos_set: return ('array_head', None) # Look for any acclient code-range vtable in -56..-4 candidate_offs = list(range(-56, 0, 4)) found = None for d in candidate_offs: if off + d < 0: continue v = struct.unpack_from(" count total = 0 mbi = MBI(); addr = 0 while k.VirtualQueryEx(h, addr, ctypes.byref(mbi), ctypes.sizeof(mbi)): pr = mbi.Protect & 0xff if (mbi.State == 0x1000 and mbi.Type == 0x20000 and pr in (0x04, 0x40)): buf = (ctypes.c_ubyte * mbi.RegionSize)() sz = ctypes.c_size_t(0) if k.ReadProcessMemory(h, mbi.BaseAddress, buf, mbi.RegionSize, ctypes.byref(sz)): data = bytes(buf[:sz.value]) end = (len(data) // 4) * 4 pos_offs = [] for off in range(0, end, 4): if struct.unpack_from("= 0x80000000: break print(f"PID {pid}: {total} Position instances") for b, n in buckets.most_common(): pct = 100.0 * n / total print(f" {b:>15}: {n:>7} ({pct:5.1f}%)") print() print("Top (offset, host-vtable) combos for 'embedded' bucket:") for (off, vt), n in host_at_off.most_common(20): pct = 100.0 * n / total print(f" off={off:+4d} vt=0x{vt:08x} count={n:>6} ({pct:.2f}%)") k.CloseHandle(h) return 0 if __name__ == "__main__": sys.exit(scan(int(sys.argv[1])))