Initial commit

This commit is contained in:
Michal 2026-01-11 18:57:42 +04:00 • committed by GitHub
commit 3dd53cf457
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
105 changed files with 35670 additions and 0 deletions

21
vendor/dynlibutils/LICENSE vendored Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 komashchenko (Phoenix)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

9
vendor/dynlibutils/README.md vendored Normal file
View file

@ -0,0 +1,9 @@
DynLibUtils is a library for interacting with dynamically loaded libraries (DLL, SO).
Allows to get library sections, find pattern in memory and VirtualTable address by name.
This library is designed to simplify development for CS2 server, supports Windows/Linux x86_64.
Usage
------------
Copy the source and header files to your project and add [`module.cpp`](module.cpp) in your build.

152
vendor/dynlibutils/memaddr.h vendored Normal file
View file

@ -0,0 +1,152 @@
// DynLibUtils
// Copyright (C) 2023 komashchenko (Phoenix)
// https://github.com/komashchenko/DynLibUtils
#ifndef DYNLIBUTILS_MEMADDR_H
#define DYNLIBUTILS_MEMADDR_H
#ifdef _WIN32
#pragma once
#endif
#include <cstdint>
#include <cstddef>
#include <utility>
namespace DynLibUtils {
class CMemory
{
public:
CMemory() : m_ptr(0) {}
CMemory(const CMemory&) noexcept = default;
CMemory& operator= (const CMemory&) noexcept = default;
CMemory(CMemory&& other) noexcept : m_ptr(std::exchange(other.m_ptr, 0)) {}
CMemory(const uintptr_t ptr) : m_ptr(ptr) {}
CMemory(const void* ptr) : m_ptr(reinterpret_cast<uintptr_t>(ptr)) {}
inline operator uintptr_t() const noexcept
{
return m_ptr;
}
inline operator void*() const noexcept
{
return reinterpret_cast<void*>(m_ptr);
}
explicit inline operator bool() const noexcept
{
return m_ptr != 0;
}
inline bool operator!= (const CMemory& addr) const noexcept
{
return m_ptr != addr.m_ptr;
}
inline bool operator== (const CMemory& addr) const noexcept
{
return m_ptr == addr.m_ptr;
}
inline bool operator== (const uintptr_t& addr) const noexcept
{
return m_ptr == addr;
}
[[nodiscard]] inline uintptr_t GetPtr() const noexcept
{
return m_ptr;
}
template<class T> [[nodiscard]] inline T GetValue() const noexcept
{
return *reinterpret_cast<T*>(m_ptr);
}
template<typename T> [[nodiscard]] inline T CCast() const noexcept
{
return (T)m_ptr;
}
template<typename T> [[nodiscard]] inline T RCast() const noexcept
{
return reinterpret_cast<T>(m_ptr);
}
template<typename T> [[nodiscard]] inline T UCast() const noexcept
{
union { uintptr_t m_ptr; T cptr; } cast;
return cast.m_ptr = m_ptr, cast.cptr;
}
[[nodiscard]] inline CMemory Offset(ptrdiff_t offset) const noexcept
{
return m_ptr + offset;
}
inline CMemory& OffsetSelf(ptrdiff_t offset) noexcept
{
m_ptr += offset;
return *this;
}
[[nodiscard]] inline CMemory Deref(int deref = 1) const
{
uintptr_t reference = m_ptr;
while (deref--)
{
if (reference)
reference = *reinterpret_cast<uintptr_t*>(reference);
}
return reference;
}
inline CMemory& DerefSelf(int deref = 1)
{
while (deref--)
{
if (m_ptr)
m_ptr = *reinterpret_cast<uintptr_t*>(m_ptr);
}
return *this;
}
[[nodiscard]] inline CMemory FollowNearCall(const ptrdiff_t opcodeOffset = 0x1, const ptrdiff_t nextInstructionOffset = 0x5) const
{
return ResolveRelativeAddress(opcodeOffset, nextInstructionOffset);
}
inline CMemory& FollowNearCallSelf(const ptrdiff_t opcodeOffset = 0x1, const ptrdiff_t nextInstructionOffset = 0x5)
{
return ResolveRelativeAddressSelf(opcodeOffset, nextInstructionOffset);
}
[[nodiscard]] inline CMemory ResolveRelativeAddress(const ptrdiff_t registerOffset = 0x0, const ptrdiff_t nextInstructionOffset = 0x4) const
{
const uintptr_t skipRegister = m_ptr + registerOffset;
const int32_t relativeAddress = *reinterpret_cast<int32_t*>(skipRegister);
const uintptr_t nextInstruction = m_ptr + nextInstructionOffset;
return nextInstruction + relativeAddress;
}
inline CMemory& ResolveRelativeAddressSelf(const ptrdiff_t registerOffset = 0x0, const ptrdiff_t nextInstructionOffset = 0x4)
{
const uintptr_t skipRegister = m_ptr + registerOffset;
const int32_t relativeAddress = *reinterpret_cast<int32_t*>(skipRegister);
const uintptr_t nextInstruction = m_ptr + nextInstructionOffset;
m_ptr = nextInstruction + relativeAddress;
return *this;
}
private:
uintptr_t m_ptr;
};
} // namespace DynLibUtils
#endif // DYNLIBUTILS_MEMADDR_H

