mirror of
https://github.com/EricPlayZ/EGameTools.git
synced 2025-07-18 17:37:53 +08:00
added RTTI utils which helps me get vtables using RTTI names, yey! no more weird offset stuff
This commit is contained in:
@ -101,6 +101,7 @@
|
||||
<ClCompile Include="source\utils\files.cpp" />
|
||||
<ClCompile Include="source\utils\hook.cpp" />
|
||||
<ClCompile Include="source\utils\memory.cpp" />
|
||||
<ClCompile Include="source\utils\rtti.cpp" />
|
||||
<ClCompile Include="source\utils\sigscan.cpp" />
|
||||
<ClCompile Include="source\utils\texture.cpp" />
|
||||
<ClCompile Include="source\utils\time.cpp" />
|
||||
@ -182,6 +183,7 @@
|
||||
<ClInclude Include="source\utils\files.h" />
|
||||
<ClInclude Include="source\utils\hook.h" />
|
||||
<ClInclude Include="source\utils\memory.h" />
|
||||
<ClInclude Include="source\utils\rtti.h" />
|
||||
<ClInclude Include="source\utils\sigscan.h" />
|
||||
<ClInclude Include="source\utils\texture.h" />
|
||||
<ClInclude Include="source\utils\time.h" />
|
||||
|
@ -197,6 +197,9 @@
|
||||
<ClCompile Include="source\game\GamePH\PlayerInfectionModule.cpp">
|
||||
<Filter>game\GamePH</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="source\utils\rtti.cpp">
|
||||
<Filter>utils</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="source\kiero.h" />
|
||||
@ -417,6 +420,9 @@
|
||||
<ClInclude Include="source\game\GamePH\PlayerInfectionModule.h">
|
||||
<Filter>game\GamePH</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="source\utils\rtti.h">
|
||||
<Filter>utils</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Filter Include="MinHook">
|
||||
|
@ -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"
|
||||
|
@ -14,25 +14,6 @@ namespace GamePH {
|
||||
std::vector<std::pair<std::string, std::pair<std::any, std::string>>> 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<DWORD*>(offsetToInstr);
|
||||
|
||||
return reinterpret_cast<PDWORD64>(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<DWORD*>(offsetToInstr);
|
||||
|
||||
return reinterpret_cast<PDWORD64>(offsetToInstr + sizeof(DWORD) + boolPlayerVariableVTOffset);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void updateDefaultVar(std::vector<std::pair<std::string, std::pair<std::any, std::string>>>& defaultVars, const std::string& varName, T varValue) {
|
||||
static_assert(std::is_same<T, float>::value || std::is_same<T, bool>::value, "Invalid type: value must be float or bool");
|
||||
@ -47,8 +28,8 @@ namespace GamePH {
|
||||
}
|
||||
static void processPlayerVar(PDWORD64*& playerVarsMem, std::pair<std::string, std::pair<LPVOID, std::string>>& 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<PDWORD64*>(Get());
|
||||
|
@ -21,7 +21,16 @@ static DWORD64 Get_## name () {\
|
||||
return name=reinterpret_cast<DWORD64>(GetModuleHandle(moduleName)) + static_cast<DWORD64>(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<retType>(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
|
||||
|
145
EGameTools/source/utils/rtti.cpp
Normal file
145
EGameTools/source/utils/rtti.cpp
Normal file
@ -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<DWORD64> getXrefsTo(DWORD64 moduleBaseAddr, DWORD64 address, DWORD64 start, size_t size) {
|
||||
std::vector<DWORD64> xrefs = {};
|
||||
|
||||
const DWORD64 finalOffset = address - moduleBaseAddr;
|
||||
const std::string idaPattern = bytesToIDAPattern(reinterpret_cast<BYTE*>(const_cast<DWORD64*>(&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<DWORD64>(Utils::SigScan::PatternScanner::FindPattern(reinterpret_cast<LPVOID>(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<PIMAGE_DOS_HEADER>(baseAddress);
|
||||
if (dosHeader->e_magic != IMAGE_DOS_SIGNATURE)
|
||||
return false;
|
||||
|
||||
const PIMAGE_NT_HEADERS32 ntHeaders = reinterpret_cast<PIMAGE_NT_HEADERS32>(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<DWORD64>(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<BYTE*>(const_cast<char*>(typeDescriptorName.data())), typeDescriptorName.size());
|
||||
|
||||
DWORD64 rttiTypeDescriptor = reinterpret_cast<DWORD64>(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<DWORD64> 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<int*>(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<BYTE*>(const_cast<DWORD64*>(&objectLocator)), 8);
|
||||
|
||||
const DWORD64 vtableAddr = reinterpret_cast<DWORD64>(Utils::SigScan::PatternScanner::FindPattern(reinterpret_cast<LPVOID>(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<BYTE*>(const_cast<DWORD64*>(&vtableAddr)), 8);
|
||||
return reinterpret_cast<DWORD64*>(Utils::SigScan::PatternScanner::FindPattern(reinterpret_cast<LPVOID>(textStart), textSize, { idaPattern.c_str(), Utils::SigScan::PatternType::Address })) + 0x8;*/
|
||||
}
|
||||
}
|
||||
|
||||
// We for some odd reason didn't find any valid xrefs
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
8
EGameTools/source/utils/rtti.h
Normal file
8
EGameTools/source/utils/rtti.h
Normal file
@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
#include "basetsd.h"
|
||||
|
||||
namespace Utils {
|
||||
namespace RTTI {
|
||||
extern DWORD64 GetVTablePtr(const char* moduleName, const char* tableName);
|
||||
}
|
||||
}
|
@ -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<LPVOID> 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<LPVOID> 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<LPVOID> PatternScanner::FindPatterns(LPVOID startAddress, DWORD64 searchSize, const Pattern& pattern) {
|
||||
std::vector<LPVOID> ret;
|
||||
@ -87,22 +95,30 @@ namespace Utils {
|
||||
if (bytesCounted <= byteCount)
|
||||
mask[bytesCounted] = '\0';
|
||||
|
||||
size_t searchLen = strlen(reinterpret_cast<char*>(&mask[0])) - 1;
|
||||
|
||||
LPVOID ret = nullptr;
|
||||
for (DWORD64 retAddress = reinterpret_cast<DWORD64>(startAddress); retAddress < reinterpret_cast<DWORD64>(startAddress) + searchSize; retAddress++) {
|
||||
DWORD64 retAddress = reinterpret_cast<DWORD64>(startAddress);
|
||||
DWORD64 endAddress = retAddress + searchSize;
|
||||
size_t searchLen = bytesCounted;
|
||||
|
||||
while (retAddress < endAddress) {
|
||||
__try {
|
||||
if ((pos < bytesCounted && *reinterpret_cast<BYTE*>(retAddress) == patt[pos]) || mask[pos] == '?') {
|
||||
if (bytesCounted <= byteCount && pos < bytesCounted && mask[pos + 1] == '\0') {
|
||||
ret = reinterpret_cast<LPVOID>(retAddress - searchLen + offset);
|
||||
bool found = true;
|
||||
for (size_t j = 0; j < searchLen; j++) {
|
||||
BYTE* currentByte = reinterpret_cast<BYTE*>(retAddress + j);
|
||||
if (mask[j] == 'x' && *currentByte != patt[j]) {
|
||||
found = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found) {
|
||||
ret = reinterpret_cast<LPVOID>(retAddress + offset);
|
||||
break;
|
||||
}
|
||||
|
||||
pos++;
|
||||
} else
|
||||
pos = 0;
|
||||
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 {
|
||||
|
@ -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<LPVOID> FindPatterns(const std::string_view ModuleName, const Pattern& pattern);
|
||||
static std::vector<LPVOID> FindPatterns(const std::string_view moduleName, const Pattern& pattern);
|
||||
static std::vector<LPVOID> 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 <typename T> static LPVOID ResolveRelativePtr(LPVOID Address) {
|
||||
if (!Address)
|
||||
|
Reference in New Issue
Block a user