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