import ctypes, win32gui, commctrl, sys, struct, psutil
from ctypes import wintypes
sys.stdout.reconfigure(encoding='utf-8')

user32 = ctypes.windll.user32
kernel32 = ctypes.windll.kernel32

# Check SeDebugPrivilege
advapi32 = ctypes.windll.advapi32
def enable_debug_privilege():
    hToken = wintypes.HANDLE()
    if advapi32.OpenProcessToken(kernel32.GetCurrentProcess(), 0x0020 | 0x0008, ctypes.byref(hToken)):
        luid = wintypes.LARGE_INTEGER()
        if advapi32.LookupPrivilegeValueW(None, "SeDebugPrivilege", ctypes.byref(luid)):
            class TOKEN_PRIVILEGES(ctypes.Structure):
                _fields_ = [('PrivilegeCount', wintypes.DWORD), ('Luid', wintypes.LARGE_INTEGER), ('Attributes', wintypes.DWORD)]
            tp = TOKEN_PRIVILEGES(1, luid, 0x00000002)
            advapi32.AdjustTokenPrivileges(hToken, False, ctypes.byref(tp), ctypes.sizeof(tp), None, None)
        kernel32.CloseHandle(hToken)

enable_debug_privilege()

# 1. Read AutoVLBS ListView
list_hwnd = 0x541030
pid = wintypes.DWORD()
user32.GetWindowThreadProcessId(list_hwnd, ctypes.byref(pid))
print(f"AutoVLBS List HWND: {hex(list_hwnd)}, PID: {pid.value}")

hProc = kernel32.OpenProcess(0x1F0FFF, False, pid.value)
print(f"OpenProcess AutoVLBS: {hProc}")

if hProc:
    count = user32.SendMessageW(list_hwnd, commctrl.LVM_GETITEMCOUNT, 0, 0)
    print(f"AutoVLBS Item count: {count}")
    remote_buf = kernel32.VirtualAllocEx(hProc, None, 4096, 0x1000 | 0x2000, 0x04)
    remote_text = remote_buf + 512
    
    for i in range(count):
        row = []
        for col in range(3):
            # LVITEM 32-bit: mask (4), iItem (4), iSubItem (4), state (4), stateMask (4), pszText (4), cchTextMax (4), iImage (4), lParam (4)
            lvitem = struct.pack('<IIIIIIII', 0x0001, i, col, 0, 0, remote_text, 256, 0)
            kernel32.WriteProcessMemory(hProc, remote_buf, lvitem, len(lvitem), None)
            res = user32.SendMessageW(list_hwnd, commctrl.LVM_GETITEMTEXTW, i, remote_buf)
            text_buf = ctypes.create_unicode_buffer(256)
            kernel32.ReadProcessMemory(hProc, remote_text, text_buf, 512, None)
            row.append(text_buf.value)
        print(f"  Row {i}: {row}")
        
    kernel32.VirtualFreeEx(hProc, remote_buf, 0, 0x8000)
    kernel32.CloseHandle(hProc)

# 2. Check memory of all vggame.exe instances for character names
char_names = [b'BoCuaCacCon', b'ConCuaBoMe', b'ConGailCung', b'MeCuaCacCon']
vggame_pids = [p.pid for p in psutil.process_iter(['pid', 'name']) if 'vggame' in (p.info['name'] or '').lower()]
print(f"\nScanning vggame.exe PIDs: {vggame_pids}")

class MEMORY_BASIC_INFORMATION(ctypes.Structure):
    _fields_ = [
        ('BaseAddress', ctypes.c_void_p),
        ('AllocationBase', ctypes.c_void_p),
        ('AllocationProtect', wintypes.DWORD),
        ('RegionSize', ctypes.c_size_t),
        ('State', wintypes.DWORD),
        ('Protect', wintypes.DWORD),
        ('Type', wintypes.DWORD),
    ]

for g_pid in vggame_pids:
    hGame = kernel32.OpenProcess(0x0010 | 0x0400, False, g_pid)
    if not hGame:
        print(f"Cannot open PID {g_pid}")
        continue
    mbi = MEMORY_BASIC_INFORMATION()
    addr = 0
    found_for_pid = []
    while kernel32.VirtualQueryEx(hGame, ctypes.c_void_p(addr), ctypes.byref(mbi), ctypes.sizeof(mbi)):
        if mbi.State == 0x1000 and (mbi.Protect & 0xEE) != 0 and (mbi.Protect & 0x01) == 0:
            size = min(mbi.RegionSize, 1024 * 1024 * 4)
            buf = (ctypes.c_char * size)()
            bytesRead = ctypes.c_size_t()
            if kernel32.ReadProcessMemory(hGame, ctypes.c_void_p(addr), buf, size, ctypes.byref(bytesRead)):
                chunk = bytes(buf)[:bytesRead.value]
                for name in char_names:
                    if name in chunk and name not in found_for_pid:
                        found_for_pid.append(name.decode())
        addr += mbi.RegionSize
        if addr >= 0x7FFFFFFF: break
    kernel32.CloseHandle(hGame)
    print(f"PID {g_pid} -> Detected Characters: {found_for_pid}")
