diff --git a/src/Prospect.Client.Loader/Prospect.Client.Loader.vcxproj b/src/Prospect.Client.Loader/Prospect.Client.Loader.vcxproj index d67238c..bef52b8 100644 --- a/src/Prospect.Client.Loader/Prospect.Client.Loader.vcxproj +++ b/src/Prospect.Client.Loader/Prospect.Client.Loader.vcxproj @@ -21,10 +21,9 @@ 17.0 Win32Proj - {8170a17f-e536-4fdb-bc01-78bf09a9a48b} + {04b1d963-2bcb-4887-9d6d-04cb3366a348} ProspectClientLoader 10.0 - Prospect.Client.Loader diff --git a/src/Prospect.Client.Loader/main.cpp b/src/Prospect.Client.Loader/main.cpp index 84d962b..88a9813 100644 --- a/src/Prospect.Client.Loader/main.cpp +++ b/src/Prospect.Client.Loader/main.cpp @@ -54,28 +54,354 @@ HANDLE OpenTargetProcess(DWORD processID) { return hProcess; } -bool CheckTutorialActorsOffset(HANDLE hProcess, uintptr_t address) { +bool WriteBytes(HANDLE hProcess, uintptr_t address, BYTE* newValues, size_t size) { + SIZE_T bytesWritten; + DWORD oldProtect; + if (VirtualProtectEx(hProcess, (LPVOID)address, size, PAGE_EXECUTE_READWRITE, &oldProtect)) { + if (WriteProcessMemory(hProcess, (LPVOID)address, newValues, size, &bytesWritten)) { + printf("Memory Write Success at address 0x%p\n", (void*)address); + } + else { + printf("Failed to write memory. Error: %lu\n", GetLastError()); + return false; + } + // Restore original protection + VirtualProtectEx(hProcess, (LPVOID)address, size, oldProtect, &oldProtect); + } + else { + printf("Failed to change memory protection. Error: %lu\n", GetLastError()); + return false; + } + return true; +} + +bool CheckForceLevelStreamingOffset(HANDLE hProcess, uintptr_t address) { BYTE buffer[2]; SIZE_T bytesRead; if (ReadProcessMemory(hProcess, (LPCVOID)address, buffer, sizeof(buffer), &bytesRead)) { return buffer[0] == 0x32 && buffer[1] == 0xDB; - } else { + } + else { DWORD error = GetLastError(); if (error == 299) { printf("Incomplete memory read. Retrying...\n"); Sleep(1000); - return CheckTutorialActorsOffset(hProcess, address); + return CheckForceLevelStreamingOffset(hProcess, address); } printf("Failed to read memory. Error: %lu\n", error); return false; } } +bool CheckGPStringOffset(HANDLE hProcess, uintptr_t address) { + BYTE buffer[2]; + SIZE_T bytesRead; + if (ReadProcessMemory(hProcess, (LPCVOID)address, buffer, sizeof(buffer), &bytesRead)) { + return buffer[0] == 0x5F && buffer[1] == 0x00; + } + else { + DWORD error = GetLastError(); + if (error == 299) { + printf("Incomplete memory read. Retrying...\n"); + Sleep(1000); + return CheckForceLevelStreamingOffset(hProcess, address); + } + printf("Failed to read memory. Error: %lu\n", error); + return false; + } +} + +bool CheckWeakPointerCheckJumpOffset(HANDLE hProcess, uintptr_t address) { + BYTE buffer[2]; + SIZE_T bytesRead; + if (ReadProcessMemory(hProcess, (LPCVOID)address, buffer, sizeof(buffer), &bytesRead)) { + return buffer[0] == 0xF8 && buffer[1] == 0xFE; + } + else { + DWORD error = GetLastError(); + if (error == 299) { + printf("Incomplete memory read. Retrying...\n"); + Sleep(1000); + return CheckWeakPointerCheckJumpOffset(hProcess, address); + } + printf("Failed to read memory. Error: %lu\n", error); + return false; + } +} + +bool CheckWeakPointerCheckOffset(HANDLE hProcess, uintptr_t address) { + BYTE buffer[3]; + SIZE_T bytesRead; + if (ReadProcessMemory(hProcess, (LPCVOID)address, buffer, sizeof(buffer), &bytesRead)) { + return buffer[0] == 0x0F && buffer[1] == 0x1F && buffer[2] == 0x40; + } + else { + DWORD error = GetLastError(); + if (error == 299) { + printf("Incomplete memory read. Retrying...\n"); + Sleep(1000); + return CheckWeakPointerCheckOffset(hProcess, address); + } + printf("Failed to read memory. Error: %lu\n", error); + return false; + } +} + +bool PatchForceLevelStreaming(HANDLE hProcess, uintptr_t imageBase) { + // Season 3 Patch force disable level streaming: sub_3DA7520 -> loc_3DA75B6 + uintptr_t address = imageBase + 0x3DA75B6; + bool found = CheckForceLevelStreamingOffset(hProcess, address); + if (!found) { + // Season 2 Patch force disable level streaming: sub_3D19900 -> loc_3D19996 + address = imageBase + 0x3D19996; + found = CheckForceLevelStreamingOffset(hProcess, address); + } + + if (!found) { + printf("Failed to patch force level streaming!"); + return false; + } + + { + BYTE newValues[2] = { 0xB3, 0x01 }; + bool result = WriteBytes(hProcess, address, newValues, sizeof(newValues)); + if (!result) { + return false; + } + } + + address = imageBase + 0x5541C08; + found = CheckGPStringOffset(hProcess, address); + if (!found) { + address = imageBase + 0x54E0558; + found = CheckGPStringOffset(hProcess, address); + } + + if (!found) { + printf("Failed to find _GP string!\n"); + return false; + } + + { + BYTE newValues[6] = { 0x47, 0x00, 0x00, 0x00, 0x00, 0x00 }; + bool result = WriteBytes(hProcess, address, newValues, sizeof(newValues)); + if (!result) { + return false; + } + } + + address = imageBase + 0x16D7C24; + found = CheckWeakPointerCheckOffset(hProcess, address); + // TODO + //if (!found) { + // address = imageBase + 0x54E0558; + // found = CheckWeakPointerCheckOffset(hProcess, address); + //} + + if (!found) { + printf("Failed to find Weak Pointer Check!\n"); + return false; + } + + { + BYTE newValues[28] = { + 0x48, 0x8B, 0x06, + 0x48, 0x8D, 0x4D, 0x7F, + 0x48, 0x89, 0x45, 0x7F, + 0xE8, 0xDC, 0xC7, 0xA7, 0x00, + 0x48, 0x85, 0xC0, + 0x0F, 0x84, 0xEE, 0x00, 0x00, 0x00, + 0x90, 0x90, 0x90 + }; + bool result = WriteBytes(hProcess, address, newValues, sizeof(newValues)); + if (!result) { + return false; + } + } + + address = imageBase + 0x16D7D34; + found = CheckWeakPointerCheckJumpOffset(hProcess, address); + // TODO + //if (!found) { + // address = imageBase + 0x54E0558; + // found = CheckWeakPointerCheckJumpOffset(hProcess, address); + //} + + if (!found) { + printf("Failed to find Weak Pointer Check Jump!\n"); + return false; + } + + { + BYTE newValues[1] = { 0xEC }; + bool result = WriteBytes(hProcess, address, newValues, sizeof(newValues)); + if (!result) { + return false; + } + } + + return true; +} + +bool CheckUpdateInventoryFunctionOffset(HANDLE hProcess, uintptr_t address) { + BYTE buffer[2]; + SIZE_T bytesRead; + if (ReadProcessMemory(hProcess, (LPCVOID)address, buffer, sizeof(buffer), &bytesRead)) { + return buffer[0] == 0x84 && buffer[1] == 0xC0; + } + else { + DWORD error = GetLastError(); + if (error == 299) { + printf("Incomplete memory read. Retrying...\n"); + Sleep(1000); + return CheckUpdateInventoryFunctionOffset(hProcess, address); + } + printf("Failed to read memory. Error: %lu\n", error); + return false; + } +} + +bool PatchUpdateInventory(HANDLE hProcess, uintptr_t imageBase) { + uintptr_t address = imageBase + 0x18D5A11; + bool found = CheckUpdateInventoryFunctionOffset(hProcess, address); + if (!found) { + address = imageBase + 0x3D19B19; + found = CheckUpdateInventoryFunctionOffset(hProcess, address); + } + + if (!found) { + printf("Failed to patch inventory update function!"); + return false; + } + + BYTE newValues[8] = { 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90 }; + bool result = WriteBytes(hProcess, address, newValues, sizeof(newValues)); + if (!result) { + return false; + } + return true; +} + +bool CheckSetupPlayerContractsOffset(HANDLE hProcess, uintptr_t address) { + BYTE buffer[2]; + SIZE_T bytesRead; + if (ReadProcessMemory(hProcess, (LPCVOID)address, buffer, sizeof(buffer), &bytesRead)) { + return buffer[0] == 0x48 && buffer[1] == 0x8D; + } + else { + DWORD error = GetLastError(); + if (error == 299) { + printf("Incomplete memory read. Retrying...\n"); + Sleep(1000); + return CheckSetupPlayerContractsOffset(hProcess, address); + } + printf("Failed to read memory. Error: %lu\n", error); + return false; + } +} + +bool CheckUpdatePlayerContractsOffset(HANDLE hProcess, uintptr_t address) { + BYTE buffer[2]; + SIZE_T bytesRead; + if (ReadProcessMemory(hProcess, (LPCVOID)address, buffer, sizeof(buffer), &bytesRead)) { + return buffer[0] == 0x3B && buffer[1] == 0xC1; + } + else { + DWORD error = GetLastError(); + if (error == 299) { + printf("Incomplete memory read. Retrying...\n"); + Sleep(1000); + return CheckUpdatePlayerContractsOffset(hProcess, address); + } + printf("Failed to read memory. Error: %lu\n", error); + return false; + } +} + +bool PatchLoadPlayerContracts(HANDLE hProcess, uintptr_t imageBase) { + uintptr_t address = imageBase + 0x171E9EC; + bool found = CheckUpdatePlayerContractsOffset(hProcess, address); + if (!found) { + printf("Failed to update player contracts function!"); + return false; + } + + { + BYTE newValues[4] = { 0x90, 0x90, 0xEB, 0x21 }; + bool result = WriteBytes(hProcess, address, newValues, sizeof(newValues)); + if (!result) { + return false; + } + } + + address = imageBase + 0x1706127; + found = CheckSetupPlayerContractsOffset(hProcess, address); + if (!found) { + printf("Failed to find setup player contracts function!"); + return false; + } + + { + BYTE newValues[39] = { + 0x45, 0x33, 0xC0, 0x49, 0x8B, 0xD5, 0x48, 0x8B, 0x89, 0xD8, 0x00, 0x00, 0x00, 0xE8, 0x47, + 0xE2, 0x10, 0x00, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0xEB + }; + bool result = WriteBytes(hProcess, address, newValues, sizeof(newValues)); + if (!result) { + return false; + } + } + + return true; +} + +bool Inject(HANDLE hProcess) { + const wchar_t* dllPath = L"UE4SS.dll"; + // Allocate space in the target process for the DLL path + size_t pathSize = (wcslen(dllPath) + 1) * sizeof(wchar_t); + LPVOID remotePath = VirtualAllocEx(hProcess, NULL, pathSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); + if (!remotePath) { + wprintf(L"Failed to allocate memory in remote process. Error: %lu\n", GetLastError()); + CloseHandle(hProcess); + return false; + } + + // Write the DLL path into the target process + if (!WriteProcessMemory(hProcess, remotePath, dllPath, pathSize, NULL)) { + wprintf(L"Failed to write memory. Error: %lu\n", GetLastError()); + VirtualFreeEx(hProcess, remotePath, 0, MEM_RELEASE); + CloseHandle(hProcess); + return false; + } + + // Get the address of LoadLibraryW in kernel32.dll + LPVOID loadLibraryAddr = (LPVOID)GetProcAddress(GetModuleHandleW(L"kernel32.dll"), "LoadLibraryW"); + if (!loadLibraryAddr) { + wprintf(L"Failed to get LoadLibraryW address. Error: %lu\n", GetLastError()); + VirtualFreeEx(hProcess, remotePath, 0, MEM_RELEASE); + CloseHandle(hProcess); + return false; + } + + // Create a remote thread to call LoadLibraryW with our DLL path + HANDLE hThread = CreateRemoteThread( + hProcess, NULL, 0, + (LPTHREAD_START_ROUTINE)loadLibraryAddr, + remotePath, 0, NULL); + + if (!hThread) { + wprintf(L"Failed to create remote thread. Error: %lu\n", GetLastError()); + VirtualFreeEx(hProcess, remotePath, 0, MEM_RELEASE); + CloseHandle(hProcess); + return false; + } + return true; +} + bool Patch(const wchar_t* processName) { DWORD processID; - uintptr_t imageBase, address; - BYTE newValues[2] = { 0xB3, 0x01 }; - SIZE_T bytesWritten; + uintptr_t imageBase; HANDLE hProcess = StartProcess(processName, &processID); if (hProcess == NULL) { @@ -92,33 +418,13 @@ bool Patch(const wchar_t* processName) { } printf("PE Image Base Address: 0x%p\n", (void*)imageBase); - // Season 3 Patch tutorial actors loader: sub_3DA7520 -> loc_3DA75B6 - address = imageBase + 0x3DA75B6; - bool found = CheckTutorialActorsOffset(hProcess, address); - if (!found) { - // Season 2 Patch tutorial actors loader: sub_3D19900 -> loc_3D19996 - address = imageBase + 0x3D19996; - found = CheckTutorialActorsOffset(hProcess, address); - } - - if (!found) { - printf("Failed to find necessary memory chunk!"); + bool success = !PatchForceLevelStreaming(hProcess, imageBase) || !PatchUpdateInventory(hProcess, imageBase) || !PatchLoadPlayerContracts(hProcess, imageBase); + if (success) { return false; } - DWORD oldProtect; - if (VirtualProtectEx(hProcess, (LPVOID)address, sizeof(newValues), PAGE_EXECUTE_READWRITE, &oldProtect)) { - if (WriteProcessMemory(hProcess, (LPVOID)address, newValues, sizeof(newValues), &bytesWritten)) { - printf("Memory Write Success: Wrote 0xB3 0x01 at address 0x%p\n", (void*)address); - } - else { - printf("Failed to write memory. Error: %lu\n", GetLastError()); - } - // Restore original protection - VirtualProtectEx(hProcess, (LPVOID)address, sizeof(newValues), oldProtect, &oldProtect); - } - else { - printf("Failed to change memory protection. Error: %lu\n", GetLastError()); + if (!Inject(hProcess)) { + return false; } // Clean up @@ -126,8 +432,42 @@ bool Patch(const wchar_t* processName) { return true; } +bool WriteSteamAppIDFile() { + const char* filename = "steam_appid.txt"; + FILE* fp; + + if (fopen_s(&fp, filename, "r") == 0) { + // File exists, close the file + fclose(fp); + printf("File '%s' already exists.\n", filename); + return true; + } + else { + // File does not exist, create and write to it + if (fopen_s(&fp, filename, "w") == 0) { + const char* content = "480"; + size_t written = fwrite(content, sizeof(char), strlen(content), fp); + fclose(fp); + printf("File '%s' was created and written successfully.\n", filename); + } + else { + printf("Error creating the file '%s'.\n", filename); + return false; + } + } + return true; +} + int main() { - bool success = Patch(L"Prospect-Win64-Shipping.exe"); + bool success = WriteSteamAppIDFile(); + if (!success) { + // Wait for user input to exit + printf("An error occurred when writing steam_appid.txt! Press any key to exit...\n"); + getchar(); + return 1; + } + + success = Patch(L"Prospect-Win64-Shipping.exe"); if (!success) { // Wait for user input to exit printf("An error occurred when patching the executable! Press any key to exit...\n");