203
vendor/dynlibutils/module.cpp vendored Normal file
View file

@ -0,0 +1,203 @@
// DynLibUtils
// Copyright (C) 2023 komashchenko (Phoenix)
// https://github.com/komashchenko/DynLibUtils
#include "module.h"
#include "memaddr.h"
#include <cstring>
#include <cmath>
#include <emmintrin.h>
using namespace DynLibUtils;
//-----------------------------------------------------------------------------
// Purpose: constructor
// Input : szModuleName (without extension .dll/.so)
//-----------------------------------------------------------------------------
CModule::CModule(const std::string_view szModuleName) : m_pModuleHandle(nullptr)
{
InitFromName(szModuleName);
}
//-----------------------------------------------------------------------------
// Purpose: constructor
// Input : pModuleMemory
//-----------------------------------------------------------------------------
CModule::CModule(const CMemory pModuleMemory) : m_pModuleHandle(nullptr)
{
InitFromMemory(pModuleMemory);
}
//-----------------------------------------------------------------------------
// Purpose: Converts a string pattern with wildcards to an array of bytes and mask
// Input : svInput
// Output : std::pair<std::vector<uint8_t>, std::string>
//-----------------------------------------------------------------------------
std::pair<std::vector<uint8_t>, std::string> CModule::PatternToMaskedBytes(const std::string_view svInput)
{
char* pszPatternStart = const_cast<char*>(svInput.data());
char* pszPatternEnd = pszPatternStart + svInput.size();
std::vector<uint8_t> vBytes;
std::string svMask;
for (char* pszCurrentByte = pszPatternStart; pszCurrentByte < pszPatternEnd; ++pszCurrentByte)
{
if (*pszCurrentByte == '?')
{
++pszCurrentByte;
if (*pszCurrentByte == '?')
{
++pszCurrentByte; // Skip double wildcard.
}
vBytes.push_back(0); // Push the byte back as invalid.
svMask += '?';
}
else
{
vBytes.push_back(static_cast<uint8_t>(strtoul(pszCurrentByte, &pszCurrentByte, 16)));
svMask += 'x';
}
}
return std::make_pair(std::move(vBytes), std::move(svMask));
}
//-----------------------------------------------------------------------------
// Purpose: Finds an array of bytes in process memory using SIMD instructions
// Input : *pPattern
// szMask
// pStartAddress
// *pModuleSection
// Output : CMemory
//-----------------------------------------------------------------------------
CMemory CModule::FindPattern(const CMemory pPattern, const std::string_view szMask, const CMemory pStartAddress, const ModuleSections_t* pModuleSection) const
{
const uint8_t* pattern = pPattern.RCast<const uint8_t*>();
const ModuleSections_t* section = pModuleSection ? pModuleSection : &m_ExecutableCode;
if (!section->IsSectionValid())
return CMemory();
const uintptr_t nBase = section->m_pSectionBase;
const size_t nSize = section->m_nSectionSize;
const size_t nMaskLen = szMask.length();
const uint8_t* pData = reinterpret_cast<uint8_t*>(nBase);
const uint8_t* pEnd = pData + nSize - nMaskLen;
if(pStartAddress)
{
const uint8_t* startAddress = pStartAddress.RCast<uint8_t*>();
if(pData > startAddress || startAddress > pEnd)
return CMemory();
pData = startAddress;
}
int nMasks[64]; // 64*16 = enough masks for 1024 bytes.
const uint8_t iNumMasks = static_cast<uint8_t>(std::ceil(static_cast<float>(nMaskLen) / 16.f));
memset(nMasks, 0, iNumMasks * sizeof(int));
for (uint8_t i = 0; i < iNumMasks; ++i)
{
for (int8_t j = static_cast<int8_t>(std::min<size_t>(nMaskLen - i * 16, 16)) - 1; j >= 0; --j)
{
if (szMask[i * 16 + j] == 'x')
{
nMasks[i] |= 1 << j;
}
}
}
const __m128i xmm1 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(pattern));
__m128i xmm2, xmm3, msks;
for (; pData != pEnd; _mm_prefetch(reinterpret_cast<const char*>(++pData + 64), _MM_HINT_NTA))
{
xmm2 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(pData));
msks = _mm_cmpeq_epi8(xmm1, xmm2);
if ((_mm_movemask_epi8(msks) & nMasks[0]) == nMasks[0])
{
bool bFound = true;
for (uint8_t i = 1; i < iNumMasks; ++i)
{
xmm2 = _mm_loadu_si128(reinterpret_cast<const __m128i*>((pData + i * 16)));
xmm3 = _mm_loadu_si128(reinterpret_cast<const __m128i*>((pattern + i * 16)));
msks = _mm_cmpeq_epi8(xmm2, xmm3);
if ((_mm_movemask_epi8(msks) & nMasks[i]) != nMasks[i])
{
bFound = false;
break;
}
}
if (bFound)
return pData;
}
}
return CMemory();
}
//-----------------------------------------------------------------------------
// Purpose: Finds a string pattern in process memory using SIMD instructions
// Input : svPattern
// pStartAddress
// *pModuleSection
// Output : CMemory
//-----------------------------------------------------------------------------
CMemory CModule::FindPattern(const std::string_view svPattern, const CMemory pStartAddress, const ModuleSections_t* pModuleSection) const
{
const std::pair patternInfo = PatternToMaskedBytes(svPattern);
return FindPattern(patternInfo.first.data(), patternInfo.second, pStartAddress, pModuleSection);
}
//-----------------------------------------------------------------------------
// Purpose: Gets a module section by name (example: '.rdata', '.text')
// Input : svModuleName
// Output : ModuleSections_t
//-----------------------------------------------------------------------------
CModule::ModuleSections_t CModule::GetSectionByName(const std::string_view svSectionName) const
{
for (const ModuleSections_t& section : m_vModuleSections)
{
if (section.m_svSectionName == svSectionName)
return section;
}
return ModuleSections_t();
}
//-----------------------------------------------------------------------------
// Purpose: Returns the module handle
//-----------------------------------------------------------------------------
void* CModule::GetModuleHandle() const noexcept
{
return m_pModuleHandle;
}
//-----------------------------------------------------------------------------
// Purpose: Returns the module path
//-----------------------------------------------------------------------------
std::string_view CModule::GetModulePath() const
{
return m_sModulePath;
}
//-----------------------------------------------------------------------------
// Purpose: Returns the module name
//-----------------------------------------------------------------------------
std::string_view CModule::GetModuleName() const
{
std::string_view svModulePath(m_sModulePath);
return svModulePath.substr(svModulePath.find_last_of("/\\") + 1);
}
#ifndef DYNLIBUTILS_SEPARATE_SOURCE_FILES
#if defined _WIN32 && _M_X64
#include "module_windows.cpp"
#elif defined __linux__ && __x86_64__
#include "module_linux.cpp"
#else
#error "Unsupported platform"
#endif
#endif

