From e8ae17fc1e05a94baec4e8d2bc948e51cd37df2e Mon Sep 17 00:00:00 2001
From: ApfelTeeSaft <91074565+ApfelTeeSaft@users.noreply.github.com>
Date: Fri, 25 Oct 2024 23:55:53 +0200
Subject: [PATCH] testing and process event hook
---
F4Menu/F4Menu.vcxproj | 5 +
F4Menu/F4Menu.vcxproj.filters | 17 +-
F4Menu/Memory.h | 1142 +++++++++++++++++++++++++++++++
F4Menu/dllmain.cpp | 1 +
F4Menu/framework.h | 12 +-
F4Menu/gameplay.h | 22 +
F4Menu/kiero.cpp | 720 ++++++++++++++++++++
F4Menu/kiero.h | 78 +++
F4Menu/memcury.h | 1208 +++++++++++++++++++++++++++++++++
9 files changed, 3202 insertions(+), 3 deletions(-)
create mode 100644 F4Menu/Memory.h
create mode 100644 F4Menu/gameplay.h
create mode 100644 F4Menu/kiero.cpp
create mode 100644 F4Menu/kiero.h
create mode 100644 F4Menu/memcury.h
diff --git a/F4Menu/F4Menu.vcxproj b/F4Menu/F4Menu.vcxproj
index eb907eb7e..785bdd9ed 100644
--- a/F4Menu/F4Menu.vcxproj
+++ b/F4Menu/F4Menu.vcxproj
@@ -143,12 +143,17 @@
+
+
+
+
+
diff --git a/F4Menu/F4Menu.vcxproj.filters b/F4Menu/F4Menu.vcxproj.filters
index e1f352d93..3e69f91de 100644
--- a/F4Menu/F4Menu.vcxproj.filters
+++ b/F4Menu/F4Menu.vcxproj.filters
@@ -24,8 +24,20 @@
Header Files
+
+ Header Files
+
- Source Files
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
@@ -770,5 +782,8 @@
Source Files
+
+ Source Files
+
\ No newline at end of file
diff --git a/F4Menu/Memory.h b/F4Menu/Memory.h
new file mode 100644
index 000000000..940a844dd
--- /dev/null
+++ b/F4Menu/Memory.h
@@ -0,0 +1,1142 @@
+#pragma once
+#include "framework.h"
+
+namespace Memory
+{
+ uintptr_t GetAddress(uintptr_t Offset)
+ {
+ return (uintptr_t)GetModuleHandle(0) + Offset;
+ }
+
+ static inline uint8_t* (*DecryptPtr)(void* ObjPtr) = [](void* Ptr) -> uint8_t* { return (uint8_t*)Ptr; };
+
+ static inline uintptr_t FindBytes(Memcury::Scanner& Scanner, const std::vector& Bytes, int Count = 255, int SkipBytes = 0, bool bGoUp = false, int Skip = 0, const bool bPrint = false)
+ {
+ if (!Scanner.Get())
+ {
+ return 0;
+ }
+
+ auto Base = __int64(GetModuleHandleW(0));
+
+ for (int i = 0 + SkipBytes; i < Count + SkipBytes; i++) // we should subtract from skip if goup
+ {
+ auto CurrentByte = *(Memcury::ASM::MNEMONIC*)(bGoUp ? Scanner.Get() - i : Scanner.Get() + i);
+
+ if (CurrentByte == Bytes[0])
+ {
+ bool Found = true;
+ for (int j = 1; j < Bytes.size(); j++)
+ {
+ if (*(Memcury::ASM::MNEMONIC*)(bGoUp ? Scanner.Get() - i + j : Scanner.Get() + i + j) != Bytes[j])
+ {
+ Found = false;
+ break;
+ }
+ }
+ if (Found)
+ {
+ if (Skip > 0)
+ {
+ Skip--;
+ continue;
+ }
+
+ return bGoUp ? Scanner.Get() - i : Scanner.Get() + i;
+ }
+ }
+ }
+
+ return -1;
+ }
+
+ template
+ inline int32_t FindOffset(std::vector>& ObjectValuePair, int MinOffset = 0x28, int MaxOffset = 0x1A0)
+ {
+ int32_t HighestFoundOffset = MinOffset;
+
+ for (int i = 0; i < ObjectValuePair.size(); i++)
+ {
+ uint8_t* BytePtr = (uint8_t*)(ObjectValuePair[i].first);
+
+ for (int j = HighestFoundOffset; j < MaxOffset; j += Alignement)
+ {
+ if ((*reinterpret_cast(BytePtr + j)) == ObjectValuePair[i].second && j >= HighestFoundOffset)
+ {
+ if (j > HighestFoundOffset)
+ {
+ HighestFoundOffset = j;
+ i = 0;
+ }
+ j = MaxOffset;
+ }
+ }
+ }
+
+ return HighestFoundOffset != MinOffset ? HighestFoundOffset : -1;
+ }
+
+ inline std::pair GetSectionByName(uintptr_t ImageBase, const std::string& ReqestedSectionName)
+ {
+ if (ImageBase == 0)
+ return { NULL, 0 };
+
+ const PIMAGE_DOS_HEADER DosHeader = reinterpret_cast(ImageBase);
+ const PIMAGE_NT_HEADERS NtHeaders = reinterpret_cast(ImageBase + DosHeader->e_lfanew);
+
+ PIMAGE_SECTION_HEADER Sections = IMAGE_FIRST_SECTION(NtHeaders);
+
+ DWORD TextSize = 0;
+
+ for (int i = 0; i < NtHeaders->FileHeader.NumberOfSections; i++)
+ {
+ IMAGE_SECTION_HEADER& CurrentSection = Sections[i];
+
+ std::string SectionName = reinterpret_cast(CurrentSection.Name);
+
+ if (SectionName == ReqestedSectionName)
+ return { (ImageBase + CurrentSection.VirtualAddress), CurrentSection.Misc.VirtualSize };
+ }
+
+ return { NULL, 0 };
+ }
+
+ inline std::pair GetImageBaseAndSize()
+ {
+ uintptr_t ImageBase = (uintptr_t)GetModuleHandle(0);
+ PIMAGE_NT_HEADERS NtHeader = reinterpret_cast(ImageBase + reinterpret_cast(ImageBase)->e_lfanew);
+
+ return { ImageBase, NtHeader->OptionalHeader.SizeOfImage };
+ }
+
+ /* Credits: https://en.cppreference.com/w/cpp/string/byte/tolower */
+ inline std::string str_tolower(std::string S)
+ {
+ std::transform(S.begin(), S.end(), S.begin(), [](unsigned char C) { return std::tolower(C); });
+ return S;
+ }
+
+ namespace ASMUtils
+ {
+ /* See IDA or https://c9x.me/x86/html/file_module_x86_id_147.html for reference on the jmp opcode */
+ inline bool Is32BitRIPRelativeJump(uintptr_t Address)
+ {
+ return Address && *reinterpret_cast(Address) == 0xE9; /* 48 for jmp, FF for "RIP relative" -- little endian */
+ }
+
+ inline uintptr_t Resolve32BitRIPRelativeJumpTarget(uintptr_t Address)
+ {
+ constexpr int32_t InstructionSizeBytes = 0x5;
+ constexpr int32_t InstructionImmediateDisplacementOffset = 0x1;
+
+ const int32_t Offset = *reinterpret_cast(Address + InstructionImmediateDisplacementOffset);
+
+ /* Add the InstructionSizeBytes because offsets are relative to the next instruction. */
+ return Address + InstructionSizeBytes + Offset;
+ }
+
+ /* See https://c9x.me/x86/html/file_module_x86_id_147.html */
+ inline uintptr_t Resolve32BitRegisterRelativeJump(uintptr_t Address)
+ {
+ /*
+ * 48 FF 25 C1 10 06 00 jmp QWORD [rip+0x610c1]
+ *
+ * 48 FF 25 <-- Information on the instruction [jump, relative, rip]
+ * C1 10 06 00 <-- 32-bit Offset relative to the address coming **after** these instructions (+ 7) [if 48 had hte address 0x0 the offset would be relative to address 0x7]
+ */
+
+ return ((Address + 7) + *reinterpret_cast(Address + 3));
+ }
+
+ inline uintptr_t Resolve32BitSectionRelativeCall(uintptr_t Address)
+ {
+ /* Same as in Resolve32BitRIPRelativeJump, but instead of a jump we resolve a call, with one less instruction byte */
+ return ((Address + 6) + *reinterpret_cast(Address + 2));
+ }
+
+ inline uintptr_t Resolve32BitRelativeCall(uintptr_t Address)
+ {
+ /* Same as in Resolve32BitRIPRelativeJump, but instead of a jump we resolve a non-relative call, with two less instruction byte */
+ return ((Address + 5) + *reinterpret_cast(Address + 1));
+ }
+
+ inline uintptr_t Resolve32BitRelativeMove(uintptr_t Address)
+ {
+ /* Same as in Resolve32BitRIPRelativeJump, but instead of a jump we resolve a relative mov */
+ return ((Address + 7) + *reinterpret_cast(Address + 3));
+ }
+ }
+
+
+ struct CLIENT_ID
+ {
+ HANDLE UniqueProcess;
+ HANDLE UniqueThread;
+ };
+
+ struct TEB
+ {
+ NT_TIB NtTib;
+ PVOID EnvironmentPointer;
+ CLIENT_ID ClientId;
+ PVOID ActiveRpcHandle;
+ PVOID ThreadLocalStoragePointer;
+ struct PEB* ProcessEnvironmentBlock;
+ };
+
+ struct PEB_LDR_DATA
+ {
+ ULONG Length;
+ BOOLEAN Initialized;
+ BYTE MoreFunnyPadding[0x3];
+ HANDLE SsHandle;
+ LIST_ENTRY InLoadOrderModuleList;
+ LIST_ENTRY InMemoryOrderModuleList;
+ LIST_ENTRY InInitializationOrderModuleList;
+ PVOID EntryInProgress;
+ BOOLEAN ShutdownInProgress;
+ BYTE MoreFunnyPadding2[0x7];
+ HANDLE ShutdownThreadId;
+ };
+
+ struct PEB
+ {
+ BOOLEAN InheritedAddressSpace;
+ BOOLEAN ReadImageFileExecOptions;
+ BOOLEAN BeingDebugged;
+ union
+ {
+ BOOLEAN BitField;
+ struct
+ {
+ BOOLEAN ImageUsesLargePages : 1;
+ BOOLEAN IsProtectedProcess : 1;
+ BOOLEAN IsImageDynamicallyRelocated : 1;
+ BOOLEAN SkipPatchingUser32Forwarders : 1;
+ BOOLEAN IsPackagedProcess : 1;
+ BOOLEAN IsAppContainer : 1;
+ BOOLEAN IsProtectedProcessLight : 1;
+ BOOLEAN SpareBits : 1;
+ };
+ };
+ BYTE ManuallyAddedPaddingCauseTheCompilerIsStupid[0x4]; // It doesn't 0x8 byte align the pointers properly
+ HANDLE Mutant;
+ PVOID ImageBaseAddress;
+ PEB_LDR_DATA* Ldr;
+ };
+
+ struct UNICODE_STRING
+ {
+ USHORT Length;
+ USHORT MaximumLength;
+ BYTE MoreStupidCompilerPaddingYay[0x4];
+ PWCH Buffer;
+ };
+
+ struct LDR_DATA_TABLE_ENTRY
+ {
+ LIST_ENTRY InLoadOrderLinks;
+ LIST_ENTRY InMemoryOrderLinks;
+ //union
+ //{
+ // LIST_ENTRY InInitializationOrderLinks;
+ // LIST_ENTRY InProgressLinks;
+ //};
+ PVOID DllBase;
+ PVOID EntryPoint;
+ ULONG SizeOfImage;
+ BYTE MoreStupidCompilerPaddingYay[0x4];
+ UNICODE_STRING FullDllName;
+ UNICODE_STRING BaseDllName;
+ };
+
+ inline _TEB* _NtCurrentTeb()
+ {
+ return reinterpret_cast(__readgsqword(((LONG)__builtin_offsetof(NT_TIB, Self))));
+ }
+
+ inline PEB* GetPEB()
+ {
+ return reinterpret_cast(_NtCurrentTeb())->ProcessEnvironmentBlock;
+ }
+
+ inline uintptr_t GetImageBase()
+ {
+ return reinterpret_cast(GetPEB()->ImageBaseAddress);
+ }
+
+ inline uintptr_t GetOffset(const void* Addr)
+ {
+ static uintptr_t ImageBase = 0x0;
+
+ if (ImageBase == 0x0)
+ ImageBase = GetImageBase();
+
+ const uintptr_t AddrAsInt = reinterpret_cast(Addr);
+
+ return AddrAsInt > ImageBase ? (AddrAsInt - ImageBase) : 0x0;
+ }
+
+ inline bool IsInProcessRange(uintptr_t Address)
+ {
+ uintptr_t ImageBase = GetImageBase();
+ PIMAGE_NT_HEADERS NtHeader = reinterpret_cast(ImageBase + reinterpret_cast(ImageBase)->e_lfanew);
+
+ return Address > ImageBase && Address < (NtHeader->OptionalHeader.SizeOfImage + ImageBase);
+ }
+
+ inline bool IsBadReadPtr(const void* p)
+ {
+ MEMORY_BASIC_INFORMATION mbi;
+
+ if (VirtualQuery(p, &mbi, sizeof(mbi)))
+ {
+ constexpr DWORD mask = (PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY);
+ bool b = !(mbi.Protect & mask);
+ if (mbi.Protect & (PAGE_GUARD | PAGE_NOACCESS))
+ b = true;
+
+ return b;
+ }
+
+ return true;
+ };
+
+ inline LDR_DATA_TABLE_ENTRY* GetModuleLdrTableEntry(const char* SearchModuleName)
+ {
+ PEB* Peb = GetPEB();
+ PEB_LDR_DATA* Ldr = Peb->Ldr;
+
+ int NumEntriesLeft = Ldr->Length;
+
+ for (LIST_ENTRY* P = Ldr->InMemoryOrderModuleList.Flink; P && NumEntriesLeft-- > 0; P = P->Flink)
+ {
+ LDR_DATA_TABLE_ENTRY* Entry = reinterpret_cast(P);
+
+ std::wstring WideModuleName(Entry->BaseDllName.Buffer, Entry->BaseDllName.Length >> 1);
+ std::string ModuleName = std::string(WideModuleName.begin(), WideModuleName.end());
+
+ if (str_tolower(ModuleName) == str_tolower(SearchModuleName))
+ return Entry;
+ }
+
+ return nullptr;
+ }
+
+ inline void* GetModuleAddress(const char* SearchModuleName)
+ {
+ LDR_DATA_TABLE_ENTRY* Entry = GetModuleLdrTableEntry(SearchModuleName);
+
+ if (Entry)
+ return Entry->DllBase;
+
+ return nullptr;
+ }
+
+ /* Gets the address at which a pointer to an imported function is stored */
+ inline PIMAGE_THUNK_DATA GetImportAddress(uintptr_t ModuleBase, const char* ModuleToImportFrom, const char* SearchFunctionName)
+ {
+ /* Get the module importing the function */
+ PIMAGE_DOS_HEADER DosHeader = reinterpret_cast(ModuleBase);
+
+ if (ModuleBase == 0x0 || DosHeader->e_magic != IMAGE_DOS_SIGNATURE)
+ return nullptr;
+
+ PIMAGE_NT_HEADERS NtHeader = reinterpret_cast(ModuleBase + reinterpret_cast(ModuleBase)->e_lfanew);
+
+ if (!NtHeader)
+ return nullptr;
+
+ PIMAGE_IMPORT_DESCRIPTOR ImportTable = reinterpret_cast(ModuleBase + NtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
+
+ //std::cout << "ModuleName: " << (SearchModuleName ? SearchModuleName : "Default") << std::endl;
+
+ /* Loop all modules and if we found the right one, loop all imports to get the one we need */
+ for (PIMAGE_IMPORT_DESCRIPTOR Import = ImportTable; Import && Import->Characteristics != 0x0; Import++)
+ {
+ if (Import->Name == 0xFFFF)
+ continue;
+
+ const char* Name = reinterpret_cast(ModuleBase + Import->Name);
+
+ //std::cout << "Name: " << str_tolower(Name) << std::endl;
+
+ if (str_tolower(Name) != str_tolower(ModuleToImportFrom))
+ continue;
+
+ PIMAGE_THUNK_DATA NameThunk = reinterpret_cast(ModuleBase + Import->OriginalFirstThunk);
+ PIMAGE_THUNK_DATA FuncThunk = reinterpret_cast(ModuleBase + Import->FirstThunk);
+
+ while (!IsBadReadPtr(reinterpret_cast(NameThunk))
+ && !IsBadReadPtr(reinterpret_cast(FuncThunk))
+ && !IsBadReadPtr(reinterpret_cast(ModuleBase + NameThunk->u1.AddressOfData))
+ && !IsBadReadPtr(reinterpret_cast(FuncThunk->u1.AddressOfData)))
+ {
+ /*
+ * A functin might be imported using the Ordinal (Index) of this function in the modules export-table
+ *
+ * The name could probably be retrieved by looking up this Ordinal in the Modules export-name-table
+ */
+ if ((NameThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG) != 0) // No ordinal
+ {
+ NameThunk++;
+ FuncThunk++;
+ continue; // Maybe Handle this in the future
+ }
+
+ /* Get Import data for this function */
+ PIMAGE_IMPORT_BY_NAME NameData = reinterpret_cast(ModuleBase + NameThunk->u1.ForwarderString);
+ PIMAGE_IMPORT_BY_NAME FunctionData = reinterpret_cast(FuncThunk->u1.AddressOfData);
+
+ //std::cout << "IMPORT: " << std::string(NameData->Name) << std::endl;
+
+ if (std::string(NameData->Name) == SearchFunctionName)
+ return FuncThunk;
+
+ NameThunk++;
+ FuncThunk++;
+ }
+ }
+
+ return nullptr;
+ }
+
+ /* Gets the address at which a pointer to an imported function is stored */
+ inline PIMAGE_THUNK_DATA GetImportAddress(const char* SearchModuleName, const char* ModuleToImportFrom, const char* SearchFunctionName)
+ {
+ const uintptr_t SearchModule = SearchModuleName ? reinterpret_cast(GetModuleAddress(SearchModuleName)) : GetImageBase();
+
+ return GetImportAddress(SearchModule, ModuleToImportFrom, SearchFunctionName);
+ }
+
+ /* Finds the import for a funciton and returns the address of the function from the imported module */
+ inline void* GetAddressOfImportedFunction(const char* SearchModuleName, const char* ModuleToImportFrom, const char* SearchFunctionName)
+ {
+ PIMAGE_THUNK_DATA FuncThunk = GetImportAddress(SearchModuleName, ModuleToImportFrom, SearchFunctionName);
+
+ if (!FuncThunk)
+ return nullptr;
+
+ return reinterpret_cast(FuncThunk->u1.AddressOfData);
+ }
+
+ inline void* GetAddressOfImportedFunctionFromAnyModule(const char* ModuleToImportFrom, const char* SearchFunctionName)
+ {
+ PEB* Peb = GetPEB();
+ PEB_LDR_DATA* Ldr = Peb->Ldr;
+
+ int NumEntriesLeft = Ldr->Length;
+
+ for (LIST_ENTRY* P = Ldr->InMemoryOrderModuleList.Flink; P && NumEntriesLeft-- > 0; P = P->Flink)
+ {
+ LDR_DATA_TABLE_ENTRY* Entry = reinterpret_cast(P);
+
+ PIMAGE_THUNK_DATA Import = GetImportAddress(reinterpret_cast(Entry->DllBase), ModuleToImportFrom, SearchFunctionName);
+
+ if (Import)
+ return reinterpret_cast(Import->u1.AddressOfData);
+ }
+
+ return nullptr;
+ }
+
+ /* Gets the address of an exported function */
+ inline void* GetExportAddress(const char* SearchModuleName, const char* SearchFunctionName)
+ {
+ /* Get the module the function was exported from */
+ uintptr_t ModuleBase = reinterpret_cast(GetModuleAddress(SearchModuleName));
+ PIMAGE_DOS_HEADER DosHeader = reinterpret_cast(ModuleBase);
+
+ if (ModuleBase == 0x0 || DosHeader->e_magic != IMAGE_DOS_SIGNATURE)
+ return nullptr;
+
+ PIMAGE_NT_HEADERS NtHeader = reinterpret_cast(ModuleBase + reinterpret_cast(ModuleBase)->e_lfanew);
+
+ if (!NtHeader)
+ return nullptr;
+
+ /* Get the table of functions exported by the module */
+ PIMAGE_EXPORT_DIRECTORY ExportTable = reinterpret_cast(ModuleBase + NtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
+
+ const DWORD* NameOffsets = reinterpret_cast(ModuleBase + ExportTable->AddressOfNames);
+ const DWORD* FunctionOffsets = reinterpret_cast(ModuleBase + ExportTable->AddressOfFunctions);
+
+ const WORD* Ordinals = reinterpret_cast(ModuleBase + ExportTable->AddressOfNameOrdinals);
+
+ /* Iterate all names and return the function if the name matches what we're looking for */
+ for (int i = 0; i < ExportTable->NumberOfFunctions; i++)
+ {
+ const WORD NameIndex = Ordinals[i];
+ const char* Name = reinterpret_cast(ModuleBase + NameOffsets[NameIndex]);
+
+ if (strcmp(SearchFunctionName, Name) == 0)
+ return reinterpret_cast(ModuleBase + FunctionOffsets[i]);
+ }
+
+ return nullptr;
+ }
+
+ inline void* FindPatternInRange(std::vector&& Signature, const uint8_t* Start, uintptr_t Range, bool bRelative = false, uint32_t Offset = 0, int SkipCount = 0)
+ {
+ const auto PatternLength = Signature.size();
+ const auto PatternBytes = Signature.data();
+
+ for (int i = 0; i < (Range - PatternLength); i++)
+ {
+ bool bFound = true;
+ int CurrentSkips = 0;
+
+ for (auto j = 0ul; j < PatternLength; ++j)
+ {
+ if (Start[i + j] != PatternBytes[j] && PatternBytes[j] != -1)
+ {
+ bFound = false;
+ break;
+ }
+ }
+ if (bFound)
+ {
+ if (CurrentSkips != SkipCount)
+ {
+ CurrentSkips++;
+ continue;
+ }
+
+ uintptr_t Address = uintptr_t(Start + i);
+ if (bRelative)
+ {
+ if (Offset == -1)
+ Offset = PatternLength;
+
+ Address = ((Address + Offset + 4) + *reinterpret_cast(Address + Offset));
+ }
+ return reinterpret_cast(Address);
+ }
+ }
+
+ return nullptr;
+ }
+
+ inline void* FindPatternInRange(const char* Signature, const uint8_t* Start, uintptr_t Range, bool bRelative = false, uint32_t Offset = 0)
+ {
+ static auto patternToByte = [](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, &Current, 16)); }
+ }
+ return Bytes;
+ };
+
+ return FindPatternInRange(patternToByte(Signature), Start, Range, bRelative, Offset);
+ }
+
+ inline void* FindPattern(const char* Signature, uint32_t Offset = 0, bool bSearchAllSegments = false, uintptr_t StartAddress = 0x0)
+ {
+ uintptr_t ImageBase = GetImageBase();
+
+ const PIMAGE_DOS_HEADER DosHeader = reinterpret_cast(ImageBase);
+ const PIMAGE_NT_HEADERS NtHeaders = reinterpret_cast(ImageBase + DosHeader->e_lfanew);
+
+ const DWORD SizeOfImage = NtHeaders->OptionalHeader.SizeOfImage;
+
+ uintptr_t SearchStart = ImageBase;
+ uintptr_t SearchRange = SizeOfImage;
+
+ if (!bSearchAllSegments)
+ {
+ PIMAGE_SECTION_HEADER Sections = IMAGE_FIRST_SECTION(NtHeaders);
+
+ uintptr_t TextSection = 0x0;
+ DWORD TextSize = 0;
+
+ for (int i = 0; i < NtHeaders->FileHeader.NumberOfSections; i++)
+ {
+ IMAGE_SECTION_HEADER& CurrentSection = Sections[i];
+
+ std::string SectionName = (const char*)CurrentSection.Name;
+
+ if (SectionName == ".text" && !TextSection)
+ {
+ TextSection = (ImageBase + CurrentSection.VirtualAddress);
+ TextSize = CurrentSection.Misc.VirtualSize;
+ }
+ }
+
+ SearchStart = TextSection;
+ SearchRange = TextSize;
+ }
+
+ const uintptr_t SearchEnd = ImageBase + SearchRange;
+
+ /* If the StartAddress is not default nullptr, and is out of memory-range */
+ if (StartAddress != 0x0 && (StartAddress < SearchStart || StartAddress > SearchEnd))
+ return nullptr;
+
+ /* Add a byte to the StartAddress to prevent instantly returning the previous result */
+ SearchStart = StartAddress != 0x0 ? (StartAddress + 0x1) : ImageBase;
+ SearchRange = StartAddress != 0x0 ? SearchEnd - StartAddress : SizeOfImage;
+
+ if (StartAddress == 0x0)
+ StartAddress = SearchStart;
+
+ return FindPatternInRange(Signature, reinterpret_cast(StartAddress), SearchRange, Offset != 0x0, Offset);
+ }
+
+
+ template
+ inline T* FindAlignedValueInProcessInRange(T Value, int32_t Alignment, uintptr_t StartAddress, uint32_t Range)
+ {
+ constexpr int32_t ElementSize = sizeof(T);
+
+ for (uint32_t i = 0x0; i < Range; i += Alignment)
+ {
+ T* TypedPtr = reinterpret_cast(StartAddress + i);
+
+ if (*TypedPtr == Value)
+ return TypedPtr;
+ }
+
+ return nullptr;
+ }
+
+ template
+ inline T* FindAlignedValueInProcess(T Value, const std::string& Sectionname = ".data", int32_t Alignment = alignof(T), bool bSearchAllSegments = false)
+ {
+ uint8_t* ImageBase = (uint8_t*)GetImageBase();
+
+ const auto DosHeader = (PIMAGE_DOS_HEADER)ImageBase;
+ const auto NtHeaders = (PIMAGE_NT_HEADERS)(ImageBase + DosHeader->e_lfanew);
+
+ const DWORD SizeOfImage = NtHeaders->OptionalHeader.SizeOfImage;
+
+ uint8_t* SearchStart = ImageBase;
+ DWORD SearchSize = SizeOfImage;
+
+ if (!bSearchAllSegments)
+ {
+ PIMAGE_SECTION_HEADER Sections = IMAGE_FIRST_SECTION(NtHeaders);
+
+ for (int i = 0; i < NtHeaders->FileHeader.NumberOfSections; i++)
+ {
+ IMAGE_SECTION_HEADER& CurrentSection = Sections[i];
+
+ std::string SectionName = (const char*)CurrentSection.Name;
+
+ if (SectionName == Sectionname)
+ {
+ SearchStart = (ImageBase + CurrentSection.VirtualAddress);
+ SearchSize = CurrentSection.Misc.VirtualSize;
+ break;
+ }
+ }
+ }
+
+ T* Result = FindAlignedValueInProcessInRange(Value, Alignment, reinterpret_cast(SearchStart), SearchSize);
+
+ if (!Result && SearchStart != ImageBase)
+ return FindAlignedValueInProcess(Value, Sectionname, Alignment, true);
+
+ return Result;
+ }
+
+ template
+ inline std::pair IterateVTableFunctions(void** VTable, const std::function& CallBackForEachFunc, int32_t NumFunctions = 0x150, int32_t OffsetFromStart = 0x0)
+ {
+ [[maybe_unused]] auto Resolve32BitRelativeJump = [](const void* FunctionPtr) -> const uint8_t*
+ {
+ if constexpr (bShouldRelove32BitJumps)
+ {
+ const uint8_t* Address = reinterpret_cast(FunctionPtr);
+ if (*Address == 0xE9)
+ {
+ const uint8_t* Ret = ((Address + 5) + *reinterpret_cast(Address + 1));
+
+ if (IsInProcessRange(uintptr_t(Ret)))
+ return Ret;
+ }
+ }
+
+ return reinterpret_cast(FunctionPtr);
+ };
+
+
+ if (!CallBackForEachFunc)
+ return { nullptr, -1 };
+
+ for (int i = 0; i < 0x150; i++)
+ {
+ const uintptr_t CurrentFuncAddress = reinterpret_cast(VTable[i]);
+
+ if (CurrentFuncAddress == NULL || !IsInProcessRange(CurrentFuncAddress))
+ break;
+
+ const uint8_t* ResolvedAddress = Resolve32BitRelativeJump(reinterpret_cast(CurrentFuncAddress));
+
+ if (CallBackForEachFunc(ResolvedAddress, i))
+ return { ResolvedAddress, i };
+ }
+
+ return { nullptr, -1 };
+ }
+
+ struct MemAddress
+ {
+ public:
+ uint8_t* Address;
+
+ private:
+ //pasted
+ static std::vector PatternToBytes(const char* pattern)
+ {
+ 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;
+ }
+
+ /* Function to determine whether this position is a function-return. Only "ret" instructions with pop operations before them and without immediate values are considered. */
+ static bool IsFunctionRet(uint8_t* Address)
+ {
+ if (!Address || (Address[0] != 0xC3 && Address[0] != 0xCB))
+ return false;
+
+ /* Opcodes representing pop instructions for x64 registers. Pop operations for r8-r15 are prefixed with 0x41. */
+ const uint8_t AsmBytePopOpcodes[] = { 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F };
+
+ const uint8_t ByteOneBeforeRet = Address[-1];
+ const uint8_t ByteTwoBeforeRet = Address[-2];
+
+ for (const uint8_t AsmPopByte : AsmBytePopOpcodes)
+ {
+ if (ByteOneBeforeRet == AsmPopByte)
+ return true;
+ }
+
+ return false;
+ }
+
+ public:
+ inline MemAddress(std::nullptr_t)
+ : Address(nullptr)
+ {
+ }
+ inline MemAddress(void* Addr)
+ : Address((uint8_t*)Addr)
+ {
+ }
+ inline MemAddress(uintptr_t Addr)
+ : Address((uint8_t*)Addr)
+ {
+ }
+
+ explicit operator bool()
+ {
+ return Address != nullptr;
+ }
+
+ template
+ explicit operator T* ()
+ {
+ return reinterpret_cast(Address);
+ }
+ operator void* ()
+ {
+ return Address;
+ }
+ operator uintptr_t()
+ {
+ return uintptr_t(Address);
+ }
+
+ inline MemAddress operator+(int Value) const
+ {
+ return Address + Value;
+ }
+
+ template
+ inline T* Get()
+ {
+ return Address;
+ }
+
+ /*
+ * Checks if the current address is a valid 32-bit relative 'jmp' instruction. and returns the address if true.
+ *
+ * If true: Returns resolved jump-target.
+ * If false: Returns current address.
+ */
+ inline MemAddress ResolveJumpIfInstructionIsJump(MemAddress DefaultReturnValueOnFail = nullptr) const
+ {
+ const uintptr_t AddrAsInt = reinterpret_cast(Address);
+
+ if (!ASMUtils::Is32BitRIPRelativeJump(AddrAsInt))
+ return DefaultReturnValueOnFail;
+
+ const uintptr_t TargetAddress = ASMUtils::Resolve32BitRIPRelativeJumpTarget(AddrAsInt);
+
+ if (!IsInProcessRange(TargetAddress))
+ return DefaultReturnValueOnFail;
+
+ return TargetAddress;
+ }
+
+ /* Helper to find the end of a function based on 'pop' instructions followed by 'ret' */
+ inline MemAddress FindFunctionEnd(uint32_t Range = 0xFFFF) const
+ {
+ if (!Address)
+ return nullptr;
+
+ if (Range > 0xFFFF)
+ Range = 0xFFFF;
+
+ for (int i = 0; i < Range; i++)
+ {
+ if (IsFunctionRet(Address + i))
+ return Address + i;
+ }
+
+ return nullptr;
+ }
+
+ /* Helper function to find a Pattern in a Range relative to the current position */
+ inline MemAddress RelativePattern(const char* Pattern, int32_t Range, int32_t Relative = 0) const
+ {
+ if (!Address)
+ return nullptr;
+
+ return FindPatternInRange(Pattern, Address, Range, Relative != 0, Relative);
+ }
+
+ /*
+ * A Function to find calls relative to the instruction pointer (RIP). Other calls are ignored.
+ *
+ * Disclaimers:
+ * Negative index to search up, positive index to search down.
+ * Function considers all E8 bytes as 'call' instructsion, that would make for a valid call (to address within process-bounds).
+ *
+ * OneBasedFuncIndex -> Index of a function we want to find, n-th sub_ in IDA starting from this MemAddress
+ * IsWantedTarget -> Allows for the caller to pass a callback to verify, that the function at index n is the target we're looking for; else continue searching for a valid target.
+ */
+ inline MemAddress GetRipRelativeCalledFunction(int32_t OneBasedFuncIndex, bool(*IsWantedTarget)(MemAddress CalledAddr) = nullptr) const
+ {
+ if (!Address || OneBasedFuncIndex == 0)
+ return nullptr;
+
+ const int32_t Multiply = OneBasedFuncIndex > 0 ? 1 : -1;
+
+ /* Returns Index if FunctionIndex is positive, else -1 if the index is less than 0 */
+ auto GetIndex = [=](int32_t Index) -> int32_t { return Index * Multiply; };
+
+ constexpr int32_t RealtiveCallOpcodeCount = 0x5;
+
+ int32_t NumCalls = 0;
+
+ for (int i = 0; i < 0xFFF; i++)
+ {
+ const int32_t Index = GetIndex(i);
+
+ /* If this isn't a call, we don't care about it and want to continue */
+ if (Address[Index] != 0xE8)
+ continue;
+
+ const int32_t RelativeOffset = *reinterpret_cast(Address + Index + 0x1 /* 0xE8 byte */);
+ void* RelativeCallTarget = Address + Index + RelativeOffset + RealtiveCallOpcodeCount;
+
+ if (!IsInProcessRange(reinterpret_cast(RelativeCallTarget)))
+ continue;
+
+ if (++NumCalls == abs(OneBasedFuncIndex))
+ {
+ /* This is not the target we wanted, even tho it's at the right index. Decrement the index to the value before and check if the next call satisfies the custom-condition. */
+ if (IsWantedTarget && !IsWantedTarget(RelativeCallTarget))
+ {
+ --NumCalls;
+ continue;
+ }
+
+ return RelativeCallTarget;
+ }
+ }
+
+ return nullptr;
+ }
+
+ /* Note: Unrealiable */
+ inline MemAddress FindNextFunctionStart() const
+ {
+ if (!Address)
+ return MemAddress(nullptr);
+
+ uintptr_t FuncEnd = (uintptr_t)FindFunctionEnd();
+
+ return FuncEnd % 0x10 != 0 ? FuncEnd + (0x10 - (FuncEnd % 0x10)) : FuncEnd;
+ }
+ };
+
+ template
+ inline MemAddress FindByString(Type RefStr)
+ {
+ uintptr_t ImageBase = GetImageBase();
+ PIMAGE_DOS_HEADER DosHeader = (PIMAGE_DOS_HEADER)(ImageBase);
+ PIMAGE_NT_HEADERS NtHeader = (PIMAGE_NT_HEADERS)(ImageBase + DosHeader->e_lfanew);
+ PIMAGE_SECTION_HEADER Sections = IMAGE_FIRST_SECTION(NtHeader);
+
+ uint8_t* DataSection = nullptr;
+ uint8_t* TextSection = nullptr;
+ DWORD DataSize = 0;
+ DWORD TextSize = 0;
+
+ uint8_t* StringAddress = nullptr;
+
+ for (int i = 0; i < NtHeader->FileHeader.NumberOfSections; i++)
+ {
+ IMAGE_SECTION_HEADER& CurrentSection = Sections[i];
+
+ std::string SectionName = (const char*)CurrentSection.Name;
+
+ if (SectionName == ".rdata" && !DataSection)
+ {
+ DataSection = (uint8_t*)(CurrentSection.VirtualAddress + ImageBase);
+ DataSize = CurrentSection.Misc.VirtualSize;
+ }
+ else if (SectionName == ".text" && !TextSection)
+ {
+ TextSection = (uint8_t*)(CurrentSection.VirtualAddress + ImageBase);
+ TextSize = CurrentSection.Misc.VirtualSize;
+ }
+ }
+
+ for (int i = 0; i < DataSize; i++)
+ {
+ if constexpr (std::is_same())
+ {
+ if (strcmp((const char*)RefStr, (const char*)(DataSection + i)) == 0)
+ {
+ //std::cout << "FoundStr ref: " << (const char*)(DataSection + i) << "\n";
+
+ StringAddress = DataSection + i;
+ }
+ }
+ else
+ {
+ if (wcscmp((const wchar_t*)RefStr, (const wchar_t*)(DataSection + i)) == 0)
+ {
+ //std::wcout << L"FoundStr wref: " << (const wchar_t*)(DataSection + i) << L"\n";
+
+ StringAddress = DataSection + i;
+ }
+ }
+ }
+
+ for (int i = 0; i < TextSize; i++)
+ {
+ // opcode: lea
+ if ((TextSection[i] == uint8_t(0x4C) || TextSection[i] == uint8_t(0x48)) && TextSection[i + 1] == uint8_t(0x8D))
+ {
+ const uint8_t* StrPtr = *(int32_t*)(TextSection + i + 3) + 7 + TextSection + i;
+
+ if (StrPtr == StringAddress)
+ {
+ //std::cout << "Found Address: 0x" << (void*)(TextSection + i) << "\n";
+
+ return { TextSection + i };
+ }
+ }
+ }
+
+ return nullptr;
+ }
+
+ inline MemAddress FindByWString(const wchar_t* RefStr)
+ {
+ return FindByString(RefStr);
+ }
+
+ template
+ inline int32_t StrlenHelper(const CharType* Str)
+ {
+ if constexpr (std::is_same())
+ {
+ return strlen(Str);
+ }
+ else
+ {
+ return wcslen(Str);
+ }
+ }
+
+ /* Slower than FindByString */
+ template
+ inline MemAddress FindByStringInAllSections(Type RefStr, uintptr_t StartAddress = 0x0, int32_t Range = 0x0)
+ {
+ /* Stop scanning when arriving 0x10 bytes before the end of the memory range */
+ constexpr int32_t OffsetFromMemoryEnd = 0x10;
+
+ uintptr_t ImageBase = GetImageBase();
+ PIMAGE_DOS_HEADER DosHeader = (PIMAGE_DOS_HEADER)(ImageBase);
+ PIMAGE_NT_HEADERS NtHeader = (PIMAGE_NT_HEADERS)(ImageBase + DosHeader->e_lfanew);
+
+ const DWORD SizeOfImage = NtHeader->OptionalHeader.SizeOfImage;
+
+ const uintptr_t ImageEnd = ImageBase + SizeOfImage;
+
+ /* If the StartAddress is not default nullptr, and is out of memory-range */
+ if (StartAddress != 0x0 && (StartAddress < ImageBase || StartAddress > ImageEnd))
+ return nullptr;
+
+ /* Add a few bytes to the StartAddress to prevent instantly returning the previous result */
+ uint8_t* SearchStart = StartAddress ? (reinterpret_cast(StartAddress) + 0x5) : reinterpret_cast(ImageBase);
+ DWORD SearchRange = StartAddress ? ImageEnd - StartAddress : SizeOfImage;
+
+ if (Range != 0x0)
+ SearchRange = min(Range, SearchRange);
+
+ if ((StartAddress + SearchRange) >= ImageEnd)
+ SearchRange -= OffsetFromMemoryEnd;
+
+ const int32_t RefStrLen = StrlenHelper(RefStr);
+
+ for (uintptr_t i = 0; i < SearchRange; i++)
+ {
+ // opcode: lea
+ if ((SearchStart[i] == uint8_t(0x4C) || SearchStart[i] == uint8_t(0x48)) && SearchStart[i + 1] == uint8_t(0x8D))
+ {
+ const uint8_t* StrPtr = *reinterpret_cast(SearchStart + i + 3) + 7 + SearchStart + i;
+
+ if (!IsInProcessRange(reinterpret_cast(StrPtr)))
+ continue;
+
+ if constexpr (std::is_same())
+ {
+ if (strncmp(reinterpret_cast(RefStr), reinterpret_cast(StrPtr), RefStrLen) == 0)
+ {
+ // std::cout << "FoundStr ref: " << (const char*)(SearchStart + i) << "\n";
+
+ return { SearchStart + i };
+ }
+ }
+ else
+ {
+ if (wcsncmp(reinterpret_cast(RefStr), reinterpret_cast(StrPtr), RefStrLen) == 0)
+ {
+ // std::wcout << L"FoundStr wref: " << (const wchar_t*)(SearchStart + i) << L"\n";
+
+ return { SearchStart + i };
+ }
+ }
+ }
+ }
+
+ return nullptr;
+ }
+
+ template
+ inline MemAddress FindUnrealExecFunctionByString(Type RefStr, void* StartAddress = nullptr)
+ {
+ uintptr_t ImageBase = GetImageBase();
+ PIMAGE_DOS_HEADER DosHeader = (PIMAGE_DOS_HEADER)(ImageBase);
+ PIMAGE_NT_HEADERS NtHeader = (PIMAGE_NT_HEADERS)(ImageBase + DosHeader->e_lfanew);
+
+ const DWORD SizeOfImage = NtHeader->OptionalHeader.SizeOfImage;
+
+ uint8_t* SearchStart = StartAddress ? reinterpret_cast(StartAddress) : reinterpret_cast(ImageBase);
+ DWORD SearchRange = SizeOfImage;
+
+ const int32_t RefStrLen = StrlenHelper(RefStr);
+
+ static auto IsValidExecFunctionNotSetupFunc = [](uintptr_t Address) -> bool
+ {
+ /*
+ * UFuntion construction functions setting up exec functions always start with these asm instructions:
+ * sub rsp, 28h
+ *
+ * In opcode bytes: 48 83 EC 28
+ */
+ if (*reinterpret_cast(Address) == 0x284883EC || *reinterpret_cast(Address) == 0x4883EC28)
+ return false;
+
+ MemAddress AsAddress(Address);
+
+ /* A signature specifically made for UFunctions-construction functions. If this signature is found we're in a function that we *don't* want. */
+ if (AsAddress.RelativePattern("48 8B 05 ? ? ? ? 48 85 C0 75 ? 48 8D 15", 0x28) != nullptr)
+ return false;
+
+ return true;
+ };
+
+ for (uintptr_t i = 0; i < (SearchRange - 0x8); i += sizeof(void*))
+ {
+ const uintptr_t PossibleStringAddress = *reinterpret_cast(SearchStart + i);
+ const uintptr_t PossibleExecFuncAddress = *reinterpret_cast(SearchStart + i + sizeof(void*));
+
+ if (PossibleStringAddress == PossibleExecFuncAddress)
+ continue;
+
+ if (!IsInProcessRange(PossibleStringAddress) || !IsInProcessRange(PossibleExecFuncAddress))
+ continue;
+
+ if constexpr (std::is_same())
+ {
+ if (strncmp(reinterpret_cast(RefStr), reinterpret_cast(PossibleStringAddress), RefStrLen) == 0 && IsValidExecFunctionNotSetupFunc(PossibleExecFuncAddress))
+ {
+ // std::cout << "FoundStr ref: " << reinterpret_cast(PossibleStringAddress) << "\n";
+
+ return { PossibleExecFuncAddress };
+ }
+ }
+ else
+ {
+ if (wcsncmp(reinterpret_cast(RefStr), reinterpret_cast(PossibleStringAddress), RefStrLen) == 0 && IsValidExecFunctionNotSetupFunc(PossibleExecFuncAddress))
+ {
+ // std::wcout << L"FoundStr wref: " << reinterpret_cast(PossibleStringAddress) << L"\n";
+
+ return { PossibleExecFuncAddress };
+ }
+ }
+ }
+
+ return nullptr;
+ }
+
+ /* Slower than FindByWString */
+ inline MemAddress FindByWStringInAllSections(const wchar_t* RefStr)
+ {
+ return FindByStringInAllSections(RefStr);
+ }
+
+
+ namespace FileNameHelper
+ {
+ inline void MakeValidFileName(std::string& InOutName)
+ {
+ for (char& c : InOutName)
+ {
+ if (c == '<' || c == '>' || c == ':' || c == '\"' || c == '/' || c == '\\' || c == '|' || c == '?' || c == '*')
+ c = '_';
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/F4Menu/dllmain.cpp b/F4Menu/dllmain.cpp
index 2d13005ff..32cd7fe24 100644
--- a/F4Menu/dllmain.cpp
+++ b/F4Menu/dllmain.cpp
@@ -20,6 +20,7 @@ void Welcome()
{
AllocateConsole();
ShowConsole();
+ Initialize();
}
BOOL APIENTRY DllMain( HMODULE hModule,
diff --git a/F4Menu/framework.h b/F4Menu/framework.h
index 0e77955d4..2201ab73e 100644
--- a/F4Menu/framework.h
+++ b/F4Menu/framework.h
@@ -1,10 +1,18 @@
#pragma once
-#include "string"
+#include
+using namespace std;
#include
#include
// UE4
#include "SDK/SDK/Engine_classes.hpp"
#include "globals.h"
-#include "Engine.h"
\ No newline at end of file
+#include "Engine.h"
+#include "gameplay.h"
+
+// 3rd Party
+#include "ThirdParty/MinHook/include/MinHook.h"
+#include "kiero.h"
+#include "memcury.h"
+#include "Memory.h"
\ No newline at end of file
diff --git a/F4Menu/gameplay.h b/F4Menu/gameplay.h
new file mode 100644
index 000000000..ca2aef67f
--- /dev/null
+++ b/F4Menu/gameplay.h
@@ -0,0 +1,22 @@
+#pragma once
+#include "globals.h"
+#include "framework.h"
+
+void (*oProcessEvent)(SDK::UObject* Object, SDK::UFunction* Function, void* Parameters);
+void hkProcessEvent(SDK::UObject* Object, SDK::UFunction* Function, void* Parameters)
+{
+ string FunctionName = Function->GetName();
+
+ if (FunctionName == "Tick");
+ {
+ std::cout << "Tick Logged!" << std::endl;
+ }
+
+ return oProcessEvent(Object, Function, Parameters);
+}
+
+void Initialize()
+{
+ MH_CreateHook((void*)(Memory::GetAddress(SDK::Offsets::ProcessEvent)), hkProcessEvent, (void**)&oProcessEvent);
+ MH_EnableHook((void*)(Memory::GetAddress(SDK::Offsets::ProcessEvent)));
+}
\ No newline at end of file
diff --git a/F4Menu/kiero.cpp b/F4Menu/kiero.cpp
new file mode 100644
index 000000000..5bbcdb01d
--- /dev/null
+++ b/F4Menu/kiero.cpp
@@ -0,0 +1,720 @@
+#include "kiero.h"
+#include
+#include
+
+#if KIERO_INCLUDE_D3D9
+# include
+#endif
+
+#if KIERO_INCLUDE_D3D10
+# include
+# include
+# include
+#endif
+
+#if KIERO_INCLUDE_D3D11
+# include
+# include
+#endif
+
+#if KIERO_INCLUDE_D3D12
+# include
+# include
+#endif
+
+#if KIERO_INCLUDE_OPENGL
+# include
+#endif
+
+#if KIERO_INCLUDE_VULKAN
+# include
+#endif
+
+#if KIERO_USE_MINHOOK
+#include "ThirdParty/MinHook/include/MinHook.h"
+#endif
+
+#ifdef _UNICODE
+# define KIERO_TEXT(text) L##text
+#else
+# define KIERO_TEXT(text) text
+#endif
+
+#define KIERO_ARRAY_SIZE(arr) ((size_t)(sizeof(arr)/sizeof(arr[0])))
+
+static kiero::RenderType::Enum g_renderType = kiero::RenderType::None;
+static uint150_t* g_methodsTable = NULL;
+
+kiero::Status::Enum kiero::init(RenderType::Enum _renderType)
+{
+ if (g_renderType != RenderType::None)
+ {
+ return Status::AlreadyInitializedError;
+ }
+
+ if (_renderType != RenderType::None)
+ {
+ if (_renderType >= RenderType::D3D9 && _renderType <= RenderType::D3D12)
+ {
+ WNDCLASSEX windowClass;
+ windowClass.cbSize = sizeof(WNDCLASSEX);
+ windowClass.style = CS_HREDRAW | CS_VREDRAW;
+ windowClass.lpfnWndProc = DefWindowProc;
+ windowClass.cbClsExtra = 0;
+ windowClass.cbWndExtra = 0;
+ windowClass.hInstance = GetModuleHandle(NULL);
+ windowClass.hIcon = NULL;
+ windowClass.hCursor = NULL;
+ windowClass.hbrBackground = NULL;
+ windowClass.lpszMenuName = NULL;
+ windowClass.lpszClassName = KIERO_TEXT("Kiero");
+ windowClass.hIconSm = NULL;
+
+ ::RegisterClassEx(&windowClass);
+
+ HWND window = ::CreateWindow(windowClass.lpszClassName, KIERO_TEXT("Kiero DirectX Window"), WS_OVERLAPPEDWINDOW, 0, 0, 100, 100, NULL, NULL, windowClass.hInstance, NULL);
+
+ if (_renderType == RenderType::D3D9)
+ {
+#if KIERO_INCLUDE_D3D9
+ HMODULE libD3D9;
+ if ((libD3D9 = ::GetModuleHandle(KIERO_TEXT("d3d9.dll"))) == NULL)
+ {
+ ::DestroyWindow(window);
+ ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
+ return Status::ModuleNotFoundError;
+ }
+
+ void* Direct3DCreate9;
+ if ((Direct3DCreate9 = ::GetProcAddress(libD3D9, "Direct3DCreate9")) == NULL)
+ {
+ ::DestroyWindow(window);
+ ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
+ return Status::UnknownError;
+ }
+
+ LPDIRECT3D9 direct3D9;
+ if ((direct3D9 = ((LPDIRECT3D9(__stdcall*)(uint32_t))(Direct3DCreate9))(D3D_SDK_VERSION)) == NULL)
+ {
+ ::DestroyWindow(window);
+ ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
+ return Status::UnknownError;
+ }
+
+ D3DDISPLAYMODE displayMode;
+ if (direct3D9->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &displayMode) < 0)
+ {
+ ::DestroyWindow(window);
+ ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
+ return Status::UnknownError;
+ }
+
+ D3DPRESENT_PARAMETERS params;
+ params.BackBufferWidth = 0;
+ params.BackBufferHeight = 0;
+ params.BackBufferFormat = displayMode.Format;
+ params.BackBufferCount = 0;
+ params.MultiSampleType = D3DMULTISAMPLE_NONE;
+ params.MultiSampleQuality = NULL;
+ params.SwapEffect = D3DSWAPEFFECT_DISCARD;
+ params.hDeviceWindow = window;
+ params.Windowed = 1;
+ params.EnableAutoDepthStencil = 0;
+ params.AutoDepthStencilFormat = D3DFMT_UNKNOWN;
+ params.Flags = NULL;
+ params.FullScreen_RefreshRateInHz = 0;
+ params.PresentationInterval = 0;
+
+ LPDIRECT3DDEVICE9 device;
+ if (direct3D9->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, window, D3DCREATE_SOFTWARE_VERTEXPROCESSING | D3DCREATE_DISABLE_DRIVER_MANAGEMENT, ¶ms, &device) < 0)
+ {
+ direct3D9->Release();
+ ::DestroyWindow(window);
+ ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
+ return Status::UnknownError;
+ }
+
+ g_methodsTable = (uint150_t*)::calloc(119, sizeof(uint150_t));
+ ::memcpy(g_methodsTable, *(uint150_t**)device, 119 * sizeof(uint150_t));
+
+#if KIERO_USE_MINHOOK
+ MH_Initialize();
+#endif
+
+ direct3D9->Release();
+ direct3D9 = NULL;
+
+ device->Release();
+ device = NULL;
+
+ g_renderType = RenderType::D3D9;
+
+ ::DestroyWindow(window);
+ ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
+
+ return Status::Success;
+#endif
+ }
+ else if (_renderType == RenderType::D3D10)
+ {
+#if KIERO_INCLUDE_D3D10
+ HMODULE libDXGI;
+ HMODULE libD3D10;
+ if ((libDXGI = ::GetModuleHandle(KIERO_TEXT("dxgi.dll"))) == NULL || (libD3D10 = ::GetModuleHandle(KIERO_TEXT("d3d10.dll"))) == NULL)
+ {
+ ::DestroyWindow(window);
+ ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
+ return Status::ModuleNotFoundError;
+ }
+
+ void* CreateDXGIFactory;
+ if ((CreateDXGIFactory = ::GetProcAddress(libDXGI, "CreateDXGIFactory")) == NULL)
+ {
+ ::DestroyWindow(window);
+ ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
+ return Status::UnknownError;
+ }
+
+ IDXGIFactory* factory;
+ if (((long(__stdcall*)(const IID&, void**))(CreateDXGIFactory))(__uuidof(IDXGIFactory), (void**)&factory) < 0)
+ {
+ ::DestroyWindow(window);
+ ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
+ return Status::UnknownError;
+ }
+
+ IDXGIAdapter* adapter;
+ if (factory->EnumAdapters(0, &adapter) == DXGI_ERROR_NOT_FOUND)
+ {
+ ::DestroyWindow(window);
+ ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
+ return Status::UnknownError;
+ }
+
+ void* D3D10CreateDeviceAndSwapChain;
+ if ((D3D10CreateDeviceAndSwapChain = ::GetProcAddress(libD3D10, "D3D10CreateDeviceAndSwapChain")) == NULL)
+ {
+ ::DestroyWindow(window);
+ ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
+ return Status::UnknownError;
+ }
+
+ DXGI_RATIONAL refreshRate;
+ refreshRate.Numerator = 60;
+ refreshRate.Denominator = 1;
+
+ DXGI_MODE_DESC bufferDesc;
+ bufferDesc.Width = 100;
+ bufferDesc.Height = 100;
+ bufferDesc.RefreshRate = refreshRate;
+ bufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
+ bufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
+ bufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
+
+ DXGI_SAMPLE_DESC sampleDesc;
+ sampleDesc.Count = 1;
+ sampleDesc.Quality = 0;
+
+ DXGI_SWAP_CHAIN_DESC swapChainDesc;
+ swapChainDesc.BufferDesc = bufferDesc;
+ swapChainDesc.SampleDesc = sampleDesc;
+ swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
+ swapChainDesc.BufferCount = 1;
+ swapChainDesc.OutputWindow = window;
+ swapChainDesc.Windowed = 1;
+ swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
+ swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
+
+ IDXGISwapChain* swapChain;
+ ID3D10Device* device;
+
+ if (((long(__stdcall*)(
+ IDXGIAdapter*,
+ D3D10_DRIVER_TYPE,
+ HMODULE,
+ UINT,
+ UINT,
+ DXGI_SWAP_CHAIN_DESC*,
+ IDXGISwapChain**,
+ ID3D10Device**))(D3D10CreateDeviceAndSwapChain))(adapter, D3D10_DRIVER_TYPE_HARDWARE, NULL, 0, D3D10_SDK_VERSION, &swapChainDesc, &swapChain, &device) < 0)
+ {
+ ::DestroyWindow(window);
+ ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
+ return Status::UnknownError;
+ }
+
+ g_methodsTable = (uint150_t*)::calloc(116, sizeof(uint150_t));
+ ::memcpy(g_methodsTable, *(uint150_t**)swapChain, 18 * sizeof(uint150_t));
+ ::memcpy(g_methodsTable + 18, *(uint150_t**)device, 98 * sizeof(uint150_t));
+
+#if KIERO_USE_MINHOOK
+ MH_Initialize();
+#endif
+
+ swapChain->Release();
+ swapChain = NULL;
+
+ device->Release();
+ device = NULL;
+
+ ::DestroyWindow(window);
+ ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
+
+ g_renderType = RenderType::D3D10;
+
+ return Status::Success;
+#endif
+ }
+ else if (_renderType == RenderType::D3D11)
+ {
+#if KIERO_INCLUDE_D3D11
+ HMODULE libD3D11;
+ if ((libD3D11 = ::GetModuleHandle(KIERO_TEXT("d3d11.dll"))) == NULL)
+ {
+ ::DestroyWindow(window);
+ ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
+ return Status::ModuleNotFoundError;
+ }
+
+ void* D3D11CreateDeviceAndSwapChain;
+ if ((D3D11CreateDeviceAndSwapChain = ::GetProcAddress(libD3D11, "D3D11CreateDeviceAndSwapChain")) == NULL)
+ {
+ ::DestroyWindow(window);
+ ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
+ return Status::UnknownError;
+ }
+
+ D3D_FEATURE_LEVEL featureLevel;
+ const D3D_FEATURE_LEVEL featureLevels[] = { D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_11_0 };
+
+ DXGI_RATIONAL refreshRate;
+ refreshRate.Numerator = 60;
+ refreshRate.Denominator = 1;
+
+ DXGI_MODE_DESC bufferDesc;
+ bufferDesc.Width = 100;
+ bufferDesc.Height = 100;
+ bufferDesc.RefreshRate = refreshRate;
+ bufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
+ bufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
+ bufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
+
+ DXGI_SAMPLE_DESC sampleDesc;
+ sampleDesc.Count = 1;
+ sampleDesc.Quality = 0;
+
+ DXGI_SWAP_CHAIN_DESC swapChainDesc;
+ swapChainDesc.BufferDesc = bufferDesc;
+ swapChainDesc.SampleDesc = sampleDesc;
+ swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
+ swapChainDesc.BufferCount = 1;
+ swapChainDesc.OutputWindow = window;
+ swapChainDesc.Windowed = 1;
+ swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
+ swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
+
+ IDXGISwapChain* swapChain;
+ ID3D11Device* device;
+ ID3D11DeviceContext* context;
+
+ if (((long(__stdcall*)(
+ IDXGIAdapter*,
+ D3D_DRIVER_TYPE,
+ HMODULE,
+ UINT,
+ const D3D_FEATURE_LEVEL*,
+ UINT,
+ UINT,
+ const DXGI_SWAP_CHAIN_DESC*,
+ IDXGISwapChain**,
+ ID3D11Device**,
+ D3D_FEATURE_LEVEL*,
+ ID3D11DeviceContext**))(D3D11CreateDeviceAndSwapChain))(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, 0, featureLevels, 1, D3D11_SDK_VERSION, &swapChainDesc, &swapChain, &device, &featureLevel, &context) < 0)
+ {
+ ::DestroyWindow(window);
+ ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
+ return Status::UnknownError;
+ }
+
+ g_methodsTable = (uint150_t*)::calloc(205, sizeof(uint150_t));
+ ::memcpy(g_methodsTable, *(uint150_t**)swapChain, 18 * sizeof(uint150_t));
+ ::memcpy(g_methodsTable + 18, *(uint150_t**)device, 43 * sizeof(uint150_t));
+ ::memcpy(g_methodsTable + 18 + 43, *(uint150_t**)context, 144 * sizeof(uint150_t));
+
+#if KIERO_USE_MINHOOK
+ MH_Initialize();
+#endif
+
+ swapChain->Release();
+ swapChain = NULL;
+
+ device->Release();
+ device = NULL;
+
+ context->Release();
+ context = NULL;
+
+ ::DestroyWindow(window);
+ ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
+
+ g_renderType = RenderType::D3D11;
+
+ return Status::Success;
+#endif
+ }
+ else if (_renderType == RenderType::D3D12)
+ {
+#if KIERO_INCLUDE_D3D12
+ HMODULE libDXGI;
+ HMODULE libD3D12;
+ if ((libDXGI = ::GetModuleHandle(KIERO_TEXT("dxgi.dll"))) == NULL || (libD3D12 = ::GetModuleHandle(KIERO_TEXT("d3d12.dll"))) == NULL)
+ {
+ ::DestroyWindow(window);
+ ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
+ return Status::ModuleNotFoundError;
+ }
+
+ void* CreateDXGIFactory;
+ if ((CreateDXGIFactory = ::GetProcAddress(libDXGI, "CreateDXGIFactory")) == NULL)
+ {
+ ::DestroyWindow(window);
+ ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
+ return Status::UnknownError;
+ }
+
+ IDXGIFactory* factory;
+ if (((long(__stdcall*)(const IID&, void**))(CreateDXGIFactory))(__uuidof(IDXGIFactory), (void**)&factory) < 0)
+ {
+ ::DestroyWindow(window);
+ ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
+ return Status::UnknownError;
+ }
+
+ IDXGIAdapter* adapter;
+ if (factory->EnumAdapters(0, &adapter) == DXGI_ERROR_NOT_FOUND)
+ {
+ ::DestroyWindow(window);
+ ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
+ return Status::UnknownError;
+ }
+
+ void* D3D12CreateDevice;
+ if ((D3D12CreateDevice = ::GetProcAddress(libD3D12, "D3D12CreateDevice")) == NULL)
+ {
+ ::DestroyWindow(window);
+ ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
+ return Status::UnknownError;
+ }
+
+ ID3D12Device* device;
+ if (((long(__stdcall*)(IUnknown*, D3D_FEATURE_LEVEL, const IID&, void**))(D3D12CreateDevice))(adapter, D3D_FEATURE_LEVEL_11_0, __uuidof(ID3D12Device), (void**)&device) < 0)
+ {
+ ::DestroyWindow(window);
+ ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
+ return Status::UnknownError;
+ }
+
+ D3D12_COMMAND_QUEUE_DESC queueDesc;
+ queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
+ queueDesc.Priority = 0;
+ queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
+ queueDesc.NodeMask = 0;
+
+ ID3D12CommandQueue* commandQueue;
+ if (device->CreateCommandQueue(&queueDesc, __uuidof(ID3D12CommandQueue), (void**)&commandQueue) < 0)
+ {
+ ::DestroyWindow(window);
+ ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
+ return Status::UnknownError;
+ }
+
+ ID3D12CommandAllocator* commandAllocator;
+ if (device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, __uuidof(ID3D12CommandAllocator), (void**)&commandAllocator) < 0)
+ {
+ ::DestroyWindow(window);
+ ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
+ return Status::UnknownError;
+ }
+
+ ID3D12GraphicsCommandList* commandList;
+ if (device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, commandAllocator, NULL, __uuidof(ID3D12GraphicsCommandList), (void**)&commandList) < 0)
+ {
+ ::DestroyWindow(window);
+ ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
+ return Status::UnknownError;
+ }
+
+ DXGI_RATIONAL refreshRate;
+ refreshRate.Numerator = 60;
+ refreshRate.Denominator = 1;
+
+ DXGI_MODE_DESC bufferDesc;
+ bufferDesc.Width = 100;
+ bufferDesc.Height = 100;
+ bufferDesc.RefreshRate = refreshRate;
+ bufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
+ bufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
+ bufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
+
+ DXGI_SAMPLE_DESC sampleDesc;
+ sampleDesc.Count = 1;
+ sampleDesc.Quality = 0;
+
+ DXGI_SWAP_CHAIN_DESC swapChainDesc = {};
+ swapChainDesc.BufferDesc = bufferDesc;
+ swapChainDesc.SampleDesc = sampleDesc;
+ swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
+ swapChainDesc.BufferCount = 2;
+ swapChainDesc.OutputWindow = window;
+ swapChainDesc.Windowed = 1;
+ swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
+ swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
+
+ IDXGISwapChain* swapChain;
+ if (factory->CreateSwapChain(commandQueue, &swapChainDesc, &swapChain) < 0)
+ {
+ ::DestroyWindow(window);
+ ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
+ return Status::UnknownError;
+ }
+
+ g_methodsTable = (uint150_t*)::calloc(150, sizeof(uint150_t));
+ ::memcpy(g_methodsTable, *(uint150_t**)device, 44 * sizeof(uint150_t));
+ ::memcpy(g_methodsTable + 44, *(uint150_t**)commandQueue, 19 * sizeof(uint150_t));
+ ::memcpy(g_methodsTable + 44 + 19, *(uint150_t**)commandAllocator, 9 * sizeof(uint150_t));
+ ::memcpy(g_methodsTable + 44 + 19 + 9, *(uint150_t**)commandList, 60 * sizeof(uint150_t));
+ ::memcpy(g_methodsTable + 44 + 19 + 9 + 60, *(uint150_t**)swapChain, 18 * sizeof(uint150_t));
+
+#if KIERO_USE_MINHOOK
+ MH_Initialize();
+#endif
+
+ device->Release();
+ device = NULL;
+
+ commandQueue->Release();
+ commandQueue = NULL;
+
+ commandAllocator->Release();
+ commandAllocator = NULL;
+
+ commandList->Release();
+ commandList = NULL;
+
+ swapChain->Release();
+ swapChain = NULL;
+
+ ::DestroyWindow(window);
+ ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
+
+ g_renderType = RenderType::D3D12;
+
+ return Status::Success;
+#endif
+ }
+
+ ::DestroyWindow(window);
+ ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
+
+ return Status::NotSupportedError;
+ }
+ else if (_renderType == RenderType::OpenGL)
+ {
+#if KIERO_INCLUDE_OPENGL
+ HMODULE libOpenGL32;
+ if ((libOpenGL32 = ::GetModuleHandle(KIERO_TEXT("opengl32.dll"))) == NULL)
+ {
+ return Status::ModuleNotFoundError;
+ }
+
+ const char* const methodsNames[] = {
+ "glAccum", "glAlphaFunc", "glAreTexturesResident", "glArrayElement", "glBegin", "glBindTexture", "glBitmap", "glBlendFunc", "glCallList", "glCallLists", "glClear", "glClearAccum",
+ "glClearColor", "glClearDepth", "glClearIndex", "glClearStencil", "glClipPlane", "glColor3b", "glColor3bv", "glColor3d", "glColor3dv", "glColor3f", "glColor3fv", "glColor3i", "glColor3iv",
+ "glColor3s", "glColor3sv", "glColor3ub", "glColor3ubv", "glColor3ui", "glColor3uiv", "glColor3us", "glColor3usv", "glColor4b", "glColor4bv", "glColor4d", "glColor4dv", "glColor4f",
+ "glColor4fv", "glColor4i", "glColor4iv", "glColor4s", "glColor4sv", "glColor4ub", "glColor4ubv", "glColor4ui", "glColor4uiv", "glColor4us", "glColor4usv", "glColorMask", "glColorMaterial",
+ "glColorPointer", "glCopyPixels", "glCopyTexImage1D", "glCopyTexImage2D", "glCopyTexSubImage1D", "glCopyTexSubImage2D", "glCullFaceglCullFace", "glDeleteLists", "glDeleteTextures",
+ "glDepthFunc", "glDepthMask", "glDepthRange", "glDisable", "glDisableClientState", "glDrawArrays", "glDrawBuffer", "glDrawElements", "glDrawPixels", "glEdgeFlag", "glEdgeFlagPointer",
+ "glEdgeFlagv", "glEnable", "glEnableClientState", "glEnd", "glEndList", "glEvalCoord1d", "glEvalCoord1dv", "glEvalCoord1f", "glEvalCoord1fv", "glEvalCoord2d", "glEvalCoord2dv",
+ "glEvalCoord2f", "glEvalCoord2fv", "glEvalMesh1", "glEvalMesh2", "glEvalPoint1", "glEvalPoint2", "glFeedbackBuffer", "glFinish", "glFlush", "glFogf", "glFogfv", "glFogi", "glFogiv",
+ "glFrontFace", "glFrustum", "glGenLists", "glGenTextures", "glGetBooleanv", "glGetClipPlane", "glGetDoublev", "glGetError", "glGetFloatv", "glGetIntegerv", "glGetLightfv", "glGetLightiv",
+ "glGetMapdv", "glGetMapfv", "glGetMapiv", "glGetMaterialfv", "glGetMaterialiv", "glGetPixelMapfv", "glGetPixelMapuiv", "glGetPixelMapusv", "glGetPointerv", "glGetPolygonStipple",
+ "glGetString", "glGetTexEnvfv", "glGetTexEnviv", "glGetTexGendv", "glGetTexGenfv", "glGetTexGeniv", "glGetTexImage", "glGetTexLevelParameterfv", "glGetTexLevelParameteriv",
+ "glGetTexParameterfv", "glGetTexParameteriv", "glHint", "glIndexMask", "glIndexPointer", "glIndexd", "glIndexdv", "glIndexf", "glIndexfv", "glIndexi", "glIndexiv", "glIndexs", "glIndexsv",
+ "glIndexub", "glIndexubv", "glInitNames", "glInterleavedArrays", "glIsEnabled", "glIsList", "glIsTexture", "glLightModelf", "glLightModelfv", "glLightModeli", "glLightModeliv", "glLightf",
+ "glLightfv", "glLighti", "glLightiv", "glLineStipple", "glLineWidth", "glListBase", "glLoadIdentity", "glLoadMatrixd", "glLoadMatrixf", "glLoadName", "glLogicOp", "glMap1d", "glMap1f",
+ "glMap2d", "glMap2f", "glMapGrid1d", "glMapGrid1f", "glMapGrid2d", "glMapGrid2f", "glMaterialf", "glMaterialfv", "glMateriali", "glMaterialiv", "glMatrixMode", "glMultMatrixd",
+ "glMultMatrixf", "glNewList", "glNormal3b", "glNormal3bv", "glNormal3d", "glNormal3dv", "glNormal3f", "glNormal3fv", "glNormal3i", "glNormal3iv", "glNormal3s", "glNormal3sv",
+ "glNormalPointer", "glOrtho", "glPassThrough", "glPixelMapfv", "glPixelMapuiv", "glPixelMapusv", "glPixelStoref", "glPixelStorei", "glPixelTransferf", "glPixelTransferi", "glPixelZoom",
+ "glPointSize", "glPolygonMode", "glPolygonOffset", "glPolygonStipple", "glPopAttrib", "glPopClientAttrib", "glPopMatrix", "glPopName", "glPrioritizeTextures", "glPushAttrib",
+ "glPushClientAttrib", "glPushMatrix", "glPushName", "glRasterPos2d", "glRasterPos2dv", "glRasterPos2f", "glRasterPos2fv", "glRasterPos2i", "glRasterPos2iv", "glRasterPos2s",
+ "glRasterPos2sv", "glRasterPos3d", "glRasterPos3dv", "glRasterPos3f", "glRasterPos3fv", "glRasterPos3i", "glRasterPos3iv", "glRasterPos3s", "glRasterPos3sv", "glRasterPos4d",
+ "glRasterPos4dv", "glRasterPos4f", "glRasterPos4fv", "glRasterPos4i", "glRasterPos4iv", "glRasterPos4s", "glRasterPos4sv", "glReadBuffer", "glReadPixels", "glRectd", "glRectdv", "glRectf",
+ "glRectfv", "glRecti", "glRectiv", "glRects", "glRectsv", "glRenderMode", "glRotated", "glRotatef", "glScaled", "glScalef", "glScissor", "glSelectBuffer", "glShadeModel", "glStencilFunc",
+ "glStencilMask", "glStencilOp", "glTexCoord1d", "glTexCoord1dv", "glTexCoord1f", "glTexCoord1fv", "glTexCoord1i", "glTexCoord1iv", "glTexCoord1s", "glTexCoord1sv", "glTexCoord2d",
+ "glTexCoord2dv", "glTexCoord2f", "glTexCoord2fv", "glTexCoord2i", "glTexCoord2iv", "glTexCoord2s", "glTexCoord2sv", "glTexCoord3d", "glTexCoord3dv", "glTexCoord3f", "glTexCoord3fv",
+ "glTexCoord3i", "glTexCoord3iv", "glTexCoord3s", "glTexCoord3sv", "glTexCoord4d", "glTexCoord4dv", "glTexCoord4f", "glTexCoord4fv", "glTexCoord4i", "glTexCoord4iv", "glTexCoord4s",
+ "glTexCoord4sv", "glTexCoordPointer", "glTexEnvf", "glTexEnvfv", "glTexEnvi", "glTexEnviv", "glTexGend", "glTexGendv", "glTexGenf", "glTexGenfv", "glTexGeni", "glTexGeniv", "glTexImage1D",
+ "glTexImage2D", "glTexParameterf", "glTexParameterfv", "glTexParameteri", "glTexParameteriv", "glTexSubImage1D", "glTexSubImage2D", "glTranslated", "glTranslatef", "glVertex2d",
+ "glVertex2dv", "glVertex2f", "glVertex2fv", "glVertex2i", "glVertex2iv", "glVertex2s", "glVertex2sv", "glVertex3d", "glVertex3dv", "glVertex3f", "glVertex3fv", "glVertex3i", "glVertex3iv",
+ "glVertex3s", "glVertex3sv", "glVertex4d", "glVertex4dv", "glVertex4f", "glVertex4fv", "glVertex4i", "glVertex4iv", "glVertex4s", "glVertex4sv", "glVertexPointer", "glViewport"
+ };
+
+ size_t size = KIERO_ARRAY_SIZE(methodsNames);
+
+ g_methodsTable = (uint150_t*)::calloc(size, sizeof(uint150_t));
+
+ for (int i = 0; i < size; i++)
+ {
+ g_methodsTable[i] = (uint150_t)::GetProcAddress(libOpenGL32, methodsNames[i]);
+ }
+
+#if KIERO_USE_MINHOOK
+ MH_Initialize();
+#endif
+
+ g_renderType = RenderType::OpenGL;
+
+ return Status::Success;
+#endif
+ }
+ else if (_renderType == RenderType::Vulkan)
+ {
+#if KIERO_INCLUDE_VULKAN
+ HMODULE libVulkan;
+ if ((libVulkan = GetModuleHandle(KIERO_TEXT("vulcan-1.dll"))) == NULL)
+ {
+ return Status::ModuleNotFoundError;
+ }
+
+ const char* const methodsNames[] = {
+ "vkCreateInstance", "vkDestroyInstance", "vkEnumeratePhysicalDevices", "vkGetPhysicalDeviceFeatures", "vkGetPhysicalDeviceFormatProperties", "vkGetPhysicalDeviceImageFormatProperties",
+ "vkGetPhysicalDeviceProperties", "vkGetPhysicalDeviceQueueFamilyProperties", "vkGetPhysicalDeviceMemoryProperties", "vkGetInstanceProcAddr", "vkGetDeviceProcAddr", "vkCreateDevice",
+ "vkDestroyDevice", "vkEnumerateInstanceExtensionProperties", "vkEnumerateDeviceExtensionProperties", "vkEnumerateDeviceLayerProperties", "vkGetDeviceQueue", "vkQueueSubmit", "vkQueueWaitIdle",
+ "vkDeviceWaitIdle", "vkAllocateMemory", "vkFreeMemory", "vkMapMemory", "vkUnmapMemory", "vkFlushMappedMemoryRanges", "vkInvalidateMappedMemoryRanges", "vkGetDeviceMemoryCommitment",
+ "vkBindBufferMemory", "vkBindImageMemory", "vkGetBufferMemoryRequirements", "vkGetImageMemoryRequirements", "vkGetImageSparseMemoryRequirements", "vkGetPhysicalDeviceSparseImageFormatProperties",
+ "vkQueueBindSparse", "vkCreateFence", "vkDestroyFence", "vkResetFences", "vkGetFenceStatus", "vkWaitForFences", "vkCreateSemaphore", "vkDestroySemaphore", "vkCreateEvent", "vkDestroyEvent",
+ "vkGetEventStatus", "vkSetEvent", "vkResetEvent", "vkCreateQueryPool", "vkDestroyQueryPool", "vkGetQueryPoolResults", "vkCreateBuffer", "vkDestroyBuffer", "vkCreateBufferView", "vkDestroyBufferView",
+ "vkCreateImage", "vkDestroyImage", "vkGetImageSubresourceLayout", "vkCreateImageView", "vkDestroyImageView", "vkCreateShaderModule", "vkDestroyShaderModule", "vkCreatePipelineCache",
+ "vkDestroyPipelineCache", "vkGetPipelineCacheData", "vkMergePipelineCaches", "vkCreateGraphicsPipelines", "vkCreateComputePipelines", "vkDestroyPipeline", "vkCreatePipelineLayout",
+ "vkDestroyPipelineLayout", "vkCreateSampler", "vkDestroySampler", "vkCreateDescriptorSetLayout", "vkDestroyDescriptorSetLayout", "vkCreateDescriptorPool", "vkDestroyDescriptorPool",
+ "vkResetDescriptorPool", "vkAllocateDescriptorSets", "vkFreeDescriptorSets", "vkUpdateDescriptorSets", "vkCreateFramebuffer", "vkDestroyFramebuffer", "vkCreateRenderPass", "vkDestroyRenderPass",
+ "vkGetRenderAreaGranularity", "vkCreateCommandPool", "vkDestroyCommandPool", "vkResetCommandPool", "vkAllocateCommandBuffers", "vkFreeCommandBuffers", "vkBeginCommandBuffer", "vkEndCommandBuffer",
+ "vkResetCommandBuffer", "vkCmdBindPipeline", "vkCmdSetViewport", "vkCmdSetScissor", "vkCmdSetLineWidth", "vkCmdSetDepthBias", "vkCmdSetBlendConstants", "vkCmdSetDepthBounds",
+ "vkCmdSetStencilCompareMask", "vkCmdSetStencilWriteMask", "vkCmdSetStencilReference", "vkCmdBindDescriptorSets", "vkCmdBindIndexBuffer", "vkCmdBindVertexBuffers", "vkCmdDraw", "vkCmdDrawIndexed",
+ "vkCmdDrawIndirect", "vkCmdDrawIndexedIndirect", "vkCmdDispatch", "vkCmdDispatchIndirect", "vkCmdCopyBuffer", "vkCmdCopyImage", "vkCmdBlitImage", "vkCmdCopyBufferToImage", "vkCmdCopyImageToBuffer",
+ "vkCmdUpdateBuffer", "vkCmdFillBuffer", "vkCmdClearColorImage", "vkCmdClearDepthStencilImage", "vkCmdClearAttachments", "vkCmdResolveImage", "vkCmdSetEvent", "vkCmdResetEvent", "vkCmdWaitEvents",
+ "vkCmdPipelineBarrier", "vkCmdBeginQuery", "vkCmdEndQuery", "vkCmdResetQueryPool", "vkCmdWriteTimestamp", "vkCmdCopyQueryPoolResults", "vkCmdPushConstants", "vkCmdBeginRenderPass", "vkCmdNextSubpass",
+ "vkCmdEndRenderPass", "vkCmdExecuteCommands"
+ };
+
+ size_t size = KIERO_ARRAY_SIZE(methodsNames);
+
+ g_methodsTable = (uint150_t*)::calloc(size, sizeof(uint150_t));
+
+ for (int i = 0; i < size; i++)
+ {
+ g_methodsTable[i] = (uint150_t)::GetProcAddress(libVulkan, methodsNames[i]);
+ }
+
+#if KIERO_USE_MINHOOK
+ MH_Initialize();
+#endif
+
+ g_renderType = RenderType::Vulkan;
+
+ return Status::Success;
+#endif
+ }
+ else if (_renderType == RenderType::Auto)
+ {
+ RenderType::Enum type = RenderType::None;
+
+ if (::GetModuleHandle(KIERO_TEXT("d3d9.dll")) != NULL)
+ {
+ type = RenderType::D3D9;
+ }
+ else if (::GetModuleHandle(KIERO_TEXT("d3d10.dll")) != NULL)
+ {
+ type = RenderType::D3D10;
+ }
+ else if (::GetModuleHandle(KIERO_TEXT("d3d11.dll")) != NULL)
+ {
+ type = RenderType::D3D11;
+ }
+ else if (::GetModuleHandle(KIERO_TEXT("d3d12.dll")) != NULL)
+ {
+ type = RenderType::D3D12;
+ }
+ else if (::GetModuleHandle(KIERO_TEXT("opengl32.dll")) != NULL)
+ {
+ type = RenderType::OpenGL;
+ }
+ else if (::GetModuleHandle(KIERO_TEXT("vulcan-1.dll")) != NULL)
+ {
+ type = RenderType::Vulkan;
+ }
+
+ return init(type);
+ }
+ }
+
+ return Status::Success;
+}
+
+void kiero::shutdown()
+{
+ if (g_renderType != RenderType::None)
+ {
+#if KIERO_USE_MINHOOK
+ MH_DisableHook(MH_ALL_HOOKS);
+#endif
+
+ ::free(g_methodsTable);
+ g_methodsTable = NULL;
+ g_renderType = RenderType::None;
+ }
+}
+
+kiero::Status::Enum kiero::bind(uint16_t _index, void** _original, void* _function)
+{
+ // TODO: Need own detour function
+
+ assert(_index >= 0 && _original != NULL && _function != NULL);
+
+ if (g_renderType != RenderType::None)
+ {
+#if KIERO_USE_MINHOOK
+ void* target = (void*)g_methodsTable[_index];
+ if (MH_CreateHook(target, _function, _original) != MH_OK || MH_EnableHook(target) != MH_OK)
+ {
+ return Status::UnknownError;
+ }
+#endif
+
+ return Status::Success;
+ }
+
+ return Status::NotInitializedError;
+}
+
+void kiero::unbind(uint16_t _index)
+{
+ assert(_index >= 0);
+
+ if (g_renderType != RenderType::None)
+ {
+#if KIERO_USE_MINHOOK
+ MH_DisableHook((void*)g_methodsTable[_index]);
+#endif
+ }
+}
+
+kiero::RenderType::Enum kiero::getRenderType()
+{
+ return g_renderType;
+}
+
+uint150_t* kiero::getMethodsTable()
+{
+ return g_methodsTable;
+}
\ No newline at end of file
diff --git a/F4Menu/kiero.h b/F4Menu/kiero.h
new file mode 100644
index 000000000..f75f6a286
--- /dev/null
+++ b/F4Menu/kiero.h
@@ -0,0 +1,78 @@
+#ifndef __KIERO_H__
+#define __KIERO_H__
+
+#include
+
+#define KIERO_VERSION "1.2.6"
+
+#define KIERO_INCLUDE_D3D9 0 // 1 if you need D3D9 hook
+#define KIERO_INCLUDE_D3D10 0 // 1 if you need D3D10 hook
+#define KIERO_INCLUDE_D3D11 1 // 1 if you need D3D11 hook
+#define KIERO_INCLUDE_D3D12 0 // 1 if you need D3D12 hook
+#define KIERO_INCLUDE_OPENGL 0 // 1 if you need OpenGL hook
+#define KIERO_INCLUDE_VULKAN 0 // 1 if you need Vulkan hook
+#define KIERO_USE_MINHOOK 1 // 1 if you will use kiero::bind function
+
+#define KIERO_ARCH_X64 0
+#define KIERO_ARCH_X86 0
+
+#if defined(_M_X64)
+# undef KIERO_ARCH_X64
+# define KIERO_ARCH_X64 1
+#else
+# undef KIERO_ARCH_X86
+# define KIERO_ARCH_X86 1
+#endif
+
+#if KIERO_ARCH_X64
+typedef uint64_t uint150_t;
+#else
+typedef uint32_t uint150_t;
+#endif
+
+namespace kiero
+{
+ struct Status
+ {
+ enum Enum
+ {
+ UnknownError = -1,
+ NotSupportedError = -2,
+ ModuleNotFoundError = -3,
+
+ AlreadyInitializedError = -4,
+ NotInitializedError = -5,
+
+ Success = 0,
+ };
+ };
+
+ struct RenderType
+ {
+ enum Enum
+ {
+ None,
+
+ D3D9,
+ D3D10,
+ D3D11,
+ D3D12,
+
+ OpenGL,
+ Vulkan,
+
+ Auto
+ };
+ };
+
+ Status::Enum init(RenderType::Enum renderType);
+ void shutdown();
+
+ Status::Enum bind(uint16_t index, void** original, void* function);
+ void unbind(uint16_t index);
+
+ RenderType::Enum getRenderType();
+ uint150_t* getMethodsTable();
+}
+
+#endif // __KIERO_H__
\ No newline at end of file
diff --git a/F4Menu/memcury.h b/F4Menu/memcury.h
new file mode 100644
index 000000000..641a4d80e
--- /dev/null
+++ b/F4Menu/memcury.h
@@ -0,0 +1,1208 @@
+#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,
+ NONE = 0x00
+ };
+
+ 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, bool find_first = false) -> 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]);
+ if (find_first)
+ break;
+ }
+ }
+ else
+ {
+ auto lea = stringAdd.GetAs();
+
+ if constexpr (bIsWide)
+ {
+ if (wcscmp(string, lea) == 0)
+ {
+ add = PE::Address(&scanBytes[i]);
+ if (find_first)
+ break;
+ }
+ }
+ else
+ {
+ if (strcmp(string, lea) == 0)
+ {
+ add = PE::Address(&scanBytes[i]);
+ if (find_first)
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ MemcuryAssertM(add != 0, "FindStringRef 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 FindFunctionBoundary(bool forward = false) -> Scanner
+ {
+ const auto scanBytes = _address.GetAs();
+
+ for (auto i = (forward ? 1 : -1); forward ? (i < 2048) : (i > -2048); forward ? i++ : i--)
+ {
+ if ( // ASM::byteIsA(scanBytes[i], ASM::MNEMONIC::JMP_REL8) ||
+ // ASM::byteIsA(scanBytes[i], ASM::MNEMONIC::JMP_REL32) ||
+ // ASM::byteIsA(scanBytes[i], ASM::MNEMONIC::JMP_EAX) ||
+ ASM::byteIsA(scanBytes[i], ASM::MNEMONIC::RETN_REL8) || ASM::byteIsA(scanBytes[i], ASM::MNEMONIC::RETN) || ASM::byteIsA(scanBytes[i], ASM::MNEMONIC::INT3))
+ {
+ _address = (uintptr_t)&scanBytes[i + 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