diff --git a/archive/README.md b/archive/README.md new file mode 100644 index 0000000..1f36bf4 --- /dev/null +++ b/archive/README.md @@ -0,0 +1,8 @@ +# Builds Archive + +Builds are stored on a Cloudflare R2 instance at `https://builds.rebootfn.org/versions.json`. +If you want to move them to another AWS-compatible object storage, run: +``` +move.ps1 +``` +and provide the required parameters. \ No newline at end of file diff --git a/archive/move.ps1 b/archive/move.ps1 new file mode 100644 index 0000000..aee0fe5 --- /dev/null +++ b/archive/move.ps1 @@ -0,0 +1,98 @@ +param( + [Parameter(Mandatory=$true)] + [string]$UrlListPath, # Path to a text file with one URL per line + + [Parameter(Mandatory=$true)] + [string]$BucketName, # Name of the R2 bucket + + [Parameter(Mandatory=$true)] + [string]$AccessKey, # Your R2 access key + + [Parameter(Mandatory=$true)] + [string]$SecretKey, # Your R2 secret key + + [Parameter(Mandatory=$true)] + [string]$EndPointURL, # Your R2 endpoint URL, e.g. https://.r2.cloudflarestorage.com + + [Parameter(Mandatory=$false)] + [int]$MaxConcurrentConnections = 16, # Number of concurrent connections for each file download + + [Parameter(Mandatory=$false)] + [int]$SplitCount = 16, # Number of segments to split the download into + + [Parameter(Mandatory=$false)] + [string]$AwsRegion = "auto" # Region; often "auto" works for R2, but can be set if needed +) + +# Set AWS environment variables for this session +$Env:AWS_ACCESS_KEY_ID = $AccessKey +$Env:AWS_SECRET_ACCESS_KEY = $SecretKey +$Env:AWS_REGION = $AwsRegion # If required, or leave as "auto" + +# Read all URLs from file +$Urls = Get-Content $UrlListPath | Where-Object { $_ -and $_. Trim() -ne "" } + +# Ensure aria2 is available +if (-not (Get-Command aria2c -ErrorAction SilentlyContinue)) { + Write-Error "aria2c not found in PATH. Please install aria2." + exit 1 +} + +# Ensure aws CLI is available +if (-not (Get-Command aws -ErrorAction SilentlyContinue)) { + Write-Error "aws CLI not found in PATH. Please install AWS CLI." + exit 1 +} + +function Process-Url { + param( + [string]$Url, + [string]$BucketName, + [string]$EndPointURL, + [int]$MaxConcurrentConnections, + [int]$SplitCount + ) + + # Extract the filename from the URL + $FileName = Split-Path -Leaf $Url + + try { + Write-Host "Downloading: $Url" + + # Use aria2c to download with multiple connections + & aria2c ` + --max-connection-per-server=$MaxConcurrentConnections ` + --split=$SplitCount ` + --out=$FileName ` + --check-certificate=false ` + --header="Cookie: _c_t_c=1" ` + $Url + + if (!(Test-Path $FileName)) { + Write-Host "Failed to download $Url" + return + } + + Write-Host "Uploading $FileName to R2 bucket: $BucketName" + & aws s3 cp $FileName "s3://$BucketName/$FileName" --endpoint-url $EndPointURL + if ($LASTEXITCODE -ne 0) { + Write-Host "Failed to upload $FileName to R2" + return + } + + Write-Host "Upload successful. Deleting local file: $FileName" + Remove-Item $FileName -Force + + Write-Host "Completed processing of $FileName." + + } catch { + Write-Host "Error processing $Url" + Write-Host $_ + } +} + +# Process each URL sequentially here. If you'd like to run multiple URLs in parallel, +# you could replace the foreach loop with a ForEach-Object -Parallel block. +foreach ($Url in $Urls) { + Process-Url -Url $Url -BucketName $BucketName -EndPointURL $EndPointURL -MaxConcurrentConnections $MaxConcurrentConnections -SplitCount $SplitCount +} diff --git a/archive/versions.txt b/archive/versions.txt new file mode 100644 index 0000000..46770e0 --- /dev/null +++ b/archive/versions.txt @@ -0,0 +1,85 @@ +https://builds.rebootfn.org/1.7.2.zip +https://builds.rebootfn.org/1.8.rar +https://builds.rebootfn.org/1.8.1.rar +https://builds.rebootfn.org/1.8.2.rar +https://builds.rebootfn.org/1.9.rar +https://builds.rebootfn.org/1.9.1.rar +https://builds.rebootfn.org/1.10.rar +https://builds.rebootfn.org/1.11.zip +https://builds.rebootfn.org/2.1.0.zip +https://builds.rebootfn.org/2.2.0.rar +https://builds.rebootfn.org/2.3.rar +https://builds.rebootfn.org/2.4.0.zip +https://builds.rebootfn.org/2.4.2.zip +https://builds.rebootfn.org/2.5.0.rar +https://builds.rebootfn.org/3.0.zip +https://builds.rebootfn.org/3.1.rar +https://builds.rebootfn.org/3.1.1.zip +https://builds.rebootfn.org/3.2.zip +https://builds.rebootfn.org/3.3.rar +https://builds.rebootfn.org/3.5.rar +https://builds.rebootfn.org/3.6.zip +https://builds.rebootfn.org/4.0.zip +https://builds.rebootfn.org/4.1.zip +https://builds.rebootfn.org/4.2.zip +https://builds.rebootfn.org/4.4.rar +https://builds.rebootfn.org/4.5.rar +https://builds.rebootfn.org/5.00.rar +https://builds.rebootfn.org/5.0.1.rar +https://builds.rebootfn.org/5.10.rar +https://builds.rebootfn.org/5.21.rar +https://builds.rebootfn.org/5.30.rar +https://builds.rebootfn.org/5.40.rar +https://builds.rebootfn.org/6.00.rar +https://builds.rebootfn.org/6.01.rar +https://builds.rebootfn.org/6.1.1.rar +https://builds.rebootfn.org/6.02.rar +https://builds.rebootfn.org/6.2.1.rar +https://builds.rebootfn.org/6.10.rar +https://builds.rebootfn.org/6.10.1.rar +https://builds.rebootfn.org/6.10.2.rar +https://builds.rebootfn.org/6.21.rar +https://builds.rebootfn.org/6.22.rar +https://builds.rebootfn.org/6.30.rar +https://builds.rebootfn.org/6.31.rar +https://builds.rebootfn.org/7.00.rar +https://builds.rebootfn.org/7.10.rar +https://builds.rebootfn.org/7.20.rar +https://builds.rebootfn.org/7.30.zip +https://builds.rebootfn.org/7.40.rar +https://builds.rebootfn.org/8.00.zip +https://builds.rebootfn.org/8.20.rar +https://builds.rebootfn.org/8.30.rar +https://builds.rebootfn.org/8.40.zip +https://builds.rebootfn.org/8.50.zip +https://builds.rebootfn.org/8.51.rar +https://builds.rebootfn.org/9.00.zip +https://builds.rebootfn.org/9.01.zip +https://builds.rebootfn.org/9.10.rar +https://builds.rebootfn.org/9.21.zip +https://builds.rebootfn.org/9.30.zip +https://builds.rebootfn.org/9.40.zip +https://builds.rebootfn.org/9.41.rar +https://builds.rebootfn.org/10.00.zip +https://builds.rebootfn.org/10.10.zip +https://builds.rebootfn.org/10.20.zip +https://builds.rebootfn.org/10.31.zip +https://builds.rebootfn.org/10.40.rar +https://builds.rebootfn.org/11.00.zip +https://builds.rebootfn.org/11.31.rar +https://builds.rebootfn.org/12.00.rar +https://builds.rebootfn.org/12.21.zip +https://builds.rebootfn.org/12.50.zip +https://builds.rebootfn.org/12.61.zip +https://builds.rebootfn.org/13.00.rar +https://builds.rebootfn.org/13.40.zip +https://builds.rebootfn.org/14.00.rar +https://builds.rebootfn.org/14.40.rar +https://builds.rebootfn.org/14.60.rar +https://builds.rebootfn.org/15.30.rar +https://builds.rebootfn.org/16.40.rar +https://builds.rebootfn.org/17.30.zip +https://builds.rebootfn.org/17.50.zip +https://builds.rebootfn.org/18.40.zip +https://builds.rebootfn.org/19.10.rar +https://builds.rebootfn.org/20.40.zip" \ No newline at end of file diff --git a/redirect/Core/Constants.h b/redirect/Core/Constants.h deleted file mode 100644 index eab0f82..0000000 --- a/redirect/Core/Constants.h +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (c) 2024 Project Nova LLC - -#pragma once - -namespace Constants -{ - constexpr auto API_URL = L"http://localhost:3551"; - - constexpr auto ProcessRequest = L"Could not set libcurl options for easy handle, processing HTTP request failed. Increase verbosity for additional information."; - constexpr auto ProcessRequest_C2 = L"STAT_FCurlHttpRequest_ProcessRequest"; - constexpr auto URLOffset = L"ProcessRequest failed. URL '%s' is not a valid HTTP request. %p"; - constexpr auto Realloc = L"AbilitySystem.Debug.NextTarget"; -} \ No newline at end of file diff --git a/redirect/Core/Core.cpp b/redirect/Core/Core.cpp deleted file mode 100644 index 36eb653..0000000 --- a/redirect/Core/Core.cpp +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) 2024 Project Nova LLC - -#include "Core.h" - -void Core::Init() -{ - FMemory::_Realloc = Memcury::Scanner::FindStringRef(Constants::Realloc) - .ScanFor({ Memcury::ASM::MNEMONIC::CALL }) - .RelativeOffset(1) - .GetAs(); -} \ No newline at end of file diff --git a/redirect/Core/Core.h b/redirect/Core/Core.h deleted file mode 100644 index e86e31e..0000000 --- a/redirect/Core/Core.h +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) 2024 Project Nova LLC - -#pragma once - -#include "..\Utilities\memcury.h" - -#include "Constants.h" - -#include "Unreal\Memory.h" -#include "Unreal\Array.h" -#include "Unreal\String.h" - -namespace Core -{ - void Init(); -} \ No newline at end of file diff --git a/redirect/Core/Unreal/Array.h b/redirect/Core/Unreal/Array.h deleted file mode 100644 index 31eeca6..0000000 --- a/redirect/Core/Unreal/Array.h +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright (c) 2024 Project Nova LLC - -#pragma once -#include -#include "Memory.h" - -template -class TArray -{ - friend class FString; - - T* Data; - int32_t NumElements; - int32_t MaxElements; - -public: - - inline TArray() - { - Data = nullptr; - NumElements = 0; - MaxElements = 0; - }; - - inline void Free() - { - FMemory::Free(Data); - Data = nullptr; - NumElements = 0; - MaxElements = 0; - } - - inline void Reset() - { - Free(); - } - - inline auto GetData() - { - return Data; - } - - inline int GetCount() const - { - return NumElements; - } - - inline int Num() const - { - return NumElements; - } - - inline auto& Get(const int Index) - { - return Data[Index]; - } - - inline auto& First() - { - return Get(0); - } - - inline auto GetRef(const int Index, int Size = sizeof(T)) - { - return (T*)((uint8_t*)Data + (Index * Size)); - } - - inline T& operator[](int i) - { - return Get(i); - }; - - inline const T& operator[](int i) const - { - return Get(i); - }; - - inline bool Remove(const int Index, int Size = sizeof(T)) - { - if (Index < NumElements) - { - if (Index != NumElements - 1) - Get(Index) = Get(NumElements - 1); - - --NumElements; - - return true; - } - return false; - }; - - inline bool Any(std::function Func) - { - for (int i = 0; i < NumElements; ++i) - { - if (Func(Get(i))) - return true; - } - return false; - } - - inline T Select(std::function Func) - { - for (int i = 0; i < NumElements; ++i) - { - if (Func(Get(i))) - return Get(i); - } - - return NULL; - } - - inline void ForEach(std::function Func) - { - for (int i = 0; i < NumElements; ++i) - { - Func(Get(i)); - } - } - - inline int Count(std::function Func) - { - int Num = 0; - - for (int i = 0; i < NumElements; ++i) - { - if (Func(Get(i))) - Num++; - } - return Num; - } - - inline int Find(const T& Item) - { - for (int i = 0; i < NumElements; i++) - { - if (this->operator[](i) == Item) - return i; - } - - return -1; - } -}; \ No newline at end of file diff --git a/redirect/Core/Unreal/Memory.h b/redirect/Core/Unreal/Memory.h deleted file mode 100644 index b33a7a1..0000000 --- a/redirect/Core/Unreal/Memory.h +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright (c) 2024 Project Nova LLC - -#pragma once -#include -#include - -class FMemory -{ -public: - static inline void* (*_Realloc)(void*, size_t, int64_t); - - static void Free(void* Data) - { - _Realloc(Data, 0, 0); - } - - static void* Malloc(size_t Size) - { - return _Realloc(0, Size, 0); - } - - static void* Realloc(void* Data, size_t NewSize) - { - return _Realloc(Data, NewSize, 0); - } - - static void* Memmove(void* Dest, const void* Src, size_t Count) - { - return memmove(Dest, Src, Count); - } - - static int Memcmp(const void* Buf1, const void* Buf2, size_t Count) - { - return memcmp(Buf1, Buf2, Count); - } - - static void* Memset(void* Dest, uint8_t Char, size_t Count) - { - return memset(Dest, Char, Count); - } - - template< class T > - static void Memset(T& Src, uint8_t ValueToSet) - { - Memset(&Src, ValueToSet, sizeof(T)); - } - - static void* Memzero(void* Dest, size_t Count) - { - return ZeroMemory(Dest, Count); - } - - template - static void Memzero(T& Src) - { - Memzero(&Src, sizeof(T)); - } - - static void* Memcpy(void* Dest, const void* Src, size_t Count) - { - return memcpy(Dest, Src, Count); - } - - template - static void Memcpy(T& Dest, const T& Src) - { - Memcpy(&Dest, &Src, sizeof(T)); - } - - static void* Calloc(size_t NumElements, size_t ElementSize) - { - auto TotalSize = NumElements * ElementSize; - auto Data = FMemory::Malloc(TotalSize); - - if (!Data) - return NULL; - - FMemory::Memzero(Data, TotalSize); - - return Data; - } - - static char* Strdup(const char* Str) - { - auto StrLen = strlen(Str) + 1; - auto StrDup = (char*)FMemory::Malloc(StrLen); - - FMemory::Memcpy(StrDup, Str, StrLen); - - return StrDup; - } -}; \ No newline at end of file diff --git a/redirect/Core/Unreal/String.h b/redirect/Core/Unreal/String.h deleted file mode 100644 index 5b54328..0000000 --- a/redirect/Core/Unreal/String.h +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) 2024 Project Nova LLC - -#pragma once -#include "Array.h" -#include "Memory.h" - -class FString : private TArray -{ -public: - - inline FString() - { - Data = nullptr; - NumElements = 0; - MaxElements = 0; - } - - inline FString(const char* Other) - { - if (Other) - { - auto NumCharacters = (int)std::strlen(Other); - MaxElements = NumElements = NumCharacters + 1; - - Data = static_cast(FMemory::Malloc(NumElements * sizeof(wchar_t))); - - size_t ConvertedChars = 0; - mbstowcs_s(&ConvertedChars, Data, NumElements, Other, _TRUNCATE); - } - else - { - MaxElements = NumElements = 0; - Data = nullptr; - } - }; - - inline FString(const wchar_t* Other) - { - MaxElements = NumElements = *Other ? (int)std::wcslen(Other) + 1 : 0; - - if (NumElements && Other) - { - Data = static_cast(FMemory::Malloc(NumElements * 2)); - - memcpy_s(Data, NumElements * 2, Other, NumElements * 2); - } - }; - - - inline auto c_str() - { - return Data; - } -}; \ No newline at end of file diff --git a/redirect/Main.cpp b/redirect/Main.cpp deleted file mode 100644 index 3c5d452..0000000 --- a/redirect/Main.cpp +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) 2024 Project Nova LLC - -#include "framework.h" - -static void Main() -{ - Sleep(7500); - - Core::Init(); - Sinum::Init(); -} - -bool DllMain(HMODULE hModule, DWORD dwReason, void* lpReserved) -{ - if (dwReason == DLL_PROCESS_ATTACH) - { - Windows::Thread::Create(Main); - } - - return true; -} \ No newline at end of file diff --git a/redirect/README.md b/redirect/README.md deleted file mode 100644 index af7172a..0000000 --- a/redirect/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Sinum Windows - -https://github.com/projectnovafn/Sinum/tree/main/Windows - -Modified to point to http://localhost:3551 \ No newline at end of file diff --git a/redirect/Sinum.sln b/redirect/Sinum.sln deleted file mode 100644 index 8d4591e..0000000 --- a/redirect/Sinum.sln +++ /dev/null @@ -1,22 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.7.34031.279 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Sinum", "Sinum.vcxproj", "{E7291B57-1B5B-497C-9C2E-C78A556A06CF}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Release|x64 = Release|x64 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {E7291B57-1B5B-497C-9C2E-C78A556A06CF}.Release|x64.ActiveCfg = Release|x64 - {E7291B57-1B5B-497C-9C2E-C78A556A06CF}.Release|x64.Build.0 = Release|x64 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {7975A2E1-0078-4AF8-AB10-0A71112857A5} - EndGlobalSection -EndGlobal diff --git a/redirect/Sinum.vcxproj b/redirect/Sinum.vcxproj deleted file mode 100644 index a0be3a0..0000000 --- a/redirect/Sinum.vcxproj +++ /dev/null @@ -1,162 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - 17.0 - Win32Proj - {e7291b57-1b5b-497c-9c2e-c78a556a06cf} - Sinum - 10.0 - - - - DynamicLibrary - true - v143 - Unicode - - - DynamicLibrary - false - v143 - true - Unicode - - - DynamicLibrary - true - v143 - Unicode - - - DynamicLibrary - false - v143 - true - Unicode - - - - - - - - - - - - - - - - - - - - - - Level3 - true - WIN32;_DEBUG;SINUM_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) - true - Use - pch.h - - - Windows - true - false - - - - - Level3 - true - true - true - WIN32;NDEBUG;SINUM_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) - true - Use - pch.h - stdcpplatest - - - Windows - true - true - true - false - - - - - Level3 - true - _DEBUG;SINUM_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) - true - Use - pch.h - - - Windows - true - false - - - - - Level3 - true - true - true - NDEBUG;SINUM_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) - true - NotUsing - pch.h - stdcpplatest - MaxSpeed - - - Windows - true - true - false - false - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/redirect/Sinum.vcxproj.filters b/redirect/Sinum.vcxproj.filters deleted file mode 100644 index abf4cda..0000000 --- a/redirect/Sinum.vcxproj.filters +++ /dev/null @@ -1,57 +0,0 @@ - - - - - {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 - - - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - - - Source Files - - - Source Files - - - Source Files - - - \ No newline at end of file diff --git a/redirect/Sinum.vcxproj.user b/redirect/Sinum.vcxproj.user deleted file mode 100644 index 88a5509..0000000 --- a/redirect/Sinum.vcxproj.user +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/redirect/Sinum/Sinum.cpp b/redirect/Sinum/Sinum.cpp deleted file mode 100644 index 86ca8f1..0000000 --- a/redirect/Sinum/Sinum.cpp +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) 2024 Project Nova LLC - -#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); -} - -void Sinum::Init() -{ - auto StringRef = Memcury::Scanner::FindStringRef(Constants::ProcessRequest); - if (StringRef.IsValid()) - { - _ProcessRequest = StringRef - .ScanFor({ 0x48, 0x81, 0xEC }, false) - .ScanFor({ 0x40 }, false) - .GetAs(); - } - else - { - _ProcessRequest = Memcury::Scanner::FindStringRef(Constants::ProcessRequest_C2) - .ScanFor({ 0x4C, 0x8B, 0xDC }, false) - .GetAs(); - } - - *Memcury::Scanner::FindPointerRef(_ProcessRequest) - .GetAs() = ProcessRequestHook; -} \ No newline at end of file diff --git a/redirect/Sinum/Sinum.h b/redirect/Sinum/Sinum.h deleted file mode 100644 index e9fb292..0000000 --- a/redirect/Sinum/Sinum.h +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) 2024 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); - } -}; - -namespace Sinum -{ - static bool (*_ProcessRequest)(FCurlHttpRequest*); - static bool ProcessRequestHook(FCurlHttpRequest* Request); - - void Init(); -} \ No newline at end of file diff --git a/redirect/Utilities/Windows.h b/redirect/Utilities/Windows.h deleted file mode 100644 index 29113b1..0000000 --- a/redirect/Utilities/Windows.h +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) 2024 Project Nova LLC - -#pragma once -#include "../framework.h" - -namespace Windows -{ - namespace Thread - { - static HANDLE Create(void* Routine, void* Param = NULL) - { - return CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Routine, Param, 0, NULL); - } - } -} \ No newline at end of file diff --git a/redirect/Utilities/memcury.h b/redirect/Utilities/memcury.h deleted file mode 100644 index 2b90f6a..0000000 --- a/redirect/Utilities/memcury.h +++ /dev/null @@ -1,1210 +0,0 @@ -#pragma once - -/* - Memcury is a single-header file library for memory manipulation in C++. - - Containers: - -PE::Address: A pointer container. - -PE::Section: Portable executable section container for internal usage. - - Modules: - -Scanner: - -Constructors: - -Default: Takes a pointer to start the scanning from. - -FindPattern: Finds a pattern in memory. - -FindStringRef: Finds a string reference in memory, supports all types of strings. - -Functions: - -SetTargetModule: Sets the target module for the scanner. - -ScanFor: Scans for a byte(s) near the current address. - -FindFunctionBoundary: Finds the boundary of a function near the current address. - -RelativeOffset: Gets the relative offset of the current address. - -AbsoluteOffset: Gets the absolute offset of the current address. - -GetAs: Gets the current address as a type. - -Get: Gets the current address as an int64. - - -TrampolineHook: - -Constructors: - -Default: Takes a pointer pointer to the target function and a pointer to the hook function. - -Functions: - -Commit: Commits the hook. - -Revert: Reverts the hook. - -Toggle: Toggles the hook on\off. - - -VEHHook: - -Functions: - -Init: Initializes the VEH Hook system. - -AddHook: Adds a hook to the VEH Hook system. - -RemoveHook: Removes a hook from the VEH Hook system. -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#pragma comment(lib, "Dbghelp.lib") - -#define MemcuryAssert(cond) \ - if (!(cond)) \ - { \ - MessageBoxA(nullptr, #cond, __FUNCTION__, MB_ICONERROR | MB_OK); \ - Memcury::Safety::FreezeCurrentThread(); \ - } - -#define MemcuryAssertM(cond, msg) \ - if (!(cond)) \ - { \ - MessageBoxA(nullptr, msg, __FUNCTION__, MB_ICONERROR | MB_OK); \ - Memcury::Safety::FreezeCurrentThread(); \ - } - -#define MemcuryThrow(msg) \ - MessageBoxA(nullptr, msg, __FUNCTION__, MB_ICONERROR | MB_OK); \ - Memcury::Safety::FreezeCurrentThread(); - -namespace Memcury -{ - extern "C" IMAGE_DOS_HEADER __ImageBase; - - inline auto GetCurrentModule() -> HMODULE - { - return reinterpret_cast(&__ImageBase); - } - - namespace Util - { - template - constexpr static auto IsInRange(T value, T min, T max) -> bool - { - return value >= min && value < max; - } - - constexpr auto StrHash(const char* str, int h = 0) -> unsigned int - { - return !str[h] ? 5381 : (StrHash(str, h + 1) * 33) ^ str[h]; - } - - inline auto IsSamePage(void* A, void* B) -> bool - { - MEMORY_BASIC_INFORMATION InfoA; - if (!VirtualQuery(A, &InfoA, sizeof(InfoA))) - { - return true; - } - - MEMORY_BASIC_INFORMATION InfoB; - if (!VirtualQuery(B, &InfoB, sizeof(InfoB))) - { - return true; - } - - return InfoA.BaseAddress == InfoB.BaseAddress; - } - - inline auto GetModuleStartAndEnd() -> std::pair - { - auto HModule = GetCurrentModule(); - auto NTHeaders = reinterpret_cast((uintptr_t)HModule + reinterpret_cast((uintptr_t)HModule)->e_lfanew); - - uintptr_t dllStart = (uintptr_t)HModule; - uintptr_t dllEnd = (uintptr_t)HModule + NTHeaders->OptionalHeader.SizeOfImage; - - return { dllStart, dllEnd }; - } - - inline auto CopyToClipboard(std::string str) - { - auto mem = GlobalAlloc(GMEM_FIXED, str.size() + 1); - memcpy(mem, str.c_str(), str.size() + 1); - - OpenClipboard(nullptr); - EmptyClipboard(); - SetClipboardData(CF_TEXT, mem); - CloseClipboard(); - - GlobalFree(mem); - } - } - - namespace Safety - { - enum class ExceptionMode - { - None, - CatchDllExceptionsOnly, - CatchAllExceptions - }; - - static auto FreezeCurrentThread() -> void - { - SuspendThread(GetCurrentThread()); - } - - static auto PrintStack(CONTEXT* ctx) -> void - { - STACKFRAME64 stack; - memset(&stack, 0, sizeof(STACKFRAME64)); - - auto process = GetCurrentProcess(); - auto thread = GetCurrentThread(); - - SymInitialize(process, NULL, TRUE); - - bool result; - DWORD64 displacement = 0; - - char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)]{ 0 }; - char name[256]{ 0 }; - char module[256]{ 0 }; - - PSYMBOL_INFO symbolInfo = (PSYMBOL_INFO)buffer; - - for (ULONG frame = 0;; frame++) - { - result = StackWalk64( - IMAGE_FILE_MACHINE_AMD64, - process, - thread, - &stack, - ctx, - NULL, - SymFunctionTableAccess64, - SymGetModuleBase64, - NULL); - - if (!result) - break; - - symbolInfo->SizeOfStruct = sizeof(SYMBOL_INFO); - symbolInfo->MaxNameLen = MAX_SYM_NAME; - SymFromAddr(process, (ULONG64)stack.AddrPC.Offset, &displacement, symbolInfo); - - HMODULE hModule = NULL; - lstrcpyA(module, ""); - GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (const wchar_t*)(stack.AddrPC.Offset), &hModule); - - if (hModule != NULL) - GetModuleFileNameA(hModule, module, 256); - - printf("[%lu] Name: %s - Address: %p - Module: %s\n", frame, symbolInfo->Name, (void*)symbolInfo->Address, module); - } - } - - template - auto MemcuryGlobalHandler(EXCEPTION_POINTERS* ExceptionInfo) -> long - { - auto [dllStart, dllEnd] = Util::GetModuleStartAndEnd(); - - if constexpr (mode == ExceptionMode::CatchDllExceptionsOnly) - { - if (!Util::IsInRange(ExceptionInfo->ContextRecord->Rip, dllStart, dllEnd)) - { - return EXCEPTION_CONTINUE_SEARCH; - } - } - - auto message = std::format("Memcury caught an exception at [{:x}]\nPress Yes if you want the address to be copied to your clipboard", ExceptionInfo->ContextRecord->Rip); - if (MessageBoxA(nullptr, message.c_str(), "Error", MB_ICONERROR | MB_YESNO) == IDYES) - { - std::string clip = std::format("{:x}", ExceptionInfo->ContextRecord->Rip); - Util::CopyToClipboard(clip); - } - - PrintStack(ExceptionInfo->ContextRecord); - - FreezeCurrentThread(); - - return EXCEPTION_EXECUTE_HANDLER; - } - - template - static auto SetExceptionMode() -> void - { - SetUnhandledExceptionFilter(MemcuryGlobalHandler); - } - } - - namespace Globals - { - constexpr const bool bLogging = true; - - inline const char* moduleName = nullptr; - } - - namespace ASM - { - //@todo: this whole namespace needs a rework, should somehow make this more modern and less ugly. - enum MNEMONIC : uint8_t - { - JMP_REL8 = 0xEB, - JMP_REL32 = 0xE9, - JMP_EAX = 0xE0, - CALL = 0xE8, - LEA = 0x8D, - CDQ = 0x99, - CMOVL = 0x4C, - CMOVS = 0x48, - CMOVNS = 0x49, - NOP = 0x90, - INT3 = 0xCC, - RETN_REL8 = 0xC2, - RETN = 0xC3, - POP = 0x58, - MAXOP = 0x5F, - CMOVNO = 0x41, - NONE = 0x00, - PUSH = 0x40 - }; - - constexpr int SIZE_OF_JMP_RELATIVE_INSTRUCTION = 5; - constexpr int SIZE_OF_JMP_ABSLOUTE_INSTRUCTION = 13; - - constexpr auto MnemonicToString(MNEMONIC e) -> const char* - { - switch (e) - { - case JMP_REL8: - return "JMP_REL8"; - case JMP_REL32: - return "JMP_REL32"; - case JMP_EAX: - return "JMP_EAX"; - case CALL: - return "CALL"; - case LEA: - return "LEA"; - case CDQ: - return "CDQ"; - case CMOVL: - return "CMOVL"; - case CMOVS: - return "CMOVS"; - case CMOVNS: - return "CMOVNS"; - case NOP: - return "NOP"; - case INT3: - return "INT3"; - case RETN_REL8: - return "RETN_REL8"; - case RETN: - return "RETN"; - case NONE: - return "NONE"; - default: - return "UNKNOWN"; - } - } - - constexpr auto Mnemonic(const char* s) -> MNEMONIC - { - switch (Util::StrHash(s)) - { - case Util::StrHash("JMP_REL8"): - return JMP_REL8; - case Util::StrHash("JMP_REL32"): - return JMP_REL32; - case Util::StrHash("JMP_EAX"): - return JMP_EAX; - case Util::StrHash("CALL"): - return CALL; - case Util::StrHash("LEA"): - return LEA; - case Util::StrHash("CDQ"): - return CDQ; - case Util::StrHash("CMOVL"): - return CMOVL; - case Util::StrHash("CMOVS"): - return CMOVS; - case Util::StrHash("CMOVNS"): - return CMOVNS; - case Util::StrHash("NOP"): - return NOP; - case Util::StrHash("INT3"): - return INT3; - case Util::StrHash("RETN_REL8"): - return RETN_REL8; - case Util::StrHash("RETN"): - return RETN; - default: - return NONE; - } - } - - inline auto byteIsA(uint8_t byte, MNEMONIC opcode) -> bool - { - return byte == opcode; - } - - inline auto byteIsAscii(uint8_t byte) -> bool - { - static constexpr bool isAscii[0x100] = { - false, false, false, false, false, false, false, false, false, true, true, false, false, true, false, false, - false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, - true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, - true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, - true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, - true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, - true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, - true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, false, - false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, - false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, - false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, - false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, - false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, - false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, - false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, - false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false - }; - - return isAscii[byte]; - } - - inline bool isJump(uint8_t byte) - { - return byte >= 0x70 && byte <= 0x7F; - } - - static auto pattern2bytes(const char* pattern) -> std::vector - { - auto bytes = std::vector{}; - const auto start = const_cast(pattern); - const auto end = const_cast(pattern) + strlen(pattern); - - for (auto current = start; current < end; ++current) - { - if (*current == '?') - { - ++current; - if (*current == '?') - ++current; - bytes.push_back(-1); - } - else - { - bytes.push_back(strtoul(current, ¤t, 16)); - } - } - return bytes; - } - } - - namespace PE - { - inline auto SetCurrentModule(const char* moduleName) -> void - { - Globals::moduleName = moduleName; - } - - inline auto GetModuleBase() -> uintptr_t - { - return reinterpret_cast(GetModuleHandleA(Globals::moduleName)); - } - - inline auto GetDOSHeader() -> PIMAGE_DOS_HEADER - { - return reinterpret_cast(GetModuleBase()); - } - - inline auto GetNTHeaders() -> PIMAGE_NT_HEADERS - { - return reinterpret_cast(GetModuleBase() + GetDOSHeader()->e_lfanew); - } - - class Address - { - uintptr_t _address; - - public: - Address() - { - _address = 0; - } - - Address(uintptr_t address) - : _address(address) - { - } - - Address(void* address) - : _address(reinterpret_cast(address)) - { - } - - auto operator=(uintptr_t address) -> Address - { - _address = address; - return *this; - } - - auto operator=(void* address) -> Address - { - _address = reinterpret_cast(address); - return *this; - } - - auto operator+(uintptr_t offset) -> Address - { - return Address(_address + offset); - } - - bool operator>(uintptr_t offset) - { - return _address > offset; - } - - bool operator>(Address address) - { - return _address > address._address; - } - - bool operator<(uintptr_t offset) - { - return _address < offset; - } - - bool operator<(Address address) - { - return _address < address._address; - } - - bool operator>=(uintptr_t offset) - { - return _address >= offset; - } - - bool operator>=(Address address) - { - return _address >= address._address; - } - - bool operator<=(uintptr_t offset) - { - return _address <= offset; - } - - bool operator<=(Address address) - { - return _address <= address._address; - } - - bool operator==(uintptr_t offset) - { - return _address == offset; - } - - bool operator==(Address address) - { - return _address == address._address; - } - - bool operator!=(uintptr_t offset) - { - return _address != offset; - } - - bool operator!=(Address address) - { - return _address != address._address; - } - - auto RelativeOffset(uint32_t offset) -> Address - { - _address = ((_address + offset + 4) + *(int32_t*)(_address + offset)); - return *this; - } - - auto AbsoluteOffset(uint32_t offset) -> Address - { - _address = _address + offset; - return *this; - } - - auto Jump() -> Address - { - if (ASM::isJump(*reinterpret_cast(_address))) - { - UINT8 toSkip = *reinterpret_cast(_address + 1); - _address = _address + 2 + toSkip; - } - - return *this; - } - - auto Get() -> uintptr_t - { - return _address; - } - - template - auto GetAs() -> T - { - return reinterpret_cast(_address); - } - - auto IsValid() -> bool - { - return _address != 0; - } - }; - - class Section - { - public: - std::string sectionName; - IMAGE_SECTION_HEADER rawSection; - - static auto GetAllSections() -> std::vector
- { - std::vector
sections; - - auto sectionsSize = GetNTHeaders()->FileHeader.NumberOfSections; - auto section = IMAGE_FIRST_SECTION(GetNTHeaders()); - - for (WORD i = 0; i < sectionsSize; i++, section++) - { - auto secName = std::string((char*)section->Name); - - sections.push_back({ secName, *section }); - } - - return sections; - } - - static auto GetSection(std::string sectionName) -> Section - { - for (auto& section : GetAllSections()) - { - if (section.sectionName == sectionName) - { - return section; - } - } - - MemcuryThrow("Section not found"); - return Section{}; - } - - auto GetSectionSize() -> uint32_t - { - return rawSection.Misc.VirtualSize; - } - - auto GetSectionStart() -> Address - { - return Address(GetModuleBase() + rawSection.VirtualAddress); - } - - auto GetSectionEnd() -> Address - { - return Address(GetSectionStart() + GetSectionSize()); - } - - auto isInSection(Address address) -> bool - { - return address >= GetSectionStart() && address < GetSectionEnd(); - } - }; - } - - class Scanner - { - PE::Address _address; - - public: - Scanner(PE::Address address) - : _address(address) - { - } - - static auto SetTargetModule(const char* moduleName) -> void - { - PE::SetCurrentModule(moduleName); - } - - static auto FindPatternEx(HANDLE handle, const char* pattern, const char* mask, uint64_t begin, uint64_t end) -> Scanner - { - auto scan = [](const char* pattern, const char* mask, char* begin, unsigned int size) -> char* - { - size_t patternLen = strlen(mask); - for (unsigned int i = 0; i < size - patternLen; i++) - { - bool found = true; - for (unsigned int j = 0; j < patternLen; j++) - { - if (mask[j] != '?' && pattern[j] != *(begin + i + j)) - { - found = false; - break; - } - } - - if (found) - return (begin + i); - } - return nullptr; - }; - - uint64_t match = NULL; - SIZE_T bytesRead; - char* buffer = nullptr; - MEMORY_BASIC_INFORMATION mbi = { 0 }; - - uint64_t curr = begin; - - for (uint64_t curr = begin; curr < end; curr += mbi.RegionSize) - { - if (!VirtualQueryEx(handle, (void*)curr, &mbi, sizeof(mbi))) - continue; - - if (mbi.State != MEM_COMMIT || mbi.Protect == PAGE_NOACCESS) - continue; - - buffer = new char[mbi.RegionSize]; - - if (ReadProcessMemory(handle, mbi.BaseAddress, buffer, mbi.RegionSize, &bytesRead)) - { - char* internalAddr = scan(pattern, mask, buffer, (unsigned int)bytesRead); - - if (internalAddr != nullptr) - { - match = curr + (uint64_t)(internalAddr - buffer); - break; - } - } - } - delete[] buffer; - - MemcuryAssertM(match != 0, "FindPatternEx return nullptr"); - - return Scanner(match); - } - - static auto FindPatternEx(HANDLE handle, const char* sig) -> Scanner - { - char pattern[100]; - char mask[100]; - - char lastChar = ' '; - unsigned int j = 0; - - for (unsigned int i = 0; i < strlen(sig); i++) - { - if ((sig[i] == '?' || sig[i] == '*') && (lastChar != '?' && lastChar != '*')) - { - pattern[j] = mask[j] = '?'; - j++; - } - - else if (isspace(lastChar)) - { - pattern[j] = lastChar = (char)strtol(&sig[i], 0, 16); - mask[j] = 'x'; - j++; - } - lastChar = sig[i]; - } - pattern[j] = mask[j] = '\0'; - - auto module = (uint64_t)GetModuleHandle(nullptr); - - return FindPatternEx(handle, pattern, mask, module, module + Memcury::PE::GetNTHeaders()->OptionalHeader.SizeOfImage); - } - - static auto FindPattern(const char* signature) -> Scanner - { - PE::Address add{ nullptr }; - - const auto sizeOfImage = PE::GetNTHeaders()->OptionalHeader.SizeOfImage; - auto patternBytes = ASM::pattern2bytes(signature); - const auto scanBytes = reinterpret_cast(PE::GetModuleBase()); - - const auto s = patternBytes.size(); - const auto d = patternBytes.data(); - - for (auto i = 0ul; i < sizeOfImage - s; ++i) - { - bool found = true; - for (auto j = 0ul; j < s; ++j) - { - if (scanBytes[i + j] != d[j] && d[j] != -1) - { - found = false; - break; - } - } - - if (found) - { - add = reinterpret_cast(&scanBytes[i]); - break; - } - } - - MemcuryAssertM(add != 0, "FindPattern return nullptr"); - - return Scanner(add); - } - - // Supports wide and normal strings both std and pointers - template - static auto FindStringRef(T string) -> Scanner - { - PE::Address add{ nullptr }; - - constexpr auto bIsWide = std::is_same::value; - constexpr auto bIsChar = std::is_same::value; - - constexpr auto bIsPtr = bIsWide || bIsChar; - - auto textSection = PE::Section::GetSection(".text"); - auto rdataSection = PE::Section::GetSection(".rdata"); - - const auto scanBytes = reinterpret_cast(textSection.GetSectionStart().Get()); - - // scan only text section - for (DWORD i = 0x0; i < textSection.GetSectionSize(); i++) - { - if ((scanBytes[i] == ASM::CMOVL || scanBytes[i] == ASM::CMOVS) && scanBytes[i + 1] == ASM::LEA) - { - auto stringAdd = PE::Address(&scanBytes[i]).RelativeOffset(3); - - // Check if the string is in the .rdata section - if (rdataSection.isInSection(stringAdd)) - { - auto strBytes = stringAdd.GetAs(); - - // Check if the first char is printable - if (ASM::byteIsAscii(strBytes[0])) - { - if constexpr (!bIsPtr) - { - typedef T::value_type char_type; - - auto lea = stringAdd.GetAs(); - - T leaT(lea); - - if (leaT == string) - { - add = PE::Address(&scanBytes[i]); - } - } - else - { - auto lea = stringAdd.GetAs(); - - if constexpr (bIsWide) - { - if (wcscmp(string, lea) == 0) - { - add = PE::Address(&scanBytes[i]); - } - } - else - { - if (strcmp(string, lea) == 0) - { - add = PE::Address(&scanBytes[i]); - } - } - } - } - } - } - } - - MemcuryAssertM(add != 0, "FindStringRef return nullptr"); - - return Scanner(add); - } - - static auto FindPointerRef(void* pointer) -> Scanner - { - PE::Address add{ nullptr }; - - auto rdataSection = PE::Section::GetSection(".rdata"); - const auto scanBytes = reinterpret_cast(rdataSection.GetSectionStart().Get()); - - for (DWORD i = 0; i < rdataSection.GetSectionSize(); i++) - { - auto currentPointer = *reinterpret_cast(scanBytes + i); - - if (currentPointer == pointer) - { - add = PE::Address(&scanBytes[i]); - break; - } - } - - MemcuryAssertM(add != 0, "FindPointerRef return nullptr"); - - return Scanner(add); - } - - auto Jump() -> Scanner - { - _address.Jump(); - return *this; - } - - auto ScanFor(std::vector opcodesToFind, bool forward = true, int toSkip = 0) -> Scanner - { - const auto scanBytes = _address.GetAs(); - - for (auto i = (forward ? 1 : -1); forward ? (i < 2048) : (i > -2048); forward ? i++ : i--) - { - bool found = true; - - for (int k = 0; k < opcodesToFind.size() && found; k++) - { - if (opcodesToFind[k] == -1) - continue; - found = opcodesToFind[k] == scanBytes[i + k]; - } - - if (found) - { - _address = &scanBytes[i]; - if (toSkip != 0) - { - return ScanFor(opcodesToFind, forward, toSkip - 1); - } - - break; - } - } - - return *this; - } - - auto RelativeOffset(uint32_t offset) -> Scanner - { - _address.RelativeOffset(offset); - - return *this; - } - - auto AbsoluteOffset(uint32_t offset) -> Scanner - { - _address.AbsoluteOffset(offset); - - return *this; - } - - template - auto GetAs() -> T - { - return _address.GetAs(); - } - - auto Get() -> uintptr_t - { - return _address.Get(); - } - - auto IsValid() -> bool - { - return _address.IsValid(); - } - }; - - /* Bad don't use it tbh... */ - class TrampolineHook - { - void** originalFunctionPtr; - PE::Address originalFunction; - PE::Address hookFunction; - PE::Address allocatedPage; - std::vector restore; - - void PointToCodeIfNot(PE::Address& ptr) - { - auto bytes = ptr.GetAs(); - - if (ASM::byteIsA(bytes[0], ASM::MNEMONIC::JMP_REL32)) - { - ptr = bytes + 5 + *(int32_t*)&bytes[1]; - } - } - - void* AllocatePageNearAddress(void* targetAddr) - { - SYSTEM_INFO sysInfo; - GetSystemInfo(&sysInfo); - const uint64_t PAGE_SIZE = sysInfo.dwPageSize; - - uint64_t startAddr = (uint64_t(targetAddr) & ~(PAGE_SIZE - 1)); // round down to nearest page boundary - uint64_t minAddr = min(startAddr - 0x7FFFFF00, (uint64_t)sysInfo.lpMinimumApplicationAddress); - uint64_t maxAddr = max(startAddr + 0x7FFFFF00, (uint64_t)sysInfo.lpMaximumApplicationAddress); - - uint64_t startPage = (startAddr - (startAddr % PAGE_SIZE)); - - for (uint64_t pageOffset = 1; pageOffset; pageOffset++) - { - uint64_t byteOffset = pageOffset * PAGE_SIZE; - uint64_t highAddr = startPage + byteOffset; - uint64_t lowAddr = (startPage > byteOffset) ? startPage - byteOffset : 0; - - bool needsExit = highAddr > maxAddr && lowAddr < minAddr; - - if (highAddr < maxAddr) - { - void* outAddr = VirtualAlloc((void*)highAddr, PAGE_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); - if (outAddr) - return outAddr; - } - - if (lowAddr > minAddr) - { - void* outAddr = VirtualAlloc((void*)lowAddr, PAGE_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); - if (outAddr != nullptr) - return outAddr; - } - - if (needsExit) - { - break; - } - } - - return nullptr; - } - - void WriteAbsoluteJump(void* jumpLocation, void* destination) - { - uint8_t absJumpInstructions[] = { - ASM::Mnemonic("CMOVNS"), 0xBA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r10, addr - 0x41, 0xFF, 0xE2 // jmp r10 - }; - - auto destination64 = (uint64_t)destination; - memcpy(&absJumpInstructions[2], &destination64, sizeof(destination64)); - memcpy(jumpLocation, absJumpInstructions, sizeof(absJumpInstructions)); - } - - uintptr_t PrepareRestore() - { - /* - This is not a correct way to do it at all, since not all functions sub from the stack - This needs so much more tests, but it works for now. - */ - - Scanner scanner(originalFunction); - scanner.ScanFor({ 0x48, 0x83, 0xEC }); // sub rsp - - auto restoreSize = scanner.Get() - originalFunction.Get(); - - MemcuryAssert(restoreSize > 0 && restoreSize < 0x100); - - restore.reserve(restoreSize); - for (auto i = 0; i < restoreSize; i++) - { - restore.push_back(originalFunction.GetAs()[i]); - } - - return restoreSize; - } - - void WriteRestore() - { - auto restorePtr = allocatedPage + ASM::SIZE_OF_JMP_ABSLOUTE_INSTRUCTION + 2; - - memcpy(restorePtr.GetAs(), restore.data(), restore.size()); - - *originalFunctionPtr = restorePtr.GetAs(); - - // Write a jump back to where the execution should resume - restorePtr.AbsoluteOffset((uint32_t)restore.size()); - - auto contuineExecution = originalFunction + restore.size(); - - WriteAbsoluteJump(restorePtr.GetAs(), contuineExecution.GetAs()); - } - - auto PrepareJMPInstruction(uint64_t dst) - { - uint8_t bytes[5] = { ASM::Mnemonic("JMP_REL32"), 0x0, 0x0, 0x0, 0x0 }; - - const uint64_t relAddr = dst - (originalFunction.Get() + ASM::SIZE_OF_JMP_RELATIVE_INSTRUCTION); - memcpy(bytes + 1, &relAddr, 4); - - return std::move(bytes); - } - - bool IsHooked() - { - return originalFunction.GetAs()[0] == ASM::Mnemonic("JMP_REL32"); - } - - public: - TrampolineHook(void** originalFunction, void* hookFunction) - { - this->originalFunctionPtr = originalFunction; - - this->originalFunction = *originalFunction; - this->hookFunction = hookFunction; - - PointToCodeIfNot(this->originalFunction); - PointToCodeIfNot(this->hookFunction); - }; - - bool Commit() - { - auto fnStart = originalFunction.GetAs(); - - auto restoreSize = PrepareRestore(); - - if (!allocatedPage.IsValid()) - { - allocatedPage = AllocatePageNearAddress(fnStart); - } - - memset(allocatedPage.GetAs(), ASM::MNEMONIC::INT3, 0x1000); - - WriteAbsoluteJump(allocatedPage.GetAs(), hookFunction.GetAs()); - - DWORD oldProtect; - VirtualProtect(fnStart, 1024, PAGE_EXECUTE_READWRITE, &oldProtect); - - auto jmpInstruction = PrepareJMPInstruction(allocatedPage.Get()); - - WriteRestore(); - - memset(fnStart, ASM::MNEMONIC::INT3, restoreSize); - memcpy(fnStart, jmpInstruction, ASM::SIZE_OF_JMP_RELATIVE_INSTRUCTION); - - return true; - } - - bool Revert() - { - auto fnStart = originalFunction.GetAs(); - - DWORD oldProtect; - VirtualProtect(fnStart, 1024, PAGE_EXECUTE_READWRITE, &oldProtect); - - memcpy(fnStart, restore.data(), restore.size()); - - *originalFunctionPtr = originalFunction.GetAs(); - - // VirtualFree(allocatedPage.GetAs(), 0x1000, MEM_RELEASE); - - return true; - } - - auto Toggle() - { - if (IsHooked()) - Revert(); - else - Commit(); - - return IsHooked(); - } - }; - - namespace VEHHook - { - struct HOOK_INFO - { - void* Original; - void* Detour; - - HOOK_INFO(void* Original, void* Detour) - : Original(Original) - , Detour(Detour) - { - } - }; - - inline std::vector Hooks; - inline std::vector HookProtections; - inline HANDLE ExceptionHandler; - - inline long Handler(EXCEPTION_POINTERS* Exception) - { - if (Exception->ExceptionRecord->ExceptionCode == STATUS_GUARD_PAGE_VIOLATION) - { - auto Itr = std::find_if(Hooks.begin(), Hooks.end(), [Rip = Exception->ContextRecord->Rip](const HOOK_INFO& Hook) - { return Hook.Original == (void*)Rip; }); - if (Itr != Hooks.end()) - { - Exception->ContextRecord->Rip = (uintptr_t)Itr->Detour; - } - - Exception->ContextRecord->EFlags |= 0x100; // SINGLE_STEP_FLAG - - return EXCEPTION_CONTINUE_EXECUTION; - } - else if (Exception->ExceptionRecord->ExceptionCode == STATUS_SINGLE_STEP) - { - // TODO: find a way to only vp the function that about to get executed - for (auto& Hook : Hooks) - { - DWORD dwOldProtect; - VirtualProtect(Hook.Original, 1, PAGE_EXECUTE_READ | PAGE_GUARD, &dwOldProtect); - } - - return EXCEPTION_CONTINUE_EXECUTION; - } - - return EXCEPTION_CONTINUE_SEARCH; - } - - inline bool Init() - { - if (ExceptionHandler == nullptr) - { - ExceptionHandler = AddVectoredExceptionHandler(true, (PVECTORED_EXCEPTION_HANDLER)Handler); - } - return ExceptionHandler != nullptr; - } - - inline bool AddHook(void* Target, void* Detour) - { - if (ExceptionHandler == nullptr) - { - return false; - } - - if (Util::IsSamePage(Target, Detour)) - { - return false; - } - - if (!VirtualProtect(Target, 1, PAGE_EXECUTE_READ | PAGE_GUARD, &HookProtections.emplace_back())) - { - HookProtections.pop_back(); - return false; - } - - Hooks.emplace_back(Target, Detour); - return true; - } - - inline bool RemoveHook(void* Original) - { - auto Itr = std::find_if(Hooks.begin(), Hooks.end(), [Original](const HOOK_INFO& Hook) - { return Hook.Original == Original; }); - - if (Itr == Hooks.end()) - { - return false; - } - - const auto ProtItr = HookProtections.begin() + std::distance(Hooks.begin(), Itr); - Hooks.erase(Itr); - - DWORD dwOldProtect; - bool Ret = VirtualProtect(Original, 1, *ProtItr, &dwOldProtect); - HookProtections.erase(ProtItr); - - return false; - } - } -} \ No newline at end of file diff --git a/redirect/framework.h b/redirect/framework.h deleted file mode 100644 index 621b6dd..0000000 --- a/redirect/framework.h +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright (c) 2024 Project Nova LLC - -#pragma once -#include - -#include "Utilities\memcury.h" -#include "Utilities\Windows.h" - -#include "Core\Core.h" -#include "Sinum\Sinum.h" \ No newline at end of file