diff --git a/Windows/Core/Core.cpp b/Windows/Core/Core.cpp index d2410f7..80b97ca 100644 --- a/Windows/Core/Core.cpp +++ b/Windows/Core/Core.cpp @@ -9,8 +9,36 @@ void Core::Init() Sleep(250); } + Sleep(2500); + FMemory::_Realloc = Memcury::Scanner::FindPattern("48 89 5C 24 08 48 89 74 24 10 57 48 83 EC 20 48 8B F1 41 8B D8 48 8B 0D ? ? ? ? 48") - .GetAs(); // Checked on: 1.8, 12.41, 15.50, 19.10 + .GetAs(); // checked on: 1.8, 12.41, 15.50, 19.10, 23.50 + + // This whole RequestExit business sucks but nothing we can do, since when you go in a map on S22+ not connecting to a server it requests exit + + auto NoCallSiteRequestExit = Memcury::Scanner::FindPattern("48 89 5C 24 ? 48 89 74 24 ? 57 48 83 EC 20 33 DB 0F B6 F9 80 3D ? ? ? ? ? 48 8B F2 72 27 48 85 D2 48 8D 05"); // 24.40 + if (NoCallSiteRequestExit.IsValid()) + { + DWORD dwProtection; + VirtualProtect(NoCallSiteRequestExit.GetAs(), 1, PAGE_EXECUTE_READWRITE, &dwProtection); + + *(NoCallSiteRequestExit.GetAs()) = 0xC3; + + DWORD dwTemp; + VirtualProtect(NoCallSiteRequestExit.GetAs(), 1, dwProtection, &dwTemp); + } + + auto RequestExit = Memcury::Scanner::FindPattern("88 4C 24 08 53 48 83 EC 20 80 3D ? ? ? ? ? 8A D9 72 0A 4C 8D 4C 24"); // 22.30, 23.50 + if (RequestExit.IsValid()) + { + DWORD dwProtection; + VirtualProtect(RequestExit.GetAs(), 1, PAGE_EXECUTE_READWRITE, &dwProtection); + + *(RequestExit.GetAs()) = 0xC3; + + DWORD dwTemp; + VirtualProtect(RequestExit.GetAs(), 1, dwProtection, &dwTemp); + } auto RequestExitRef = Memcury::Scanner::FindStringRef(L"FPlatformMisc::RequestExitWithStatus(%i, %i)"); @@ -24,7 +52,7 @@ void Core::Init() if (bFound) { - *(Found + 6) = { 0xC3 }; + *(Found + 6) = 0xC3; auto StringRef = Memcury::Scanner::FindStringRef(L"UnsafeEnvironment_Title"); diff --git a/Windows/Core/EOS.cpp b/Windows/Core/EOS.cpp new file mode 100644 index 0000000..a4a51d8 --- /dev/null +++ b/Windows/Core/EOS.cpp @@ -0,0 +1,32 @@ +#include "../framework.h" + +#include "EOS.h" + +#include "Unreal/CurlHttp.h" + +bool EOS::ProcessRequestHook(FCurlHttpRequest* Request) +{ + return _ProcessRequest(Request->RedirectRequest()); +} + +void EOS::Init() +{ + auto EOSHandle = (uintptr_t)GetModuleHandleA("EOSSDK-Win64-Shipping.dll"); + if (!EOSHandle) return; + + auto ProcessRequestStrRef = Memcury::Scanner::FindStringRef(L"ProcessRequest failed. URL '%s' is not using a whitelisted domain. %p", EOSHandle); + + _ProcessRequest = ProcessRequestStrRef + .ScanFor({ 0x48, 0x89, 0x5C }, false) + .GetAs(); + + auto ProcessRequestRef = Memcury::Scanner::FindPointerRef(_ProcessRequest, EOSHandle); + + DWORD dwProtection; + VirtualProtect(ProcessRequestRef.GetAs(), 8, PAGE_EXECUTE_READWRITE, &dwProtection); + + *ProcessRequestRef.GetAs() = ProcessRequestHook; + + DWORD dwTemp; + VirtualProtect(ProcessRequestRef.GetAs(), 8, dwProtection, &dwTemp); +} \ No newline at end of file diff --git a/Windows/Core/EOS.h b/Windows/Core/EOS.h new file mode 100644 index 0000000..e385e23 --- /dev/null +++ b/Windows/Core/EOS.h @@ -0,0 +1,11 @@ +#pragma once + +class FCurlHttpRequest; + +namespace EOS +{ + static bool (*_ProcessRequest)(FCurlHttpRequest*); + static bool ProcessRequestHook(FCurlHttpRequest* Request); + + void Init(); +} \ No newline at end of file diff --git a/Windows/Core/Unreal/CurlHttp.cpp b/Windows/Core/Unreal/CurlHttp.cpp new file mode 100644 index 0000000..8c9caf7 --- /dev/null +++ b/Windows/Core/Unreal/CurlHttp.cpp @@ -0,0 +1,45 @@ +#include "../../framework.h" + +#include "CurlHttp.h" + +FString FCurlHttpRequest::GetURL() +{ + FString Result; + return ((FString& (*)(FCurlHttpRequest*, FString&))(*VTable))(this, Result); +} + +void FCurlHttpRequest::SetURL(FString URL) +{ + ((void (*)(FCurlHttpRequest*, FString&))(VTable[10]))(this, URL); +} + +FCurlHttpRequest* FCurlHttpRequest::RedirectRequest() +{ + std::wstring URL(this->GetURL().c_str()); + + const std::vector Domains = { + L"game-social.epicgames.com", + L"ol.epicgames.com", + L"ol.epicgames.net", + L"on.epicgames.com", + L"ak.epicgames.com", + L"epicgames.dev" + }; + + for (const auto& Domain : Domains) + { + size_t PathIndex = URL.find(Domain); + if (PathIndex != std::wstring::npos) + { + size_t PathStart = PathIndex + Domain.length(); + + std::wstring Path = URL.substr(PathStart); + std::wstring NewURL = Constants::API_URL + Path; + + this->SetURL(NewURL.c_str()); + break; + } + } + + return this; +} \ No newline at end of file diff --git a/Windows/Core/Unreal/CurlHttp.h b/Windows/Core/Unreal/CurlHttp.h new file mode 100644 index 0000000..543d706 --- /dev/null +++ b/Windows/Core/Unreal/CurlHttp.h @@ -0,0 +1,14 @@ +#pragma once + +class FCurlHttpRequest +{ +private: + void** VTable; + +public: + + class FString GetURL(); + void SetURL(FString URL); + + FCurlHttpRequest* RedirectRequest(); +}; \ No newline at end of file diff --git a/Windows/Main.cpp b/Windows/Main.cpp index 74b7598..8267601 100644 --- a/Windows/Main.cpp +++ b/Windows/Main.cpp @@ -11,6 +11,7 @@ static void Main() #endif Core::Init(); + EOS::Init(); Sinum::Init(); } diff --git a/Windows/Sinum.vcxproj b/Windows/Sinum.vcxproj index a0be3a0..fcab662 100644 --- a/Windows/Sinum.vcxproj +++ b/Windows/Sinum.vcxproj @@ -143,8 +143,10 @@ + + @@ -154,6 +156,8 @@ + + diff --git a/Windows/Sinum.vcxproj.filters b/Windows/Sinum.vcxproj.filters index abf4cda..c8bfddb 100644 --- a/Windows/Sinum.vcxproj.filters +++ b/Windows/Sinum.vcxproj.filters @@ -42,6 +42,12 @@ Header Files + + Header Files + + + Header Files + @@ -53,5 +59,11 @@ Source Files + + Source Files + + + Source Files + \ No newline at end of file diff --git a/Windows/Sinum/Sinum.cpp b/Windows/Sinum/Sinum.cpp index e92bc73..2d114d5 100644 --- a/Windows/Sinum/Sinum.cpp +++ b/Windows/Sinum/Sinum.cpp @@ -1,21 +1,13 @@ // Copyright (c) 2025 Project Nova LLC +#include "../framework.h" +#include "../Core/Unreal/CurlHttp.h" + #include "Sinum.h" bool Sinum::ProcessRequestHook(FCurlHttpRequest* Request) { - std::wstring URL(Request->GetURL().c_str()); - size_t PathIndex = URL.find(L"ol.epicgames.com"); - - if (PathIndex != std::wstring::npos) - { - auto Path = URL.substr(PathIndex + 16); - auto NewURL = Constants::API_URL + Path; - - Request->SetURL(NewURL.c_str()); - } - - return _ProcessRequest(Request); + return _ProcessRequest(Request->RedirectRequest()); } void Sinum::Init() @@ -25,19 +17,19 @@ void Sinum::Init() { bool bFound = false; _ProcessRequest = StringRef - .ScanFor({ 0x4C, 0x8B, 0xDC }, false, 0, 500, &bFound) + .ScanFor({ 0x4C, 0x8B, 0xDC }, false, 0, 200, &bFound) .GetAs(); if (!bFound) { _ProcessRequest = StringRef - .ScanFor({ 0x40, 0x53, 0x55 }, false, 0, 500, &bFound) // checked on: 12.41 + .ScanFor({ 0x40, 0x53, 0x55 }, false, 0, 200, &bFound) // checked on: 12.41 .GetAs(); if (!bFound) { _ProcessRequest = StringRef - .ScanFor({ 0x48, 0x8B, 0xC4 }, false, 0, 500, &bFound) // checked on: 16.50, 19.10 + .ScanFor({ 0x48, 0x8B, 0xC4 }, false, 0, 200, &bFound) // checked on: 16.50, 19.10, 23.50 .GetAs(); } } diff --git a/Windows/Sinum/Sinum.h b/Windows/Sinum/Sinum.h index 4a0fe47..d4c83d0 100644 --- a/Windows/Sinum/Sinum.h +++ b/Windows/Sinum/Sinum.h @@ -1,26 +1,8 @@ // Copyright (c) 2025 Project Nova LLC #pragma once -#include "../framework.h" -class FCurlHttpRequest -{ -private: - void** VTable; - -public: - - FString GetURL() - { - FString Result; - return ((FString& (*)(FCurlHttpRequest*, FString&))(*VTable))(this, Result); - } - - void SetURL(FString URL) - { - ((void (*)(FCurlHttpRequest*, FString&))(VTable[10]))(this, URL); - } -}; +class FCurlHttpRequest; namespace Sinum { diff --git a/Windows/Utilities/memcury.h b/Windows/Utilities/memcury.h index 15c3d56..df603e5 100644 --- a/Windows/Utilities/memcury.h +++ b/Windows/Utilities/memcury.h @@ -405,14 +405,14 @@ namespace Memcury return reinterpret_cast(GetModuleHandleA(Globals::moduleName)); } - inline auto GetDOSHeader() -> PIMAGE_DOS_HEADER + inline auto GetDOSHeader(uintptr_t moduleBase = PE::GetModuleBase()) -> PIMAGE_DOS_HEADER { - return reinterpret_cast(GetModuleBase()); + return reinterpret_cast(moduleBase); } - inline auto GetNTHeaders() -> PIMAGE_NT_HEADERS + inline auto GetNTHeaders(uintptr_t moduleBase = PE::GetModuleBase()) -> PIMAGE_NT_HEADERS { - return reinterpret_cast(GetModuleBase() + GetDOSHeader()->e_lfanew); + return reinterpret_cast(moduleBase + GetDOSHeader(moduleBase)->e_lfanew); } class Address @@ -558,12 +558,11 @@ namespace Memcury std::string sectionName; IMAGE_SECTION_HEADER rawSection; - static auto GetAllSections() -> std::vector
+ static auto GetAllSections(uintptr_t moduleBase = PE::GetModuleBase()) -> std::vector
{ std::vector
sections; - - auto sectionsSize = GetNTHeaders()->FileHeader.NumberOfSections; - auto section = IMAGE_FIRST_SECTION(GetNTHeaders()); + auto sectionsSize = GetNTHeaders(moduleBase)->FileHeader.NumberOfSections; + auto section = IMAGE_FIRST_SECTION(GetNTHeaders(moduleBase)); for (WORD i = 0; i < sectionsSize; i++, section++) { @@ -575,9 +574,9 @@ namespace Memcury return sections; } - static auto GetSection(std::string sectionName) -> Section + static auto GetSection(const std::string& sectionName, uintptr_t moduleBase = PE::GetModuleBase()) -> Section { - for (auto& section : GetAllSections()) + for (auto& section : GetAllSections(moduleBase)) { if (section.sectionName == sectionName) { @@ -594,19 +593,19 @@ namespace Memcury return rawSection.Misc.VirtualSize; } - auto GetSectionStart() -> Address + auto GetSectionStart(uintptr_t moduleBase = PE::GetModuleBase()) -> Address { - return Address(GetModuleBase() + rawSection.VirtualAddress); + return Address(moduleBase + rawSection.VirtualAddress); } - auto GetSectionEnd() -> Address + auto GetSectionEnd(uintptr_t moduleBase = PE::GetModuleBase()) -> Address { - return Address(GetSectionStart() + GetSectionSize()); + return Address(GetSectionStart(moduleBase) + GetSectionSize()); } - auto isInSection(Address address) -> bool + auto isInSection(Address address, uintptr_t moduleBase = PE::GetModuleBase()) -> bool { - return address >= GetSectionStart() && address < GetSectionEnd(); + return address >= GetSectionStart(moduleBase) && address < GetSectionEnd(moduleBase); } }; } @@ -715,6 +714,7 @@ namespace Memcury return FindPatternEx(handle, pattern, mask, module, module + Memcury::PE::GetNTHeaders()->OptionalHeader.SizeOfImage); } + template static auto FindPattern(const char* signature) -> Scanner { PE::Address add{ nullptr }; @@ -745,14 +745,17 @@ namespace Memcury } } - MemcuryAssertM(add != 0, "FindPattern return nullptr"); + if constexpr (bWarnIfNotFound) + MemcuryAssertM(add != 0, "FindPattern return nullptr") + else if (add == 0) + return Scanner(nullptr); return Scanner(add); } // Supports wide and normal strings both std and pointers - template - static auto FindStringRef(T string) -> Scanner + template + static auto FindStringRef(T string, uintptr_t moduleBase = PE::GetModuleBase()) -> Scanner { PE::Address add{ nullptr }; @@ -761,10 +764,10 @@ namespace Memcury constexpr auto bIsPtr = bIsWide || bIsChar; - auto textSection = PE::Section::GetSection(".text"); - auto rdataSection = PE::Section::GetSection(".rdata"); + auto textSection = PE::Section::GetSection(".text", moduleBase); + auto rdataSection = PE::Section::GetSection(".rdata", moduleBase); - const auto scanBytes = reinterpret_cast(textSection.GetSectionStart().Get()); + const auto scanBytes = reinterpret_cast(textSection.GetSectionStart(moduleBase).Get()); // scan only text section for (DWORD i = 0x0; i < textSection.GetSectionSize(); i++) @@ -774,7 +777,7 @@ namespace Memcury auto stringAdd = PE::Address(&scanBytes[i]).RelativeOffset(3); // Check if the string is in the .rdata section - if (rdataSection.isInSection(stringAdd)) + if (rdataSection.isInSection(stringAdd, moduleBase)) { auto strBytes = stringAdd.GetAs(); @@ -818,18 +821,20 @@ namespace Memcury } } - if constexpr (WarnIfNotFound) - MemcuryAssertM(add != 0, "FindStringRef return nullptr"); + if constexpr (bWarnIfNotFound) + MemcuryAssertM(add != 0, "FindStringRef return nullptr") + else if (add == 0) + return Scanner(nullptr); return Scanner(add); } - static auto FindPointerRef(void* pointer) -> Scanner + static auto FindPointerRef(void* pointer, uintptr_t moduleBase = PE::GetModuleBase()) -> Scanner { PE::Address add{ nullptr }; - auto rdataSection = PE::Section::GetSection(".rdata"); - const auto scanBytes = reinterpret_cast(rdataSection.GetSectionStart().Get()); + auto rdataSection = PE::Section::GetSection(".rdata", moduleBase); + const auto scanBytes = reinterpret_cast(rdataSection.GetSectionStart(moduleBase).Get()); for (DWORD i = 0; i < rdataSection.GetSectionSize(); i++) { diff --git a/Windows/framework.h b/Windows/framework.h index f1ff083..954c31e 100644 --- a/Windows/framework.h +++ b/Windows/framework.h @@ -7,4 +7,5 @@ #include "Utilities\Windows.h" #include "Core\Core.h" +#include "Core\EOS.h" #include "Sinum\Sinum.h" \ No newline at end of file