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)