112 lines
4.6 KiB
Python
112 lines
4.6 KiB
Python
import ctypes, ctypes.wintypes as wt, os, sys, time, subprocess
|
|
|
|
DLL = r"X:\Users\user\fallendoll\dumper\Dumper-7\build2\bin\Dumper-7.dll"
|
|
EXE = r"C:\SteamLibrary\steamapps\common\Operation Lovecraft Fallen Doll Playtest\Paralogue\Binaries\Win64\Paralogue-Win64-Shipping.exe"
|
|
CWD = os.path.dirname(EXE)
|
|
OUT = r"C:\Dumper-7"
|
|
|
|
# Steam appid shim
|
|
appid_file = os.path.join(CWD, "steam_appid.txt")
|
|
if not os.path.exists(appid_file):
|
|
open(appid_file, "w").write("1810920")
|
|
|
|
# Find or spawn game
|
|
def pid_by_name(name):
|
|
import ctypes.wintypes as wt
|
|
k32 = ctypes.windll.kernel32
|
|
TH32CS_SNAPPROCESS = 0x2
|
|
class PE(ctypes.Structure):
|
|
_fields_ = [("dwSize", wt.DWORD), ("cntUsage", wt.DWORD),
|
|
("th32ProcessID", wt.DWORD), ("th32DefaultHeapID", ctypes.c_void_p),
|
|
("th32ModuleID", wt.DWORD), ("cntThreads", wt.DWORD),
|
|
("th32ParentProcessID", wt.DWORD), ("pcPriClassBase", ctypes.c_long),
|
|
("dwFlags", wt.DWORD), ("szExeFile", ctypes.c_wchar*260)]
|
|
snap = k32.CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)
|
|
pe = PE(); pe.dwSize = ctypes.sizeof(PE)
|
|
pids = []
|
|
if k32.Process32FirstW(snap, ctypes.byref(pe)):
|
|
while True:
|
|
if pe.szExeFile.lower() == name.lower():
|
|
pids.append(pe.th32ProcessID)
|
|
if not k32.Process32NextW(snap, ctypes.byref(pe)): break
|
|
k32.CloseHandle(snap)
|
|
return pids
|
|
|
|
existing = pid_by_name("Paralogue-Win64-Shipping.exe")
|
|
if existing:
|
|
# prefer the larger PID (most recently spawned)
|
|
pid = max(existing)
|
|
print(f"[*] attaching to running pid={pid}")
|
|
else:
|
|
print(f"[*] spawning game")
|
|
p = subprocess.Popen([EXE], cwd=CWD)
|
|
time.sleep(12)
|
|
pids = pid_by_name("Paralogue-Win64-Shipping.exe")
|
|
if not pids:
|
|
print("[-] spawn failed"); sys.exit(1)
|
|
pid = max(pids)
|
|
print(f"[*] spawned pid={pid}")
|
|
|
|
# DLL injection via CreateRemoteThread + LoadLibraryW
|
|
PROCESS_ALL_ACCESS = 0x1F0FFF
|
|
MEM_COMMIT = 0x1000
|
|
MEM_RESERVE = 0x2000
|
|
PAGE_READWRITE = 0x04
|
|
|
|
k32 = ctypes.windll.kernel32
|
|
k32.OpenProcess.restype = wt.HANDLE
|
|
k32.VirtualAllocEx.restype = ctypes.c_void_p
|
|
k32.VirtualAllocEx.argtypes = [wt.HANDLE, ctypes.c_void_p, ctypes.c_size_t, wt.DWORD, wt.DWORD]
|
|
k32.WriteProcessMemory.argtypes = [wt.HANDLE, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_size_t, ctypes.POINTER(ctypes.c_size_t)]
|
|
k32.GetProcAddress.restype = ctypes.c_void_p
|
|
k32.GetProcAddress.argtypes = [wt.HMODULE, ctypes.c_char_p]
|
|
k32.GetModuleHandleA.restype = wt.HMODULE
|
|
k32.CreateRemoteThread.restype = wt.HANDLE
|
|
k32.CreateRemoteThread.argtypes = [wt.HANDLE, ctypes.c_void_p, ctypes.c_size_t, ctypes.c_void_p, ctypes.c_void_p, wt.DWORD, ctypes.POINTER(wt.DWORD)]
|
|
|
|
hProc = k32.OpenProcess(PROCESS_ALL_ACCESS, False, pid)
|
|
if not hProc:
|
|
print(f"[-] OpenProcess failed: {ctypes.get_last_error()}"); sys.exit(1)
|
|
|
|
dll_w = (DLL + "\0").encode("utf-16-le")
|
|
addr = k32.VirtualAllocEx(hProc, None, len(dll_w), MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE)
|
|
written = ctypes.c_size_t(0)
|
|
k32.WriteProcessMemory(hProc, addr, dll_w, len(dll_w), ctypes.byref(written))
|
|
|
|
h_k32 = k32.GetModuleHandleA(b"kernel32.dll")
|
|
loadlib = k32.GetProcAddress(h_k32, b"LoadLibraryW")
|
|
print(f"[*] LoadLibraryW @ {hex(loadlib)}, remote alloc @ {hex(addr)}")
|
|
|
|
tid = wt.DWORD(0)
|
|
hThread = k32.CreateRemoteThread(hProc, None, 0, loadlib, addr, 0, ctypes.byref(tid))
|
|
if not hThread:
|
|
print(f"[-] CreateRemoteThread failed: {ctypes.get_last_error()}"); sys.exit(1)
|
|
print(f"[*] remote thread tid={tid.value}, waiting for LoadLibrary return...")
|
|
k32.WaitForSingleObject(hThread, 30000)
|
|
rc = wt.DWORD(0)
|
|
k32.GetExitCodeThread(hThread, ctypes.byref(rc))
|
|
print(f"[*] LoadLibraryW returned HMODULE={hex(rc.value)} (0 = failed)")
|
|
|
|
print(f"[*] polling {OUT} for SDK output (up to 3 min)...")
|
|
for i in range(36):
|
|
time.sleep(5)
|
|
if os.path.isdir(OUT):
|
|
entries = os.listdir(OUT)
|
|
if entries:
|
|
print(f"[+] t={5*(i+1)}s: {len(entries)} entries: {entries[:15]}")
|
|
# Check for common dumper-7 markers of completion
|
|
has_sdk = any(e.endswith(".hpp") or "SDK" in e for e in entries)
|
|
if has_sdk:
|
|
print("[+] SDK appears generated. Stopping poll.")
|
|
break
|
|
print("[*] final SDK listing:")
|
|
if os.path.isdir(OUT):
|
|
for e in sorted(os.listdir(OUT)):
|
|
p = os.path.join(OUT, e)
|
|
try:
|
|
sz = os.path.getsize(p) if os.path.isfile(p) else sum(os.path.getsize(os.path.join(dp,f)) for dp,_,fs in os.walk(p) for f in fs)
|
|
print(f" {e} ({sz} B)")
|
|
except Exception as ex:
|
|
print(f" {e} <err {ex}>")
|
|
print("[*] done")
|