From 0b6c04f1078b3666fb172dbbc45166e5e5121f51 Mon Sep 17 00:00:00 2001 From: EricPlayZ Date: Wed, 8 May 2024 04:19:16 +0300 Subject: [PATCH] added RTTI utils which helps me get vtables using RTTI names, yey! no more weird offset stuff --- EGameTools/EGameTools.vcxproj | 2 + EGameTools/EGameTools.vcxproj.filters | 6 + EGameTools/pch/pch.h | 1 + .../source/game/GamePH/PlayerVariables.cpp | 27 +--- EGameTools/source/offsets.h | 12 +- EGameTools/source/utils/rtti.cpp | 145 ++++++++++++++++++ EGameTools/source/utils/rtti.h | 8 + EGameTools/source/utils/sigscan.cpp | 49 ++++-- EGameTools/source/utils/sigscan.h | 6 +- 9 files changed, 211 insertions(+), 45 deletions(-) create mode 100644 EGameTools/source/utils/rtti.cpp create mode 100644 EGameTools/source/utils/rtti.h diff --git a/EGameTools/EGameTools.vcxproj b/EGameTools/EGameTools.vcxproj index 945cb69..dbd5483 100644 --- a/EGameTools/EGameTools.vcxproj +++ b/EGameTools/EGameTools.vcxproj @@ -101,6 +101,7 @@ + @@ -182,6 +183,7 @@ + diff --git a/EGameTools/EGameTools.vcxproj.filters b/EGameTools/EGameTools.vcxproj.filters index ab4db9b..5f83102 100644 --- a/EGameTools/EGameTools.vcxproj.filters +++ b/EGameTools/EGameTools.vcxproj.filters @@ -197,6 +197,9 @@ game\GamePH + + utils + @@ -417,6 +420,9 @@ game\GamePH + + utils + diff --git a/EGameTools/pch/pch.h b/EGameTools/pch/pch.h index 3397a22..9977fb2 100644 --- a/EGameTools/pch/pch.h +++ b/EGameTools/pch/pch.h @@ -73,6 +73,7 @@ #include "..\source\utils\files.h" #include "..\source\utils\hook.h" #include "..\source\utils\memory.h" +#include "..\source\utils\rtti.h" #include "..\source\utils\sigscan.h" #include "..\source\utils\texture.h" #include "..\source\utils\time.h" diff --git a/EGameTools/source/game/GamePH/PlayerVariables.cpp b/EGameTools/source/game/GamePH/PlayerVariables.cpp index 39e2f7b..42665d9 100644 --- a/EGameTools/source/game/GamePH/PlayerVariables.cpp +++ b/EGameTools/source/game/GamePH/PlayerVariables.cpp @@ -14,25 +14,6 @@ namespace GamePH { std::vector>> PlayerVariables::playerCustomVarsDefault; bool PlayerVariables::gotPlayerVars = false; - static PDWORD64 getFloatPlayerVariableVT() { - if (!Offsets::Get_InitializePlayerVariables()) - return nullptr; - - const DWORD64 offsetToInstr = Offsets::Get_InitializePlayerVariables() + Offsets::Get_initPlayerFloatVarsInstr_offset() + 0x3; // 0x3 is instruction size - const DWORD floatPlayerVariableVTOffset = *reinterpret_cast(offsetToInstr); - - return reinterpret_cast(offsetToInstr + sizeof(DWORD) + floatPlayerVariableVTOffset); - } - static PDWORD64 getBoolPlayerVariableVT() { - if (!Offsets::Get_InitializePlayerVariables()) - return nullptr; - - const DWORD64 offsetToInstr = Offsets::Get_InitializePlayerVariables() + Offsets::Get_initPlayerBoolVarsInstr_offset() + 0x3; // 0x3 is instruction size - const DWORD boolPlayerVariableVTOffset = *reinterpret_cast(offsetToInstr); - - return reinterpret_cast(offsetToInstr + sizeof(DWORD) + boolPlayerVariableVTOffset); - } - template static void updateDefaultVar(std::vector>>& defaultVars, const std::string& varName, T varValue) { static_assert(std::is_same::value || std::is_same::value, "Invalid type: value must be float or bool"); @@ -47,8 +28,8 @@ namespace GamePH { } static void processPlayerVar(PDWORD64*& playerVarsMem, std::pair>& var) { while (true) { - const bool isFloatPlayerVar = *playerVarsMem == getFloatPlayerVariableVT(); - const bool isBoolPlayerVar = *playerVarsMem == getBoolPlayerVariableVT(); + const bool isFloatPlayerVar = *playerVarsMem == Offsets::GetVT_FloatPlayerVariable(); + const bool isBoolPlayerVar = *playerVarsMem == Offsets::GetVT_BoolPlayerVariable(); if (isFloatPlayerVar || isBoolPlayerVar) { var.second.first = playerVarsMem + VAR_LOC_OFFSET; @@ -81,9 +62,9 @@ namespace GamePH { return; if (playerVars.empty()) return; - if (!getFloatPlayerVariableVT()) + if (!Offsets::GetVT_FloatPlayerVariable()) return; - if (!getBoolPlayerVariableVT()) + if (!Offsets::GetVT_BoolPlayerVariable()) return; PDWORD64* playerVarsMem = reinterpret_cast(Get()); diff --git a/EGameTools/source/offsets.h b/EGameTools/source/offsets.h index f9f4f47..6fdf95f 100644 --- a/EGameTools/source/offsets.h +++ b/EGameTools/source/offsets.h @@ -19,9 +19,18 @@ static DWORD64 Get_## name () {\ static DWORD64 name = 0;\ if (Utils::Memory::IsValidPtr(name)) return name;\ return name=reinterpret_cast(GetModuleHandle(moduleName)) + static_cast(off);\ +} + +#define AddVTOffset(name, moduleName, retType)\ +static retType GetVT_## name () {\ + static retType VT_## name = NULL;\ + if (Utils::Memory::IsValidPtr(VT_## name)) return VT_## name;\ + return VT_## name=reinterpret_cast(Utils::RTTI::GetVTablePtr(moduleName, #name));\ } struct Offsets { + AddVTOffset(FloatPlayerVariable, "gamedll_ph_x64_rwdi.dll", LPVOID) + AddVTOffset(BoolPlayerVariable, "gamedll_ph_x64_rwdi.dll", LPVOID) // ntdll.dll //AddOffset(LdrpCallInitRoutine, "ntdll.dll", "[48 89 5C 24 08 44 89 44 24 18 48", PatternType::Address, DWORD64*) //AddOffset(LdrpRunInitializeRoutines, "ntdll.dll", "[48 89 4C 24 08 53 56 57 41 54 41 55 41 56 41 57 48 81 EC 90", PatternType::Address, DWORD64*) // for win7 @@ -33,9 +42,6 @@ struct Offsets { // Player vars related AddStaticOffset(LoadPlayerVariableFunc_size, 0x14E) //AddOffset(LoadPlayerFloatVariable, "gamedll_ph_x64_rwdi.dll", "E8 [?? ?? ?? ?? 48 8B D0 48 8D 8C 24 ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 8D 94 24 ?? ?? ?? ?? 48 8B 8C 24 ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 8D 8C 24 ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 8D 8C 24 ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 8B 84 24 ?? ?? ?? ??", PatternType::RelativePointer, DWORD64*); - AddOffset(InitializePlayerVariables, "gamedll_ph_x64_rwdi.dll", "E8 [?? ?? ?? ?? 48 8B F0 48 8B CF", Utils::SigScan::PatternType::RelativePointer, DWORD64) - AddStaticOffset(initPlayerFloatVarsInstr_offset, 0x99); - AddStaticOffset(initPlayerBoolVarsInstr_offset, 0x2E7); AddOffset(PlayerState, "gamedll_ph_x64_rwdi.dll", "4C 8B 35 [?? ?? ?? ?? 4C 8B E2", Utils::SigScan::PatternType::RelativePointer, LPVOID) // Game related diff --git a/EGameTools/source/utils/rtti.cpp b/EGameTools/source/utils/rtti.cpp new file mode 100644 index 0000000..b3c5310 --- /dev/null +++ b/EGameTools/source/utils/rtti.cpp @@ -0,0 +1,145 @@ +#include "pch.h" + +namespace Utils { + namespace RTTI { + static std::string bytesToIDAPattern(BYTE* bytes, size_t size) { + std::stringstream idaPattern; + idaPattern << std::hex << std::uppercase << std::setfill('0'); + + for (size_t i = 0; i < size; i++) { + const int currentByte = bytes[i]; + if (currentByte != 255) + idaPattern << std::setw(2) << currentByte; + else + idaPattern << "??"; + + if (i != size - 1) + idaPattern << " "; + } + + return idaPattern.str(); + } + static std::vector getXrefsTo(DWORD64 moduleBaseAddr, DWORD64 address, DWORD64 start, size_t size) { + std::vector xrefs = {}; + + const DWORD64 finalOffset = address - moduleBaseAddr; + const std::string idaPattern = bytesToIDAPattern(reinterpret_cast(const_cast(&finalOffset)), 4); + + // Get the end of the section (in our case the end of the .rdata section) + const DWORD64 end = start + size; + + while (start && start < end) { + DWORD64 xref = reinterpret_cast(Utils::SigScan::PatternScanner::FindPattern(reinterpret_cast(start), size, { idaPattern.c_str(), Utils::SigScan::PatternType::Address})); + + // If the xref is 0 it means that there either were no xrefs, or there are no remaining xrefs. + // So we should break out of our loop, otherwise it will keep on trying to look for xrefs. + if (!xref) + break; + + // We've found an xref, save it in the vector, and add 8 to start, so it wil now search for xrefs + // from the previously found xref untill we're at the end of the section, or there aren't any xrefs left. + xrefs.push_back(xref); + start = xref + 8; + } + + return xrefs; + } + static bool getSectionInfo(DWORD64 baseAddress, const char* sectionName, DWORD64& sectionStart, DWORD64& sectionSize) { + const PIMAGE_DOS_HEADER dosHeader = reinterpret_cast(baseAddress); + if (dosHeader->e_magic != IMAGE_DOS_SIGNATURE) + return false; + + const PIMAGE_NT_HEADERS32 ntHeaders = reinterpret_cast(baseAddress + dosHeader->e_lfanew); + if (ntHeaders->Signature != IMAGE_NT_SIGNATURE) + return false; + + PIMAGE_SECTION_HEADER sectionHeader = IMAGE_FIRST_SECTION(ntHeaders); + USHORT numOfSections = ntHeaders->FileHeader.NumberOfSections; + while (numOfSections > 0) { + // If we're at the right section + if (!strcmp(sectionName, (const char*)sectionHeader->Name)) { + sectionStart = baseAddress + sectionHeader->VirtualAddress; + sectionSize = sectionHeader->SizeOfRawData; + + return true; + } + + sectionHeader++; + numOfSections--; + } + + return false; + } + + DWORD64 GetVTablePtr(const char* moduleName, const char* tableName) { + if (!moduleName || !tableName) + return 0; + + DWORD64 baseAddress = reinterpret_cast(GetModuleHandleA(moduleName)); + if (!baseAddress) + return 0; + + // Type descriptor names look like this: .?AVFloatPlayerVariable@@ (so: ".?AV" + table_name + "@@") + const std::string typeDescriptorName = ".?AV" + std::string(tableName) + "@@"; + + // Convert the string to an IDA pattern so that we can pattern scan it + std::string idaPattern = bytesToIDAPattern(reinterpret_cast(const_cast(typeDescriptorName.data())), typeDescriptorName.size()); + + DWORD64 rttiTypeDescriptor = reinterpret_cast(Utils::SigScan::PatternScanner::FindPattern(moduleName, { idaPattern.c_str(), Utils::SigScan::PatternType::Address })); + if (!rttiTypeDescriptor) + return 0; + + // We're doing - 0x10 here, because the location of the rtti_type_descriptor is 0x10 bytes before the string (open up gamedll_ph_x64_rwdi.dll in IDA and take a look) + rttiTypeDescriptor -= 0x10; + + // We only need to get xrefs that are inside the .rdata section (there sometimes are xrefs in .text, so we have to filter them out) + DWORD64 rdataStart = 0; + DWORD64 rdataSize = 0; + if (!getSectionInfo(baseAddress, ".rdata", rdataStart, rdataSize)) + return 0; + + // Get all xrefs to the type_descriptor + const std::vector xrefs = getXrefsTo(baseAddress, rttiTypeDescriptor, rdataStart, rdataSize); + for (const DWORD64 xref : xrefs) { + // xref - 0x8 = offset of this vtable in complete class (from top) + // So if it's 0 it means it's the class we need, and not some class it inherits from (again, opening up gamedll_ph_x64_rwdi.dll in IDA will help you understand) + const int offsetFromClass = *reinterpret_cast(xref - 0x8); + if (offsetFromClass != 0) + continue; + + // We've found the correct xref, the object locator is 0xC bytes before the xref. (Again, gamedll_ph_x64_rwdi.dll yada yada yada) + const DWORD64 objectLocator = xref - 0xC; + + // Now we need to get an xref to the object locator, as that's where the vtable is located + { + // Convert the object locator address to an IDA pattern + idaPattern = bytesToIDAPattern(reinterpret_cast(const_cast(&objectLocator)), 8); + + const DWORD64 vtableAddr = reinterpret_cast(Utils::SigScan::PatternScanner::FindPattern(reinterpret_cast(rdataStart), rdataSize, { idaPattern.c_str(), Utils::SigScan::PatternType::Address })) + 0x8; + + // Here I'm checking for <= 8 as we're adding 0x8 to it. So if the pattern scan returns 0 we still head the fuck out + if (vtableAddr <= 8) + return 0; + + return vtableAddr; + + // We've now found the vtable address, however, we probably want a pointer to the vtable (which is in .text). + // To do this, we're going to find a reference to the vtable address, and use that as pointer. + + // If you don't need a pointer to the vtable in your implementation however, just return vtable_address + /*DWORD64 textStart = 0; + DWORD64 textSize = 0; + if (!getSectionInfo(baseAddress, ".text", textStart, textSize)) + return nullptr; + + // Convert the vtable address to an IDA pattern + idaPattern = bytesToIDAPattern(reinterpret_cast(const_cast(&vtableAddr)), 8); + return reinterpret_cast(Utils::SigScan::PatternScanner::FindPattern(reinterpret_cast(textStart), textSize, { idaPattern.c_str(), Utils::SigScan::PatternType::Address })) + 0x8;*/ + } + } + + // We for some odd reason didn't find any valid xrefs + return 0; + } + } +} \ No newline at end of file diff --git a/EGameTools/source/utils/rtti.h b/EGameTools/source/utils/rtti.h new file mode 100644 index 0000000..01894ff --- /dev/null +++ b/EGameTools/source/utils/rtti.h @@ -0,0 +1,8 @@ +#pragma once +#include "basetsd.h" + +namespace Utils { + namespace RTTI { + extern DWORD64 GetVTablePtr(const char* moduleName, const char* tableName); + } +} \ No newline at end of file diff --git a/EGameTools/source/utils/sigscan.cpp b/EGameTools/source/utils/sigscan.cpp index fc76960..d004341 100644 --- a/EGameTools/source/utils/sigscan.cpp +++ b/EGameTools/source/utils/sigscan.cpp @@ -2,17 +2,25 @@ namespace Utils { namespace SigScan { - LPVOID PatternScanner::FindPattern(const std::string_view ModuleName, const Pattern& pattern) { - return PatternScanner::FindPattern(GetModuleHandle(ModuleName.data()), Utils::Memory::GetModuleInfo(ModuleName.data()).SizeOfImage, pattern); + LPVOID PatternScanner::FindPattern(const std::string_view moduleName, const Pattern& pattern) { + HMODULE hModule = GetModuleHandleA(moduleName.data()); + if (!hModule) + return nullptr; + + return PatternScanner::FindPattern(hModule, Utils::Memory::GetModuleInfo(moduleName.data()).SizeOfImage, pattern); } LPVOID PatternScanner::FindPattern(const Pattern& pattern) { MODULEINFO hModuleInfo; - GetModuleInformation(GetCurrentProcess(), GetModuleHandle(nullptr), &hModuleInfo, sizeof(hModuleInfo)); - return PatternScanner::FindPattern(GetModuleHandle(nullptr), hModuleInfo.SizeOfImage, pattern); + GetModuleInformation(GetCurrentProcess(), GetModuleHandleA(nullptr), &hModuleInfo, sizeof(hModuleInfo)); + return PatternScanner::FindPattern(GetModuleHandleA(nullptr), hModuleInfo.SizeOfImage, pattern); } - std::vector PatternScanner::FindPatterns(const std::string_view ModuleName, const Pattern& pattern) { - return PatternScanner::FindPatterns(GetModuleHandle(ModuleName.data()), Utils::Memory::GetModuleInfo(ModuleName.data()).SizeOfImage, pattern); + std::vector PatternScanner::FindPatterns(const std::string_view moduleName, const Pattern& pattern) { + HMODULE hModule = GetModuleHandleA(moduleName.data()); + if (!hModule) + return {}; + + return PatternScanner::FindPatterns(hModule, Utils::Memory::GetModuleInfo(moduleName.data()).SizeOfImage, pattern); } std::vector PatternScanner::FindPatterns(LPVOID startAddress, DWORD64 searchSize, const Pattern& pattern) { std::vector ret; @@ -87,22 +95,30 @@ namespace Utils { if (bytesCounted <= byteCount) mask[bytesCounted] = '\0'; - size_t searchLen = strlen(reinterpret_cast(&mask[0])) - 1; - LPVOID ret = nullptr; - for (DWORD64 retAddress = reinterpret_cast(startAddress); retAddress < reinterpret_cast(startAddress) + searchSize; retAddress++) { + DWORD64 retAddress = reinterpret_cast(startAddress); + DWORD64 endAddress = retAddress + searchSize; + size_t searchLen = bytesCounted; + + while (retAddress < endAddress) { __try { - if ((pos < bytesCounted && *reinterpret_cast(retAddress) == patt[pos]) || mask[pos] == '?') { - if (bytesCounted <= byteCount && pos < bytesCounted && mask[pos + 1] == '\0') { - ret = reinterpret_cast(retAddress - searchLen + offset); + bool found = true; + for (size_t j = 0; j < searchLen; j++) { + BYTE* currentByte = reinterpret_cast(retAddress + j); + if (mask[j] == 'x' && *currentByte != patt[j]) { + found = false; break; } + } - pos++; - } else - pos = 0; + if (found) { + ret = reinterpret_cast(retAddress + offset); + break; + } + + retAddress++; } __except (EXCEPTION_EXECUTE_HANDLER) { - pos = 0; + retAddress++; } } @@ -147,6 +163,7 @@ namespace Utils { return ret; } + LPVOID PatternScanner::FindPattern(LPVOID startAddress, DWORD64 searchSize, const Pattern* patterns, float* ratio) { int totalCount = 0; struct result { diff --git a/EGameTools/source/utils/sigscan.h b/EGameTools/source/utils/sigscan.h index 0d87f87..c6a8abb 100644 --- a/EGameTools/source/utils/sigscan.h +++ b/EGameTools/source/utils/sigscan.h @@ -27,14 +27,14 @@ namespace Utils { class PatternScanner { public: static LPVOID FindPattern(LPVOID startAddress, DWORD64 searchSize, const Pattern& pattern); - static LPVOID FindPattern(const std::string_view ModuleName, const Pattern& pattern); + static LPVOID FindPattern(const std::string_view moduleName, const Pattern& pattern); static LPVOID FindPattern(const Pattern& pattern); - static std::vector FindPatterns(const std::string_view ModuleName, const Pattern& pattern); + static std::vector FindPatterns(const std::string_view moduleName, const Pattern& pattern); static std::vector FindPatterns(LPVOID startAddress, DWORD64 searchSize, const Pattern& pattern); static LPVOID FindPattern(LPVOID startAddress, DWORD64 searchSize, const Pattern* patterns, float* ratio = nullptr); - static LPVOID FindPattern(const std::string_view ModuleName, Pattern* patterns, float* ratio = nullptr); + static LPVOID FindPattern(const std::string_view moduleName, Pattern* patterns, float* ratio = nullptr); private: template static LPVOID ResolveRelativePtr(LPVOID Address) { if (!Address)