"""identify_holders_by_module.py For all 260KB private RW regions, scan ALL readable memory in the process for pointers, then group hits by owning module (DLL/exe). Reveals which DLL maintains a cache pointing at these chunks.""" import ctypes, ctypes.wintypes as wt, sys, struct from collections import defaultdict k = ctypes.windll.kernel32 psapi = ctypes.windll.psapi k.OpenProcess.argtypes = [wt.DWORD, wt.BOOL, wt.DWORD]; k.OpenProcess.restype = wt.HANDLE 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, wt.LPCVOID, ctypes.c_void_p, ctypes.c_size_t] k.VirtualQueryEx.restype = ctypes.c_size_t class MBI(ctypes.Structure): _fields_ = [("BaseAddress", ctypes.c_void_p), ("AllocationBase", ctypes.c_void_p), ("AllocationProtect", wt.DWORD), ("RegionSize", ctypes.c_size_t), ("State", wt.DWORD), ("Protect", wt.DWORD), ("Type", wt.DWORD)] class MODULEINFO(ctypes.Structure): _fields_ = [("lpBaseOfDll", ctypes.c_void_p), ("SizeOfImage", wt.DWORD), ("EntryPoint", ctypes.c_void_p)] psapi.EnumProcessModulesEx.argtypes = [wt.HANDLE, ctypes.POINTER(wt.HMODULE), wt.DWORD, ctypes.POINTER(wt.DWORD), wt.DWORD] psapi.GetModuleFileNameExA.argtypes = [wt.HANDLE, wt.HMODULE, ctypes.c_char_p, wt.DWORD] psapi.GetModuleInformation.argtypes = [wt.HANDLE, wt.HMODULE, ctypes.POINTER(MODULEINFO), wt.DWORD] def rd(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]) pid = int(sys.argv[1]) h = k.OpenProcess(0x410, False, pid) if not h: print("OpenProcess fail"); sys.exit(2) # Enumerate modules modules = [] # list of (base, end, name) needed = wt.DWORD(0) hmods = (wt.HMODULE * 1024)() if psapi.EnumProcessModulesEx(h, hmods, ctypes.sizeof(hmods), ctypes.byref(needed), 0x03): n = needed.value // ctypes.sizeof(wt.HMODULE) name = ctypes.create_string_buffer(260) info = MODULEINFO() for i in range(n): psapi.GetModuleFileNameExA(h, hmods[i], name, 260) nm = name.value.decode(errors='replace') nm_short = nm.split('\\')[-1].split('/')[-1].lower() if psapi.GetModuleInformation(h, hmods[i], ctypes.byref(info), ctypes.sizeof(info)): modules.append((info.lpBaseOfDll, info.lpBaseOfDll + info.SizeOfImage, nm_short)) modules.sort() print(f"Found {len(modules)} modules") def find_module(va): # Linear scan — module count small for base, end, name in modules: if base <= va < end: return name return "" # Find all 260KB regions candidates = [] mbi = MBI(); addr = 0 while k.VirtualQueryEx(h, addr, ctypes.byref(mbi), ctypes.sizeof(mbi)): base = mbi.BaseAddress or 0 if mbi.State == 0x1000 and mbi.RegionSize == 266240 and (mbi.Type & 0x20000): if (mbi.Protect & 0xFF) in (0x04, 0x40): candidates.append(base) next_addr = base + mbi.RegionSize if next_addr <= addr: break addr = next_addr if addr >= 0x80000000: break print(f"Found {len(candidates)} candidate 260KB regions") target_set = set(candidates) # Walk ALL readable committed memory and find pointers to candidates, # group by owning module holders_by_module = defaultdict(int) holders_by_module_unique = defaultdict(set) # which orphan target each module hits scanned_total = 0 mbi = MBI(); addr = 0 while k.VirtualQueryEx(h, addr, ctypes.byref(mbi), ctypes.sizeof(mbi)): base = mbi.BaseAddress or 0 sz = mbi.RegionSize if mbi.State == 0x1000 and (mbi.Protect & 0xFF) in (0x02, 0x04, 0x20, 0x40): # any readable # Skip huge regions to keep this fast if sz <= 64 * 1024 * 1024: data = rd(h, base, sz) if data: scanned_total += sz # Walk dwords for off in range(0, len(data) - 3, 4): v = int.from_bytes(data[off:off+4], 'little') if v in target_set: holder_va = base + off mod = find_module(holder_va) holders_by_module[mod] += 1 holders_by_module_unique[mod].add(v) next_addr = base + sz if next_addr <= addr: break addr = next_addr if addr >= 0x80000000: break print(f"\nScanned {scanned_total/1024/1024:.0f} MB total") print(f"\nHolders by module (top 20):") sorted_mods = sorted(holders_by_module.items(), key=lambda x: -x[1]) for mod, hits in sorted_mods[:20]: unique = len(holders_by_module_unique[mod]) print(f" {mod:30s} hits={hits:6d} unique_targets={unique}") print(f"\nTotal hits: {sum(holders_by_module.values())}") print(f"Total unique targets: {len(set().union(*holders_by_module_unique.values()))} of {len(candidates)}") k.CloseHandle(h)