77
vendor/dynlibutils/module.h vendored Normal file
View file

@ -0,0 +1,77 @@
// DynLibUtils
// Copyright (C) 2023 komashchenko (Phoenix)
// https://github.com/komashchenko/DynLibUtils
#ifndef DYNLIBUTILS_MODULE_H
#define DYNLIBUTILS_MODULE_H
#ifdef _WIN32
#pragma once
#endif
#include "memaddr.h"
#include <vector>
#include <string>
#include <string_view>
#include <utility>
namespace DynLibUtils {
class CModule
{
public:
struct ModuleSections_t
{
ModuleSections_t() : m_nSectionSize(0) {}
ModuleSections_t(const ModuleSections_t&) = default;
ModuleSections_t& operator= (const ModuleSections_t&) = default;
ModuleSections_t(ModuleSections_t&& other) noexcept : m_svSectionName(std::move(other.m_svSectionName)), m_pSectionBase(std::move(other.m_pSectionBase)), m_nSectionSize(std::exchange(other.m_nSectionSize, 0)) {}
ModuleSections_t(const std::string_view svSectionName, uintptr_t pSectionBase, size_t nSectionSize) : m_svSectionName(svSectionName), m_pSectionBase(pSectionBase), m_nSectionSize(nSectionSize) {}
[[nodiscard]] inline bool IsSectionValid() const noexcept
{
return m_pSectionBase;
}
std::string m_svSectionName; // Name of section.
CMemory m_pSectionBase; // Start address of section.
size_t m_nSectionSize; // Size of section.
};
CModule() : m_pModuleHandle(nullptr) {}
~CModule();
CModule (const CModule&) = delete;
CModule& operator= (const CModule&) = delete;
CModule(CModule&& other) noexcept : m_ExecutableCode(std::move(other.m_ExecutableCode)), m_sModulePath(std::move(other.m_sModulePath)), m_pModuleHandle(std::exchange(other.m_pModuleHandle, nullptr)), m_vModuleSections(std::move(other.m_vModuleSections)) {}
explicit CModule(const std::string_view svModuleName);
explicit CModule(const char* pszModuleName) : CModule(std::string_view(pszModuleName)) {}
explicit CModule(const std::string& sModuleName) : CModule(std::string_view(sModuleName)) {}
CModule(const CMemory pModuleMemory);
bool InitFromName(const std::string_view svModuleName, bool bExtension = false);
bool InitFromMemory(const CMemory pModuleMemory);
[[nodiscard]] static std::pair<std::vector<uint8_t>, std::string> PatternToMaskedBytes(const std::string_view svInput);
[[nodiscard]] CMemory FindPattern(const CMemory pPattern, const std::string_view szMask, const CMemory pStartAddress = nullptr, const ModuleSections_t* pModuleSection = nullptr) const;
[[nodiscard]] CMemory FindPattern(const std::string_view svPattern, const CMemory pStartAddress = nullptr, const ModuleSections_t* pModuleSection = nullptr) const;
[[nodiscard]] CMemory GetVirtualTableByName(const std::string_view svTableName, bool bDecorated = false) const;
[[nodiscard]] CMemory GetFunctionByName(const std::string_view svFunctionName) const noexcept;
[[nodiscard]] ModuleSections_t GetSectionByName(const std::string_view svSectionName) const;
[[nodiscard]] void* GetModuleHandle() const noexcept;
[[nodiscard]] CMemory GetModuleBase() const noexcept;
[[nodiscard]] std::string_view GetModulePath() const;
[[nodiscard]] std::string_view GetModuleName() const;
private:
bool Init(const std::string_view svModelePath);
ModuleSections_t m_ExecutableCode;
std::string m_sModulePath;
void* m_pModuleHandle;
std::vector<ModuleSections_t> m_vModuleSections;
};
} // namespace DynLibUtils
#endif // DYNLIBUTILS_MODULE_H

