added RTTI utils which helps me get vtables using RTTI names, yey! no more weird offset stuff

This commit is contained in:
EricPlayZ
2024-05-08 04:19:16 +03:00
parent e3a90c6d58
commit 0b6c04f107
9 changed files with 211 additions and 45 deletions

View File

@ -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" />

View File

@ -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">

View File

@ -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"

View File

@ -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());

View File

@ -19,9 +19,18 @@ static DWORD64 Get_## name () {\
static DWORD64 name = 0;\
if (Utils::Memory::IsValidPtr(name)) return 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

View 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;
}
}
}

View File

@ -0,0 +1,8 @@
#pragma once
#include "basetsd.h"
namespace Utils {
namespace RTTI {
extern DWORD64 GetVTablePtr(const char* moduleName, const char* tableName);
}
}

View File

@ -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;
}
}
pos++;
} else
pos = 0;
if (found) {
ret = reinterpret_cast<LPVOID>(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 {

View File

@ -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)