#include #include #include #include typedef char *(*wgufn_t)(wchar_t* path); // wine_get_unix_file_name const char *J_MB_TITLE = "Jadeite Launcher Payload"; BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID reserved) { // Only listen for attach if (reason != DLL_PROCESS_ATTACH) { return TRUE; } // Get target EXE path char *targetExe = getenv(ENV_EXE_PATH); // Get the path of the DLL to inject char *injectDll = getenv(ENV_DLL_PATH); // Get game commandline char *cmdline = getenv(ENV_PROC_CMD); // Compute the working directory path char workdir[MAX_PATH]; strcpy(workdir, targetExe); *(strrchr(workdir, '\\')) = '\0'; // SAFETY: verify that the injector is not inside the game directory HMODULE kernel32 = GetModuleHandleA("kernel32.dll"); wgufn_t wine_get_unix_file_name = (wgufn_t)GetProcAddress(kernel32, "wine_get_unix_file_name"); if (wine_get_unix_file_name) { wchar_t wInjectDll[MAX_PATH], wWorkdir[MAX_PATH]; MultiByteToWideChar(CP_UTF8, 0, injectDll, strlen(injectDll) + 1, wInjectDll, MAX_PATH); MultiByteToWideChar(CP_UTF8, 0, workdir, strlen(workdir) + 1, wWorkdir, MAX_PATH); char *unixInjectDll = wine_get_unix_file_name(wInjectDll); char *unixWorkdir = wine_get_unix_file_name(wWorkdir); char *i = unixInjectDll, *w = unixWorkdir; char startsWith = 0; while (*i != '\0' && *w != '\0') { startsWith = *i == *w; if (!startsWith) break; i++, w++; } HANDLE heap = GetProcessHeap(); HeapFree(heap, 0, unixInjectDll); HeapFree(heap, 0, unixWorkdir); if (startsWith) { MessageBoxA(NULL, "Putting the patcher (or any other foreign PE binaries) inside the game directory is dangerous! Please move it elsewhere.", J_MB_TITLE, MB_OK | MB_ICONERROR); exit(1); } } else { MessageBoxA(NULL, "Could not find wine_get_unix_file_name! Wine version too old?", J_MB_TITLE, MB_OK | MB_ICONWARNING); } // Start the game STARTUPINFO si; ZeroMemory(&si, sizeof(si)); PROCESS_INFORMATION pi; si.cb = sizeof(si); ZeroMemory(&pi, sizeof(pi)); if (!CreateProcessA( NULL, cmdline, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, workdir, &si, &pi )) { char message[64]; sprintf(message, "Failed to start game process: %ld", GetLastError()); MessageBoxA(NULL, message, J_MB_TITLE, MB_OK | MB_ICONERROR); exit(1); } // Inject void *payloadStart = &_binary_game_p_o_p_game_p_bin_start; size_t payloadSize = (size_t)&_binary_game_p_o_p_game_p_bin_size; inject(pi.hProcess, payloadStart, payloadSize, injectDll); // Optional: wait for user input before resuming (useful for debugging) char *waitEnabled = getenv("WAIT_BEFORE_RESUME"); if (waitEnabled && strcmp(waitEnabled, "") != 0) { char message[64]; sprintf(message, "PID: %ld. Press OK to continue", pi.dwProcessId); MessageBoxA(NULL, message, J_MB_TITLE, MB_OK | MB_ICONINFORMATION); } // Resume the process ResumeThread(pi.hThread); // The launcher process should now hang untill the game terminates WaitForSingleObject(pi.hProcess, INFINITE); return TRUE; }