219
vendor/dynlibutils/module_linux.cpp vendored Normal file
View file

@ -0,0 +1,219 @@
// DynLibUtils
// Copyright (C) 2023 komashchenko (Phoenix)
// https://github.com/komashchenko/DynLibUtils
#include "module.h"
#include "memaddr.h"
#include <cstring>
#include <link.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/mman.h>
using namespace DynLibUtils;
CModule::~CModule()
{
if (m_pModuleHandle)
dlclose(m_pModuleHandle);
}
//-----------------------------------------------------------------------------
// Purpose: Initializes the module from module name
// Input : svModuleName
// bExtension
// Output : bool
//-----------------------------------------------------------------------------
bool CModule::InitFromName(const std::string_view svModuleName, bool bExtension)
{
if (m_pModuleHandle)
return false;
if (svModuleName.empty())
return false;
std::string sModuleName(svModuleName);
if (!bExtension)
sModuleName.append(".so");
struct dl_data
{
ElfW(Addr) addr;
const char* moduleName;
const char* modulePath;
} dldata{ 0, sModuleName.c_str(), {} };
dl_iterate_phdr([](dl_phdr_info* info, size_t /* size */, void* data)
{
dl_data* dldata = reinterpret_cast<dl_data*>(data);
if (std::strstr(info->dlpi_name, dldata->moduleName) != nullptr)
{
dldata->addr = info->dlpi_addr;
dldata->modulePath = info->dlpi_name;
}
return 0;
}, &dldata);
if (!dldata.addr)
return false;
if (!Init(dldata.modulePath))
return false;
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Initializes the module from module memory
// Input : pModuleMemory
// Output : bool
//-----------------------------------------------------------------------------
bool CModule::InitFromMemory(const CMemory pModuleMemory)
{
if (m_pModuleHandle)
return false;
if (!pModuleMemory)
return false;
Dl_info info;
if (!dladdr(pModuleMemory, &info) || !info.dli_fbase || !info.dli_fname)
return false;
if (!Init(info.dli_fname))
return false;
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Initializes a module descriptors
//-----------------------------------------------------------------------------
bool CModule::Init(const std::string_view svModelePath)
{
void* handle = dlopen(svModelePath.data(), RTLD_LAZY | RTLD_NOLOAD);
if (!handle)
return false;
link_map* lmap;
if (dlinfo(handle, RTLD_DI_LINKMAP, &lmap) != 0)
{
dlclose(handle);
return false;
}
int fd = open(lmap->l_name, O_RDONLY);
if (fd == -1)
{
dlclose(handle);
return false;
}
struct stat st;
if (fstat(fd, &st) == 0)
{
void* map = mmap(nullptr, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (map != MAP_FAILED)
{
ElfW(Ehdr)* ehdr = static_cast<ElfW(Ehdr)*>(map);
ElfW(Shdr)* shdrs = reinterpret_cast<ElfW(Shdr)*>(reinterpret_cast<uintptr_t>(ehdr) + ehdr->e_shoff);
const char* strTab = reinterpret_cast<const char*>(reinterpret_cast<uintptr_t>(ehdr) + shdrs[ehdr->e_shstrndx].sh_offset);
for (auto i = 0; i < ehdr->e_shnum; ++i) // Loop through the sections.
{
ElfW(Shdr)* shdr = reinterpret_cast<ElfW(Shdr)*>(reinterpret_cast<uintptr_t>(shdrs) + i * ehdr->e_shentsize);
if (*(strTab + shdr->sh_name) == '\0')
continue;
m_vModuleSections.emplace_back(strTab + shdr->sh_name, static_cast<uintptr_t>(lmap->l_addr + shdr->sh_addr), shdr->sh_size);
}
munmap(map, st.st_size);
}
}
close(fd);
m_pModuleHandle = handle;
m_sModulePath.assign(svModelePath);
m_ExecutableCode = GetSectionByName(".text");
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Gets an address of a virtual method table by rtti type descriptor name
// Input : svTableName
// bDecorated
// Output : CMemory
//-----------------------------------------------------------------------------
CMemory CModule::GetVirtualTableByName(const std::string_view svTableName, bool bDecorated) const
{
if (svTableName.empty())
return CMemory();
CModule::ModuleSections_t readOnlyData = GetSectionByName(".rodata"), readOnlyRelocations = GetSectionByName(".data.rel.ro");
if (!readOnlyData.IsSectionValid() || !readOnlyRelocations.IsSectionValid())
return CMemory();
std::string sDecoratedTableName(bDecorated ? svTableName : std::to_string(svTableName.length()) + std::string(svTableName));
std::string sMask(sDecoratedTableName.length() + 1, 'x');
CMemory typeInfoName = FindPattern(sDecoratedTableName.data(), sMask, nullptr, &readOnlyData);
if (!typeInfoName)
return CMemory();
CMemory referenceTypeName = FindPattern(&typeInfoName, "xxxxxxxx", nullptr, &readOnlyRelocations); // Get reference to type name.
if (!referenceTypeName)
return CMemory();
CMemory typeInfo = referenceTypeName.Offset(-0x8); // Offset -0x8 to typeinfo.
for (const auto& sectionName : { std::string_view(".data.rel.ro"), std::string_view(".data.rel.ro.local") })
{
CModule::ModuleSections_t section = GetSectionByName(sectionName);
if (!section.IsSectionValid())
continue;
CMemory reference;
while ((reference = FindPattern(&typeInfo, "xxxxxxxx", reference, &section))) // Get reference typeinfo in vtable
{
if (reference.Offset(-0x8).GetValue<int64_t>() == 0) // Offset to this.
{
return reference.Offset(0x8);
}
reference.OffsetSelf(0x8);
}
}
return CMemory();
}
//-----------------------------------------------------------------------------
// Purpose: Gets an address of a virtual method table by rtti type descriptor name
// Input : svFunctionName
// Output : CMemory
//-----------------------------------------------------------------------------
CMemory CModule::GetFunctionByName(const std::string_view svFunctionName) const noexcept
{
if (!m_pModuleHandle)
return CMemory();
if (svFunctionName.empty())
return CMemory();
return dlsym(m_pModuleHandle, svFunctionName.data());
}
//-----------------------------------------------------------------------------
// Purpose: Returns the module base
//-----------------------------------------------------------------------------
CMemory CModule::GetModuleBase() const noexcept
{
return static_cast<link_map*>(m_pModuleHandle)->l_addr;
}

196
vendor/dynlibutils/module_windows.cpp vendored Normal file
View file

@ -0,0 +1,196 @@
// DynLibUtils
// Copyright (C) 2023 komashchenko (Phoenix)
// https://github.com/komashchenko/DynLibUtils
#include "module.h"
#include "memaddr.h"
#include <cstring>
#include <cmath>
#include <windows.h>
using namespace DynLibUtils;
CModule::~CModule()
{
if (m_pModuleHandle)
FreeLibrary(reinterpret_cast<HMODULE>(m_pModuleHandle));
}
static std::string GetModulePath(HMODULE hModule)
{
std::string modulePath(MAX_PATH, '\0');
while (true)
{
size_t len = GetModuleFileNameA(hModule, modulePath.data(), static_cast<DWORD>(modulePath.length()));
if (len == 0)
{
modulePath.clear();
break;
}
if (len < modulePath.length())
{
modulePath.resize(len);
break;
}
else
modulePath.resize(modulePath.length() * 2);
}
return modulePath;
}
//-----------------------------------------------------------------------------
// Purpose: Initializes the module from module name
// Input : svModuleName
// bExtension
// Output : bool
//-----------------------------------------------------------------------------
bool CModule::InitFromName(const std::string_view svModuleName, bool bExtension)
{
if (m_pModuleHandle)
return false;
if (svModuleName.empty())
return false;
std::string sModuleName(svModuleName);
if (!bExtension)
sModuleName.append(".dll");
HMODULE handle = GetModuleHandleA(sModuleName.c_str());
if (!handle)
return false;
std::string modulePath = ::GetModulePath(handle);
if(modulePath.empty())
return false;
if (!Init(modulePath))
return false;
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Initializes the module from module memory
// Input : pModuleMemory
// Output : bool
//-----------------------------------------------------------------------------
bool CModule::InitFromMemory(const CMemory pModuleMemory)
{
if (m_pModuleHandle)
return false;
if (!pModuleMemory)
return false;
MEMORY_BASIC_INFORMATION mbi;
if (!VirtualQuery(pModuleMemory, &mbi, sizeof(mbi)))
return false;
std::string modulePath = ::GetModulePath(reinterpret_cast<HMODULE>(mbi.AllocationBase));
if (modulePath.empty())
return false;
if (!Init(modulePath))
return false;
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Initializes a module descriptors
//-----------------------------------------------------------------------------
bool CModule::Init(const std::string_view svModelePath)
{
HMODULE handle = LoadLibraryExA(svModelePath.data(), nullptr, DONT_RESOLVE_DLL_REFERENCES);
if (!handle)
return false;
IMAGE_DOS_HEADER* pDOSHeader = reinterpret_cast<IMAGE_DOS_HEADER*>(handle);
IMAGE_NT_HEADERS64* pNTHeaders = reinterpret_cast<IMAGE_NT_HEADERS64*>(reinterpret_cast<uintptr_t>(handle) + pDOSHeader->e_lfanew);
const IMAGE_SECTION_HEADER* hSection = IMAGE_FIRST_SECTION(pNTHeaders); // Get first image section.
for (WORD i = 0; i < pNTHeaders->FileHeader.NumberOfSections; ++i) // Loop through the sections.
{
const IMAGE_SECTION_HEADER& hCurrentSection = hSection[i]; // Get current section.
m_vModuleSections.emplace_back(reinterpret_cast<const char*>(hCurrentSection.Name), static_cast<uintptr_t>(reinterpret_cast<uintptr_t>(handle) + hCurrentSection.VirtualAddress), hCurrentSection.SizeOfRawData); // Push back a struct with the section data.
}
m_pModuleHandle = handle;
m_sModulePath.assign(svModelePath);
m_ExecutableCode = GetSectionByName(".text");
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Gets an address of a virtual method table by rtti type descriptor name
// Input : svTableName
// bDecorated
// Output : CMemory
//-----------------------------------------------------------------------------
CMemory CModule::GetVirtualTableByName(const std::string_view svTableName, bool bDecorated) const
{
if(svTableName.empty())
return CMemory();
CModule::ModuleSections_t runTimeData = GetSectionByName(".data"), readOnlyData = GetSectionByName(".rdata");
if(!runTimeData.IsSectionValid() || !readOnlyData.IsSectionValid())
return CMemory();
std::string sDecoratedTableName(bDecorated ? svTableName : ".?AV" + std::string(svTableName) + "@@");
std::string sMask(sDecoratedTableName.length() + 1, 'x');
CMemory typeDescriptorName = FindPattern(sDecoratedTableName.data(), sMask, nullptr, &runTimeData);
if (!typeDescriptorName)
return CMemory();
CMemory rttiTypeDescriptor = typeDescriptorName.Offset(-0x10);
const uintptr_t rttiTDRva = rttiTypeDescriptor - GetModuleBase(); // The RTTI gets referenced by a 4-Byte RVA address. We need to scan for that address.
CMemory reference;
while ((reference = FindPattern(&rttiTDRva, "xxxx", reference, &readOnlyData))) // Get reference typeinfo in vtable
{
// 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.
// Check that offset of this vtable is 0
if (reference.Offset(-0xC).GetValue<int32_t>() == 1 && reference.Offset(-0x8).GetValue<int32_t>() == 0)
{
CMemory referenceOffset = reference.Offset(-0xC);
CMemory rttiCompleteObjectLocator = FindPattern(&referenceOffset, "xxxxxxxx", nullptr, &readOnlyData);
if (rttiCompleteObjectLocator)
return rttiCompleteObjectLocator.Offset(0x8);
}
reference.OffsetSelf(0x4);
}
return CMemory();
}
//-----------------------------------------------------------------------------
// Purpose: Gets an address of a virtual method table by rtti type descriptor name
// Input : svFunctionName
// Output : CMemory
//-----------------------------------------------------------------------------
CMemory CModule::GetFunctionByName(const std::string_view svFunctionName) const noexcept
{
if(!m_pModuleHandle)
return CMemory();
if (svFunctionName.empty())
return CMemory();
return GetProcAddress(reinterpret_cast<HMODULE>(m_pModuleHandle), svFunctionName.data());
}
//-----------------------------------------------------------------------------
// Purpose: Returns the module base
//-----------------------------------------------------------------------------
CMemory CModule::GetModuleBase() const noexcept
{
return m_pModuleHandle;
}