import ctypes
import struct

PROCESS_ALL_ACCESS = 0x1F0FFF
kernel32 = ctypes.windll.kernel32
h_proc = kernel32.OpenProcess(PROCESS_ALL_ACCESS, False, 43084)

def rpm(addr, size):
    buf = ctypes.create_string_buffer(size)
    bytes_read = ctypes.c_size_t(0)
    if kernel32.ReadProcessMemory(h_proc, ctypes.c_void_p(addr), buf, size, ctypes.byref(bytes_read)):
        return buf.raw
    return None

def rpm_u64(addr):
    d = rpm(addr, 8)
    return struct.unpack("<Q", d)[0] if d else 0

def rpm_i32(addr):
    d = rpm(addr, 4)
    return struct.unpack("<i", d)[0] if d else 0

mod_base = 0x7FF6AFC20000

def get_class_name(vtable):
    # vtable - 8 -> RTTICompleteObjectLocator
    col = rpm_u64(vtable - 8)
    if not col:
        return "No COL"
    # COL layout:
    # +0x00: signature (0 for 32-bit, 1 for 64-bit)
    # +0x04: offset
    # +0x08: cdOffset
    # +0x0C: typeDescriptor RVA
    # +0x10: classDescriptor RVA
    # +0x14: objectBase RVA
    td_rva = rpm_i32(col + 12)
    obj_base = rpm_u64(col + 20) # or col + 16/20
    td_addr = mod_base + td_rva
    # TypeDescriptor layout:
    # +0x00: pVFTable
    # +0x08: spare
    # +0x10: name (null-terminated string)
    name_bytes = rpm(td_addr + 16, 128)
    if name_bytes:
        s = name_bytes.split(b"\x00")[0].decode("ascii", errors="ignore")
        return s
    return "Unknown"

igs = rpm_u64(mod_base + 0x4430108)
area = rpm_u64(igs + 0x290)
print(f"AreaInstance: {hex(area)}")

for off in range(0, 0x1200, 8):
    ptr = rpm_u64(area + off)
    if 0x20000000000 <= ptr <= 0x30000000000:
        vt = rpm_u64(ptr)
        if mod_base <= vt <= mod_base + 0x4000000:
            cname = get_class_name(vt)
            print(f"Area + {hex(off)}: {cname} (ptr={hex(ptr)})")

kernel32.CloseHandle(h_proc)
