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");