From 22bb1d4cde9515fec9262d4ca70d07bcedff0cd8 Mon Sep 17 00:00:00 2001 From: PixieCore <41352111+IcePixelx@users.noreply.github.com> Date: Wed, 20 Jul 2022 18:03:35 +0200 Subject: [PATCH] Implemented CModule::GetVirtualMethodTable. * Added CModule::GetVirtualMethodTable. Gets you a pointer to a VTable by its rtti type descriptor name. * Added StringToMaskedBytes. Turns a string into masked bytes. * CModule::FindPatternSIMD now allows you to custom define a section it should search in. --- r5dev/public/include/module.h | 5 ++-- r5dev/public/include/utility.h | 1 + r5dev/public/module.cpp | 55 ++++++++++++++++++++++++++++++---- r5dev/public/utility.cpp | 24 +++++++++++++++ 4 files changed, 78 insertions(+), 7 deletions(-) diff --git a/r5dev/public/include/module.h b/r5dev/public/include/module.h index ead15fa9..93d6d8cb 100644 --- a/r5dev/public/include/module.h +++ b/r5dev/public/include/module.h @@ -22,11 +22,12 @@ public: CModule(void) = default; CModule(const string& moduleName); - CMemory FindPatternSIMD(const uint8_t* szPattern, const char* szMask) const; - CMemory FindPatternSIMD(const string& svPattern) const; + CMemory FindPatternSIMD(const uint8_t* szPattern, const char* szMask, ModuleSections_t moduleSection = {}) const; + CMemory FindPatternSIMD(const string& svPattern, ModuleSections_t moduleSection = {}) const; CMemory FindString(const string& string, const ptrdiff_t occurence = 1, bool nullTerminator = false) const; CMemory FindStringReadOnly(const string& svString, bool nullTerminator) const; + CMemory GetVirtualMethodTable(const std::string& tableName); CMemory GetExportedFunction(const string& svFunctionName) const; ModuleSections_t GetSectionByName(const string& svSectionName) const; uintptr_t GetModuleBase(void) const; diff --git a/r5dev/public/include/utility.h b/r5dev/public/include/utility.h index 2e652a53..e2a29220 100644 --- a/r5dev/public/include/utility.h +++ b/r5dev/public/include/utility.h @@ -45,6 +45,7 @@ string StringUnescape(const string& svInput); ///////////////////////////////////////////////////////////////////////////// // Bytes vector StringToBytes(const string& svInput, bool bNullTerminator); +pair, string> StringToMaskedBytes(const string& svInput, bool bNullTerminator); vector PatternToBytes(const string& svInput); pair, string> PatternToMaskedBytes(const string& svInput); vector IntToDigits(int iValue); diff --git a/r5dev/public/module.cpp b/r5dev/public/module.cpp index a1c4ce7e..7ce3ca4c 100644 --- a/r5dev/public/module.cpp +++ b/r5dev/public/module.cpp @@ -41,13 +41,19 @@ CModule::CModule(const string& svModuleName) : m_svModuleName(svModuleName) // *szMask - // Output : CMemory //----------------------------------------------------------------------------- -CMemory CModule::FindPatternSIMD(const uint8_t* szPattern, const char* szMask) const +CMemory CModule::FindPatternSIMD(const uint8_t* szPattern, const char* szMask, ModuleSections_t moduleSection) const { if (!m_ExecutableCode.IsSectionValid()) return CMemory(); - const uint64_t nBase = static_cast(m_ExecutableCode.m_pSectionBase); - const uint64_t nSize = static_cast(m_ExecutableCode.m_nSectionSize); + uint64_t nBase = static_cast(m_ExecutableCode.m_pSectionBase); + uint64_t nSize = static_cast(m_ExecutableCode.m_nSectionSize); + + if (moduleSection.IsSectionValid()) + { + nBase = static_cast(moduleSection.m_pSectionBase); + nSize = static_cast(moduleSection.m_nSectionSize); + } const uint8_t* pData = reinterpret_cast(nBase); const uint8_t* pEnd = pData + static_cast(nSize) - strlen(szMask); @@ -106,10 +112,10 @@ CMemory CModule::FindPatternSIMD(const uint8_t* szPattern, const char* szMask) c // Input : *szPattern // Output : CMemory //----------------------------------------------------------------------------- -CMemory CModule::FindPatternSIMD(const string& svPattern) const +CMemory CModule::FindPatternSIMD(const string& svPattern, ModuleSections_t moduleSection) const { const pair patternInfo = PatternToMaskedBytes(svPattern); - return FindPatternSIMD(patternInfo.first.data(), patternInfo.second.c_str()); + return FindPatternSIMD(patternInfo.first.data(), patternInfo.second.c_str(), moduleSection); } //----------------------------------------------------------------------------- @@ -246,6 +252,45 @@ CMemory CModule::GetExportedFunction(const string& svFunctionName) const return CMemory(); } +//----------------------------------------------------------------------------- +// Purpose: get address of a virtual method table by rtti type descriptor name. +// Input : *tableName - +// Output : CMemory +//----------------------------------------------------------------------------- +CMemory CModule::GetVirtualMethodTable(const std::string& tableName) +{ + const auto tableNameInfo = StringToMaskedBytes(tableName, false); + CMemory rttiTypeDescriptor = FindPatternSIMD(tableNameInfo.first.data(), tableNameInfo.second.c_str(), {".data", m_RunTimeData.m_pSectionBase, m_RunTimeData.m_nSectionSize}).OffsetSelf(-0x10); + if (!rttiTypeDescriptor) + return CMemory(); + + vector rttiTDRef = {}; + uintptr_t scanStart = m_ReadOnlyData.m_pSectionBase; // Get the start address of our scan. + + const uintptr_t scanEnd = (m_ReadOnlyData.m_pSectionBase + m_ReadOnlyData.m_nSectionSize) - 0x4; // Calculate the end of our scan. + const uintptr_t rttiTDRva = rttiTypeDescriptor.GetPtr() - m_pModuleBase; // The RTTI gets referenced by a 4-Byte RVA address. We need to scan for that address. + while (scanStart < scanEnd) + { + CMemory reference = FindPatternSIMD(reinterpret_cast(&rttiTDRva), "xxxx", {".rdata", scanStart, m_ReadOnlyData.m_nSectionSize}); + if (!reference) + break; + + rttiTDRef.push_back(reference); + scanStart = reference.Offset(0x4).GetPtr(); // Set location to current reference + 0x4 so we avoid pushing it back again into the vector. + } + + for (const CMemory& ref : rttiTDRef) + { + CMemory referenceOffset = ref.Offset(-0xC); + if (referenceOffset.GetValue() != 1) // Check if we got a RTTI Object Locator for this reference by checking if -0xC is 1, which is the 'signature' field which is always 1 on x64. + continue; + + return FindPatternSIMD(reinterpret_cast(&referenceOffset), "xxxx", { ".rdata", m_ReadOnlyData.m_pSectionBase, m_ReadOnlyData.m_nSectionSize }).OffsetSelf(0x8); + } + + return CMemory(); +} + //----------------------------------------------------------------------------- // Purpose: get the module section by name (example: '.rdata', '.text') // Input : *svModuleName - diff --git a/r5dev/public/utility.cpp b/r5dev/public/utility.cpp index e2eb4565..0782aa56 100644 --- a/r5dev/public/utility.cpp +++ b/r5dev/public/utility.cpp @@ -581,6 +581,30 @@ vector StringToBytes(const string& svInput, bool bNullTerminator) return vBytes; }; +/////////////////////////////////////////////////////////////////////////////// +// For converting a string to an array of bytes. +pair, string> StringToMaskedBytes(const string& svInput, bool bNullTerminator) +{ + char* pszStringStart = const_cast(svInput.c_str()); + char* pszStringEnd = pszStringStart + strlen(svInput.c_str()); + vector vBytes = vector{ }; + string svMask = string(); + + for (char* pszCurrentByte = pszStringStart; pszCurrentByte < pszStringEnd; ++pszCurrentByte) + { + // Dereference character and push back the byte. + vBytes.push_back(*pszCurrentByte); + svMask.append("x"); + } + + if (bNullTerminator) + { + vBytes.push_back(0x0); + svMask.append("x"); + } + return make_pair(vBytes, svMask); +}; + /////////////////////////////////////////////////////////////////////////////// // For converting a string pattern with wildcards to an array of bytes. vector PatternToBytes(const string& svInput)