From 840efb8383aa5677eeba7c30eccda21cf21503ed Mon Sep 17 00:00:00 2001 From: Yury Date: Tue, 4 Mar 2025 23:56:50 +0200 Subject: [PATCH] Add Prospect.Client.Loader and update Solution config --- .../Prospect.Client.Loader.vcxproj | 136 +++++++++++++++++ .../Prospect.Client.Loader.vcxproj.filters | 22 +++ src/Prospect.Client.Loader/main.cpp | 139 ++++++++++++++++++ .../Prospect.Server.Game.csproj | 1 + src/Prospect.Steam/Prospect.Steam.csproj | 1 + .../Prospect.Unreal.Generator.csproj | 1 + .../Prospect.Unreal.Tests.csproj | 2 + src/Prospect.Unreal/Prospect.Unreal.csproj | 1 + src/Prospect.sln | 5 +- 9 files changed, 307 insertions(+), 1 deletion(-) create mode 100644 src/Prospect.Client.Loader/Prospect.Client.Loader.vcxproj create mode 100644 src/Prospect.Client.Loader/Prospect.Client.Loader.vcxproj.filters create mode 100644 src/Prospect.Client.Loader/main.cpp diff --git a/src/Prospect.Client.Loader/Prospect.Client.Loader.vcxproj b/src/Prospect.Client.Loader/Prospect.Client.Loader.vcxproj new file mode 100644 index 0000000..d67238c --- /dev/null +++ b/src/Prospect.Client.Loader/Prospect.Client.Loader.vcxproj @@ -0,0 +1,136 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 17.0 + Win32Proj + {8170a17f-e536-4fdb-bc01-78bf09a9a48b} + ProspectClientLoader + 10.0 + Prospect.Client.Loader + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + + + + + \ No newline at end of file diff --git a/src/Prospect.Client.Loader/Prospect.Client.Loader.vcxproj.filters b/src/Prospect.Client.Loader/Prospect.Client.Loader.vcxproj.filters new file mode 100644 index 0000000..ce0c35c --- /dev/null +++ b/src/Prospect.Client.Loader/Prospect.Client.Loader.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + \ No newline at end of file diff --git a/src/Prospect.Client.Loader/main.cpp b/src/Prospect.Client.Loader/main.cpp new file mode 100644 index 0000000..84d962b --- /dev/null +++ b/src/Prospect.Client.Loader/main.cpp @@ -0,0 +1,139 @@ +#include +#include +#include +#include +#include + +// Function to create and launch a process with parameters +HANDLE StartProcess(const wchar_t* exePath, DWORD* processID) { + STARTUPINFO si = { 0 }; + PROCESS_INFORMATION pi = { 0 }; + si.cb = sizeof(si); + + // Command-line arguments to pass to the process + wchar_t commandLine[MAX_PATH] = L""; // Buffer for full command + _snwprintf_s(commandLine, sizeof(commandLine) / sizeof(wchar_t), L"\"%s\" -log -steam_auth PF_TITLEID=2EA46", exePath); + + if (CreateProcess(NULL, commandLine, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi)) { + *processID = pi.dwProcessId; + printf("Successfully started process: %ls (PID: %lu)\n", exePath, *processID); + ResumeThread(pi.hThread); + CloseHandle(pi.hThread); + return pi.hProcess; + } + else { + printf("Failed to start process. Error: %lu\n", GetLastError()); + return NULL; + } +} + +// Function to get the base address of the main module (PE Image) +uintptr_t GetModuleBaseAddress(DWORD processID) { + uintptr_t baseAddress = 0; + HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, processID); + if (hSnapshot != INVALID_HANDLE_VALUE) { + MODULEENTRY32 moduleEntry; + moduleEntry.dwSize = sizeof(MODULEENTRY32); + if (Module32First(hSnapshot, &moduleEntry)) { + baseAddress = (uintptr_t)moduleEntry.modBaseAddr; + } + CloseHandle(hSnapshot); + } + else { + printf("Failed to take process snapshot. Error: %lu\n", GetLastError()); + } + return baseAddress; +} + +// Function to get a handle to the target process +HANDLE OpenTargetProcess(DWORD processID) { + HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processID); + if (hProcess == NULL) { + printf("Failed to open process. Error: %lu\n", GetLastError()); + } + return hProcess; +} + +bool CheckTutorialActorsOffset(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 { + DWORD error = GetLastError(); + if (error == 299) { + printf("Incomplete memory read. Retrying...\n"); + Sleep(1000); + return CheckTutorialActorsOffset(hProcess, address); + } + printf("Failed to read memory. Error: %lu\n", error); + return false; + } +} + +bool Patch(const wchar_t* processName) { + DWORD processID; + uintptr_t imageBase, address; + BYTE newValues[2] = { 0xB3, 0x01 }; + SIZE_T bytesWritten; + + HANDLE hProcess = StartProcess(processName, &processID); + if (hProcess == NULL) { + return false; + } + + // Wait a bit for PE image to load + Sleep(5000); + // Find the PE Image Base Address + imageBase = GetModuleBaseAddress(processID); + if (imageBase == 0) { + printf("Failed to find PE Image Base Address.\n"); + return false; + } + 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!"); + 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()); + } + + // Clean up + CloseHandle(hProcess); + return true; +} + +int main() { + bool 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"); + getchar(); + return 1; + } + + return 0; +} \ No newline at end of file diff --git a/src/Prospect.Server.Game/Prospect.Server.Game.csproj b/src/Prospect.Server.Game/Prospect.Server.Game.csproj index 3659f1a..704692d 100644 --- a/src/Prospect.Server.Game/Prospect.Server.Game.csproj +++ b/src/Prospect.Server.Game/Prospect.Server.Game.csproj @@ -5,6 +5,7 @@ net8.0 enable enable + Debug;Release;Season 3 Release;Season 2 Release;Season 2 Debug;Season 3 Debug diff --git a/src/Prospect.Steam/Prospect.Steam.csproj b/src/Prospect.Steam/Prospect.Steam.csproj index 5210c96..37ebcaf 100644 --- a/src/Prospect.Steam/Prospect.Steam.csproj +++ b/src/Prospect.Steam/Prospect.Steam.csproj @@ -3,6 +3,7 @@ net8.0 true + Debug;Release;Season 3 Release;Season 2 Release;Season 2 Debug;Season 3 Debug diff --git a/src/Prospect.Unreal.Generator/Prospect.Unreal.Generator.csproj b/src/Prospect.Unreal.Generator/Prospect.Unreal.Generator.csproj index fdada5a..a6948a7 100644 --- a/src/Prospect.Unreal.Generator/Prospect.Unreal.Generator.csproj +++ b/src/Prospect.Unreal.Generator/Prospect.Unreal.Generator.csproj @@ -4,6 +4,7 @@ netstandard2.0 9.0 true + Debug;Release;Season 3 Release;Season 2 Release;Season 2 Debug;Season 3 Debug diff --git a/src/Prospect.Unreal.Tests/Prospect.Unreal.Tests.csproj b/src/Prospect.Unreal.Tests/Prospect.Unreal.Tests.csproj index 6ad5418..fba6736 100644 --- a/src/Prospect.Unreal.Tests/Prospect.Unreal.Tests.csproj +++ b/src/Prospect.Unreal.Tests/Prospect.Unreal.Tests.csproj @@ -5,6 +5,8 @@ enable false + + Debug;Release;Season 3 Release;Season 2 Release;Season 2 Debug;Season 3 Debug diff --git a/src/Prospect.Unreal/Prospect.Unreal.csproj b/src/Prospect.Unreal/Prospect.Unreal.csproj index 69d0772..412fdbc 100644 --- a/src/Prospect.Unreal/Prospect.Unreal.csproj +++ b/src/Prospect.Unreal/Prospect.Unreal.csproj @@ -5,6 +5,7 @@ enable enable true + Debug;Release;Season 3 Release;Season 2 Release;Season 2 Debug;Season 3 Debug diff --git a/src/Prospect.sln b/src/Prospect.sln index bbafc87..df8ef87 100644 --- a/src/Prospect.sln +++ b/src/Prospect.sln @@ -1,4 +1,4 @@ - + Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.12.35514.174 @@ -21,6 +21,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Prospect.Unreal.Tests", "Pr EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Prospect.Unreal.Generator", "Prospect.Unreal.Generator\Prospect.Unreal.Generator.csproj", "{71E3E262-49C5-4B6C-9670-D0741CEDB63B}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Prospect.Client.Loader", "Prospect.Client.Loader\Prospect.Client.Loader.vcxproj", "{8170A17F-E536-4FDB-BC01-78BF09A9A48B}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -216,6 +218,7 @@ Global {380DD490-8F01-4025-91D7-74C289AA5B75} = {76364BAA-BC9D-4A92-AA2A-8914FAE22834} {14DAE1A4-6F3F-43C0-AC17-9DCDE420AF34} = {168B6247-2D33-4942-AB3F-C5041B3BDFEA} {71E3E262-49C5-4B6C-9670-D0741CEDB63B} = {76364BAA-BC9D-4A92-AA2A-8914FAE22834} + {8170A17F-E536-4FDB-BC01-78BF09A9A48B} = {31D3D5D2-B011-4F9B-B6CC-D0A35675D797} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {12888C62-D3A2-4DD7-A9A5-1481D95865D0}