import ctypes
import struct

k32 = ctypes.windll.kernel32
pid = 43084
h = k32.OpenProcess(0x10 | 0x400, False, pid)
assert h, "cannot open process"

# Let us scan committed memory regions for UTF-8 and UTF-16 strings
# matching "Hollow" or "Swirling"
addr = 0x10000
mbi = (ctypes.c_byte * 48)()

keywords = [b"Hollow", b"Swirling", b"hollow", b"swirling"]
results = []

while k32.VirtualQueryEx(h, ctypes.c_void_p(addr), mbi, 48):
    base_addr = struct.unpack_from('<Q', bytes(mbi), 0)[0]
    region_size = struct.unpack_from('<Q', bytes(mbi), 24)[0]
    state = struct.unpack_from('<I', bytes(mbi), 32)[0]
    protect = struct.unpack_from('<I', bytes(mbi), 36)[0]

    # MEM_COMMIT = 0x1000, readable protect
    if state == 0x1000 and (protect & 0xEE) and not (protect & 0x101):
        if region_size <= 64 * 1024 * 1024:
            buf = (ctypes.c_char * min(region_size, 4 * 1024 * 1024))()
            br = ctypes.c_size_t()
            for off in range(0, region_size, len(buf)):
                cur_len = min(len(buf), region_size - off)
                if k32.ReadProcessMemory(h, ctypes.c_void_p(base_addr + off), buf, cur_len, ctypes.byref(br)):
                    raw = bytes(buf)[:br.value]
                    for kw in keywords:
                        idx = 0
                        while True:
                            pos = raw.find(kw, idx)
                            if pos == -1: break
                            # inspect string around pos
                            match_addr = base_addr + off + pos
                            sub = raw[max(0, pos-20) : min(len(raw), pos+60)]
                            results.append((match_addr, kw, sub))
                            if len(results) > 100: break
                            idx = pos + len(kw)
                if len(results) > 100: break
    addr = base_addr + region_size
    if len(results) > 100: break

print(f"Found {len(results)} matches:")
for a, kw, s in results[:30]:
    printable = "".join(chr(c) if 0x20 <= c <= 0x7E else "." for c in s)
    print(f"  0x{a:012X} [{kw.decode()}]: {printable}")

k32.CloseHandle(h)
