import ctypes
from ctypes import wintypes
import struct

PROCESS_VM_READ = 0x0010
PROCESS_QUERY_INFORMATION = 0x0400

kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)
OpenProcess = kernel32.OpenProcess
OpenProcess.restype = wintypes.HANDLE
OpenProcess.argtypes = [wintypes.DWORD, wintypes.BOOL, wintypes.DWORD]

ReadProcessMemory = kernel32.ReadProcessMemory
ReadProcessMemory.restype = wintypes.BOOL
ReadProcessMemory.argtypes = [wintypes.HANDLE, wintypes.LPCVOID, wintypes.LPVOID, ctypes.c_size_t, ctypes.POINTER(ctypes.c_size_t)]

CloseHandle = kernel32.CloseHandle
CloseHandle.restype = wintypes.BOOL
CloseHandle.argtypes = [wintypes.HANDLE]

pid = 39484
h_proc = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, False, pid)

base = 0x7FF6AFC20000
text_start = base + 0x1000
text_size = 0x2E85000  # ~48 MB

targets = {
    0x00007FF6B2D88E10: "MinimapTilesRender",
    0x00007FF6B2DA9200: "Minimap_Visibility",
    0x00007FF6B2DA9240: "explored_tile",
    0x00007FF6B2DAD670: "FogOfWar",
    0x00007FF6B31CD070: "FogOfWar_2",
    0x00007FF6B31CD2B8: "FogOfWarAdd",
}

print("Scanning .text for xrefs to target strings...")
chunk_size = 4 * 1024 * 1024
buf = ctypes.create_string_buffer(chunk_size)
bytes_read = ctypes.c_size_t(0)

hits = []
for offset in range(0, text_size, chunk_size - 16):
    cur_addr = text_start + offset
    n = min(chunk_size, text_size - offset)
    if ReadProcessMemory(h_proc, ctypes.c_void_p(cur_addr), buf, n, ctypes.byref(bytes_read)):
        data = bytes(buf)[:bytes_read.value]
        # Search for 4-byte displacement
        # In x86_64: lea reg, [rip + disp32] -> opcode 48 8D ?? disp32
        # Target = rip_next + disp32 = (cur_addr + i + 4) + disp32 (or similar)
        # We check for disp32 at i: target - (cur_addr + i + 4) == disp32
        for target_addr, name in targets.items():
            for i in range(0, len(data) - 4, 1):
                # Check 7-byte instruction (e.g. 48 8d 0d disp32) -> rip_next = cur_addr + i + 4
                rip_next = cur_addr + i + 4
                expected_disp = target_addr - rip_next
                if -0x7FFFFFFF <= expected_disp <= 0x7FFFFFFF:
                    disp = struct.unpack('<i', data[i:i+4])[0]
                    if disp == expected_disp:
                        hits.append((cur_addr + i, name, target_addr))

print(f"Found {len(hits)} xrefs:")
for addr, name, target in hits:
    print(f"  0x{addr:016X} -> {name} (0x{target:016X})")

CloseHandle(h_proc)
