From 9099d50ba89306a0c25e4faca461d81d86f80f37 Mon Sep 17 00:00:00 2001 From: tretrauit Date: Sun, 24 Mar 2024 17:14:00 +0700 Subject: [PATCH] feat: support external game launchers --- game_payload/src/main.c | 5 ++ game_payload/src/utils.c | 20 ++++++-- injector/src/dll.c | 106 ++++++++++++++++++++++++++++++++++++--- 3 files changed, 122 insertions(+), 9 deletions(-) diff --git a/game_payload/src/main.c b/game_payload/src/main.c index 6253b5c..3c426bd 100644 --- a/game_payload/src/main.c +++ b/game_payload/src/main.c @@ -75,6 +75,11 @@ BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved) { return TRUE; } + LPWSTR targetExe = malloc(MAX_PATH); + GetModuleFileNameW(NULL, targetExe, 0); + SetCurrentDirectoryW(targetExe); + free(targetExe); + this_module = instance; // Dynamically link functions from ntdll diff --git a/game_payload/src/utils.c b/game_payload/src/utils.c index c312cb6..8195189 100644 --- a/game_payload/src/utils.c +++ b/game_payload/src/utils.c @@ -6,16 +6,26 @@ #include void utils_map_file(const wchar_t *path, struct file_mapping *map) { - map->file = CreateFileW(path, FILE_READ_ACCESS, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + wchar_t* final_path = malloc(MAX_PATH); + if (wcsstr(path, L"C:\\") == NULL) { + wchar_t* tmp = malloc(MAX_PATH); + GetEnvironmentVariableW(L"GAME_PATH", tmp, MAX_PATH); + swprintf(final_path, MAX_PATH, L"%ls\\%ls", tmp, path); + free(tmp); + } else { + swprintf(final_path, MAX_PATH, L"%ls", path); + } + map->file = CreateFileW(final_path, FILE_READ_ACCESS, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (map->file == INVALID_HANDLE_VALUE) { - msg_err_w(L"Could not open file: %ls", path); + msg_err_w(L"Could not open file: %ls", final_path); } map->mapping = CreateFileMappingA(map->file, NULL, PAGE_READONLY, 0, 0, NULL); map->data = MapViewOfFile(map->mapping, FILE_MAP_READ, 0, 0, 0); if (!map->data) { - msg_err_w(L"Could not map view of file %ls", path); + msg_err_w(L"Could not map view of file %ls", final_path); } + free(final_path); } void utils_unmap_file(struct file_mapping *map) { @@ -30,6 +40,10 @@ int utils_path_exists(const wchar_t *filePath) { uint32_t utils_file_crc32c(const wchar_t *filePath) { struct file_mapping map; + // LPWSTR cwd = malloc(MAX_PATH); + // GetCurrentDirectoryW(MAX_PATH, cwd); + // msg_info_w(L"File %ls %ls", filePath, cwd); + // free(cwd); utils_map_file(filePath, &map); LARGE_INTEGER fileSize; diff --git a/injector/src/dll.c b/injector/src/dll.c index d70b467..a3b1c43 100644 --- a/injector/src/dll.c +++ b/injector/src/dll.c @@ -1,5 +1,6 @@ #include - +#include +#include #include #include @@ -9,6 +10,75 @@ typedef char *(*wgufn_t)(wchar_t* path); // wine_get_unix_file_name const wchar_t *J_MB_TITLE = L"Jadeite Launcher Payload"; +// Copied from https://cocomelonc.github.io/pentest/2021/09/29/findmyprocess.html +// Find process ID by process name +DWORD find_proc_id(const char *procname) { + + HANDLE hSnapshot; + PROCESSENTRY32 pe; + DWORD pid = 0; + BOOL hResult; + + // snapshot of all processes in the system + hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (INVALID_HANDLE_VALUE == hSnapshot) return 0; + + // initializing size: needed for using Process32First + pe.dwSize = sizeof(PROCESSENTRY32); + + // info about first process encountered in a system snapshot + hResult = Process32First(hSnapshot, &pe); + + // retrieve information about the processes + // and exit if unsuccessful + while (hResult) { + // if we find the process: return process ID + if (strcmp(procname, pe.szExeFile) == 0) { + pid = pe.th32ProcessID; + break; + } + hResult = Process32Next(hSnapshot, &pe); + } + + // closes an open handle (CreateToolhelp32Snapshot) + CloseHandle(hSnapshot); + return pid; +} + +// Find thread ID by process PID +int find_main_thread(DWORD pid) { + + HANDLE hSnapshot; + THREADENTRY32 pe; + DWORD threadId = 0; + BOOL hResult; + + // snapshot of all processes in the system + hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); + if (INVALID_HANDLE_VALUE == hSnapshot) return 0; + + // initializing size: needed for using Process32First + pe.dwSize = sizeof(THREADENTRY32); + + // info about first process encountered in a system snapshot + hResult = Thread32First(hSnapshot, &pe); + + // retrieve information about the processes + // and exit if unsuccessful + while (hResult) { + // if we find the process: return process ID + if (pid == pe.th32OwnerProcessID) { + threadId = pe.th32ThreadID; + break; + } + hResult = Thread32Next(hSnapshot, &pe); + } + + // closes an open handle (CreateToolhelp32Snapshot) + CloseHandle(hSnapshot); + return threadId; +} + BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID reserved) { // Only listen for attach if (reason != DLL_PROCESS_ATTACH) { @@ -32,6 +102,13 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID reserved) { wcscpy(workdir, targetExe); *(wcsrchr(workdir, L'\\')) = L'\0'; + // Change the game's working directory + LPWSTR game_path = malloc(MAX_PATH); + GetEnvironmentVariableW(L"GAME_PATH", game_path, MAX_PATH); + wchar_t message[64]; + wsprintfW(message, L"Game Path: %ls", game_path); + MessageBoxW(NULL, message, J_MB_TITLE, MB_OK | MB_ICONINFORMATION); + // 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"); @@ -81,7 +158,7 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID reserved) { NULL, NULL, FALSE, - CREATE_SUSPENDED, + INHERIT_PARENT_AFFINITY, NULL, workdir, &si, @@ -94,10 +171,27 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID reserved) { exit(1); } + // Find the game process + wprintf(L"Waiting for game process to start...\n"); + DWORD game_pid = 0; + while (!game_pid) { + // wprintf(L"Looking for game process...\n"); + game_pid = find_proc_id("StarRail.exe"); + Sleep(1); + } + DWORD thread_id = 0; + while (!thread_id) { + // wprintf(L"Looking for game process...\n"); + thread_id = find_main_thread(game_pid); + Sleep(1); + } + HANDLE game = OpenProcess(PROCESS_ALL_ACCESS, FALSE, game_pid); + HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, thread_id); + SuspendThread(hThread); // 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); + inject(game, payloadStart, payloadSize, injectDll); // Remove the restart flag file DeleteFileW(restartFlagFile); @@ -106,15 +200,15 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID reserved) { char *waitEnabled = getenv("WAIT_BEFORE_RESUME"); if (waitEnabled && *waitEnabled) { wchar_t message[64]; - wsprintfW(message, L"PID: %ld. Press OK to continue", pi.dwProcessId); + wsprintfW(message, L"PID: %ld. Thread ID: %ld. Press OK to continue", game_pid, thread_id); MessageBoxW(NULL, message, J_MB_TITLE, MB_OK | MB_ICONINFORMATION); } // Resume the process - ResumeThread(pi.hThread); + ResumeThread(hThread); // The launcher process should now hang untill the game terminates - WaitForSingleObject(pi.hProcess, INFINITE); + WaitForSingleObject(game, INFINITE); } while (GetFileAttributesW(restartFlagFile) != INVALID_FILE_ATTRIBUTES); return TRUE;