backup, made a lot of changes related to vftable scanning

This commit is contained in:
EricPlayZ
2025-01-03 04:35:13 +02:00
parent eb633ec72f
commit 32a0f8b124
68 changed files with 337 additions and 317 deletions

View File

@ -9,9 +9,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "EGameTools", "EGameTools\EG
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "EGameSDK", "EGameSDK\EGameSDK.vcxproj", "{152BCFEC-E42D-4370-AEDD-36F044085C0C}"
ProjectSection(ProjectDependencies) = postProject
{47F0BD81-B327-0619-BC31-6A3C28E82C12} = {47F0BD81-B327-0619-BC31-6A3C28E82C12}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Ultimate-ASI-Loader-x64", "Ultimate-ASI-Loader-x64\Ultimate-ASI-Loader-x64.vcxproj", "{47F0BD81-B327-0619-BC31-6A3C28E82C12}"
EndProject

View File

@ -13,6 +13,7 @@
<ItemGroup>
<ClCompile Include="deps\memscan\memscan.c" />
<ClCompile Include="deps\memscan\util\util.c" />
<ClCompile Include="src\ClassHelpers.cpp" />
<ClCompile Include="src\Core\Core.cpp" />
<ClCompile Include="src\dllmain.cpp" />
<ClCompile Include="src\Engine\CBaseCamera.cpp" />

View File

@ -136,6 +136,9 @@
<ClCompile Include="src\Vector3.cpp">
<Filter>src</Filter>
</ClCompile>
<ClCompile Include="src\ClassHelpers.cpp">
<Filter>src</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Filter Include="include">

View File

@ -2,61 +2,72 @@
#define NOMINMAX
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <typeinfo>
#include <EGSDK\Utils\Memory.h>
#include <EGSDK\Utils\RTTI.h>
#include <EGSDK\Utils\Values.h>
#include <EGSDK\Exports.h>
namespace EGSDK {
namespace ClassHelpers {
#pragma pack(1)
template<size_t size, typename T>
class buffer {
char buffer[size];
public:
T data;
template<size_t size, typename T>
class buffer {
char buffer[size];
public:
T data;
operator T() { return data; }
T operator->() { return data; }
operator T() { return data; }
T operator->() { return data; }
DWORD64 operator&(const DWORD64 other) const { return reinterpret_cast<DWORD64>(data) & other; }
DWORD64 operator>>(const int shift) const { return reinterpret_cast<DWORD64>(data) >> shift; }
DWORD64 operator<<(const int shift) const { return reinterpret_cast<DWORD64>(data) << shift; }
DWORD64 operator&(const DWORD64 other) const { return reinterpret_cast<DWORD64>(data) & other; }
DWORD64 operator>>(const int shift) const { return reinterpret_cast<DWORD64>(data) >> shift; }
DWORD64 operator<<(const int shift) const { return reinterpret_cast<DWORD64>(data) << shift; }
T& operator=(const T& other) { data = other; return data; }
T& operator*=(const T& other) { data *= other; return data; }
T operator*(const T& other) const { return data * other; }
T& operator/=(const T& other) { data /= other; return data; }
T operator/(const T& other) const { return data / other; }
T& operator+=(const T& other) { data += other; return data; }
T operator+(const T& other) const { return data + other; }
T& operator-=(const T& other) { data -= other; return data; }
T operator-(const T& other) const { return data - other; }
};
T& operator=(const T& other) { data = other; return data; }
T& operator*=(const T& other) { data *= other; return data; }
T operator*(const T& other) const { return data * other; }
T& operator/=(const T& other) { data /= other; return data; }
T operator/(const T& other) const { return data / other; }
T& operator+=(const T& other) { data += other; return data; }
T operator+(const T& other) const { return data + other; }
T& operator-=(const T& other) { data -= other; return data; }
T operator-(const T& other) const { return data - other; }
};
#pragma pack()
template <typename T, typename GetOffsetFunc, typename GetVTFunc = std::nullptr_t, typename... Args>
static T* _SafeGetImpl(GetOffsetFunc getOffset, const char* moduleName, bool isDoublePtr, GetVTFunc getVT, Args... args) {
if (!getOffset(args...))
return nullptr;
extern EGameSDK_API bool IsVftableScanningDisabled();
extern EGameSDK_API void SetIsVftableScanningDisabled(bool value);
T* ptr = isDoublePtr ? *reinterpret_cast<T**>(getOffset(args...)) : reinterpret_cast<T*>(getOffset(args...));
if (moduleName && !Utils::Memory::IsValidPtrMod(ptr, moduleName))
return nullptr;
if constexpr (!std::is_same_v<GetVTFunc, std::nullptr_t>) {
if (*reinterpret_cast<DWORD64**>(ptr) != getVT())
template <typename T, typename GetOffsetFunc, typename... Args>
T* _SafeGetImpl(GetOffsetFunc getOffset, bool isDoublePtr, std::string_view className, Args... args) {
if (!getOffset(args...))
return nullptr;
}
return ptr;
T* ptr = isDoublePtr ? *reinterpret_cast<T**>(getOffset(args...)) : reinterpret_cast<T*>(getOffset(args...));
if (Utils::Memory::IsBadReadPtr(ptr))
return nullptr;
if (IsVftableScanningDisabled())
return ptr;
if (!className.empty() && !Utils::RTTI::IsClassVTableNameEqualTo(ptr, className))
return nullptr;
else if (className.empty() && !Utils::RTTI::IsClassVTableNameEqualTo(ptr, Utils::Values::GetSimpleTypeName(typeid(T).name())))
return nullptr;
return ptr;
}
template <typename T, typename GetOffsetFunc, typename... Args>
T* SafeGetter(GetOffsetFunc getOffset, bool isDoublePtr = true, std::string_view className = {}, Args... args) {
return Utils::Memory::SafeExecution::Execute<T*>(
reinterpret_cast<uint64_t>(&_SafeGetImpl<T, GetOffsetFunc, Args...>),
nullptr,
reinterpret_cast<void*>(getOffset),
isDoublePtr,
className,
args...
);
}
}
template <typename T, typename GetOffsetFunc, typename GetVTFunc = std::nullptr_t, typename... Args>
static T* _SafeGetter(GetOffsetFunc getOffset, const char* moduleName, bool isDoublePtr = true, GetVTFunc getVT = nullptr, Args... args) {
return Utils::Memory::SafeExecution::Execute<T*>(
reinterpret_cast<uint64_t>(&_SafeGetImpl<T, GetOffsetFunc, GetVTFunc, Args...>),
nullptr,
reinterpret_cast<void*>(getOffset),
moduleName,
isDoublePtr,
reinterpret_cast<void*>(getVT),
args...
);
}
}
}

View File

@ -5,11 +5,11 @@ namespace EGSDK::Engine {
class EGameSDK_API CBaseCamera {
public:
/*union {
buffer<0x48, float> yaw;
buffer<0x4C, float> X;
buffer<0x58, float> pitch;
buffer<0x5C, float> Y;
buffer<0x6C, float> Z;
ClassHelpers::buffer<0x48, float> yaw;
ClassHelpers::buffer<0x4C, float> X;
ClassHelpers::buffer<0x58, float> pitch;
ClassHelpers::buffer<0x5C, float> Y;
ClassHelpers::buffer<0x6C, float> Z;
};*/
Vector3* GetForwardVector(Vector3* outForwardVec);
Vector3* GetUpVector(Vector3* outUpVec);

View File

@ -6,9 +6,9 @@ namespace EGSDK::Engine {
class EGameSDK_API CBulletPhysicsCharacter {
public:
union {
buffer<0xCB8, Vector3> playerPos2;
buffer<0xCD0, Vector3> playerPos;
buffer<0x1050, float> playerDownwardVelocity;
ClassHelpers::buffer<0xCB8, Vector3> playerPos2;
ClassHelpers::buffer<0xCD0, Vector3> playerPos;
ClassHelpers::buffer<0x1050, float> playerDownwardVelocity;
};
static Vector3 posBeforeFreeze;

View File

@ -7,7 +7,7 @@ namespace EGSDK::Engine {
class EGameSDK_API CGSObject {
public:
union {
buffer<0x48, CLevel2*> pCLevel2;
ClassHelpers::buffer<0x48, CLevel2*> pCLevel2;
};
static CGSObject* Get();

View File

@ -10,7 +10,7 @@ namespace EGSDK {
class EGameSDK_API CGSObject2 {
public:
union {
buffer<0x20, GamePH::LogicalPlayer*> pLogicalPlayer;
ClassHelpers::buffer<0x20, GamePH::LogicalPlayer*> pLogicalPlayer;
};
static CGSObject2* Get();

View File

@ -13,9 +13,9 @@ namespace EGSDK {
class EGameSDK_API CGame {
public:
union {
buffer<0x8, GamePH::GameDI_PH*> pGameDI_PH;
buffer<0x28, CVideoSettings*> pCVideoSettings;
buffer<0x390, CLevel*> pCLevel;
ClassHelpers::buffer<0x8, GamePH::GameDI_PH*> pGameDI_PH;
ClassHelpers::buffer<0x28, CVideoSettings*> pCVideoSettings;
ClassHelpers::buffer<0x390, CLevel*> pCLevel;
};
static CGame* Get();

View File

@ -12,8 +12,8 @@ namespace EGSDK {
class EGameSDK_API CLevel {
public:
union {
buffer<0x20, GamePH::LevelDI*> pLevelDI;
buffer<0x30, CGSObject*> pCGSObject;
ClassHelpers::buffer<0x20, GamePH::LevelDI*> pLevelDI;
ClassHelpers::buffer<0x30, CGSObject*> pCGSObject;
};
static CLevel* Get();

View File

@ -7,7 +7,7 @@ namespace EGSDK::Engine {
class EGameSDK_API CLevel2 {
public:
union {
buffer<0x28, CGSObject2*> pCGSObject2;
ClassHelpers::buffer<0x28, CGSObject2*> pCGSObject2;
};
static CLevel2* Get();

View File

@ -7,7 +7,7 @@ namespace EGSDK::Engine {
class EGameSDK_API CLobbySteam {
public:
union {
buffer<0xF8, CGame*> pCGame;
ClassHelpers::buffer<0xF8, CGame*> pCGame;
};
static CLobbySteam* Get();

View File

@ -5,7 +5,7 @@ namespace EGSDK::Engine {
class EGameSDK_API CVideoSettings {
public:
union {
buffer<0x7C, float> extraFOV;
ClassHelpers::buffer<0x7C, float> extraFOV;
};
static CVideoSettings* Get();

View File

@ -7,7 +7,7 @@ namespace EGSDK::Engine {
class EGameSDK_API CoPhysicsProperty {
public:
union {
buffer<0x20, CBulletPhysicsCharacter*> pCBulletPhysicsCharacter;
ClassHelpers::buffer<0x20, CBulletPhysicsCharacter*> pCBulletPhysicsCharacter;
};
static CoPhysicsProperty* Get();

View File

@ -7,7 +7,7 @@ namespace EGSDK::GamePH {
class EGameSDK_API CoBaseCameraProxy {
public:
union {
buffer<0xD0, TPPCameraDI*> pTPPCameraDI;
ClassHelpers::buffer<0xD0, TPPCameraDI*> pTPPCameraDI;
};
};
}

View File

@ -5,9 +5,9 @@ namespace EGSDK::GamePH {
class EGameSDK_API DayNightCycle {
public:
union {
buffer<0x10, float> time1;
buffer<0x20, float> time2;
buffer<0x5C, float> time3;
ClassHelpers::buffer<0x10, float> time1;
ClassHelpers::buffer<0x20, float> time2;
ClassHelpers::buffer<0x5C, float> time3;
};
void SetDaytime(float time);

View File

@ -8,12 +8,12 @@ namespace EGSDK::GamePH {
class EGameSDK_API FreeCamera : public Engine::CBaseCamera {
public:
union {
buffer<0x18, CoBaseCameraProxy*> pCoBaseCameraProxy;
buffer<0x38, Engine::CBaseCamera*> pCBaseCamera;
buffer<0x42, bool> enableSpeedMultiplier1;
buffer<0x43, bool> enableSpeedMultiplier2;
buffer<0x1B4, float> FOV;
buffer<0x1CC, float> speedMultiplier;
ClassHelpers::buffer<0x18, CoBaseCameraProxy*> pCoBaseCameraProxy;
ClassHelpers::buffer<0x38, Engine::CBaseCamera*> pCBaseCamera;
ClassHelpers::buffer<0x42, bool> enableSpeedMultiplier1;
ClassHelpers::buffer<0x43, bool> enableSpeedMultiplier2;
ClassHelpers::buffer<0x1B4, float> FOV;
ClassHelpers::buffer<0x1CC, float> speedMultiplier;
};
void AllowCameraMovement(int mode = 2);

View File

@ -7,8 +7,8 @@ namespace EGSDK::GamePH {
class EGameSDK_API GameDI_PH {
public:
union {
buffer<0x130, SessionCooperativeDI*> pSessionCooperativeDI;
buffer<0x910, bool> blockPauseGameOnPlayerAfk;
ClassHelpers::buffer<0x130, SessionCooperativeDI*> pSessionCooperativeDI;
ClassHelpers::buffer<0x910, bool> blockPauseGameOnPlayerAfk;
};
float GetGameTimeDelta();

View File

@ -4,6 +4,6 @@
namespace EGSDK::GamePH {
class EGameSDK_API InventoryMoney {
public:
buffer<0x38, int> oldWorldMoney;
ClassHelpers::buffer<0x38, int> oldWorldMoney;
};
}

View File

@ -5,7 +5,7 @@ namespace EGSDK::GamePH {
class EGameSDK_API ItemDescWithContext {
public:
union {
buffer<0xA8, float> weaponDurability;
ClassHelpers::buffer<0xA8, float> weaponDurability;
};
};
}

View File

@ -7,7 +7,7 @@ namespace EGSDK::GamePH {
class EGameSDK_API LocalClientDI {
public:
union {
buffer<0x90, PlayerDI_PH*> pPlayerDI_PH;
ClassHelpers::buffer<0x90, PlayerDI_PH*> pPlayerDI_PH;
};
static LocalClientDI* Get();

View File

@ -11,9 +11,9 @@ namespace EGSDK::GamePH {
class EGameSDK_API PlayerDI_PH {
public:
union {
buffer<0xF0, Engine::CoPhysicsProperty*> pCoPhysicsProperty;
buffer<0x35E9, bool> enableTPPModel1;
buffer<0x35EA, bool> enableTPPModel2;
ClassHelpers::buffer<0xF0, Engine::CoPhysicsProperty*> pCoPhysicsProperty;
ClassHelpers::buffer<0x35E9, bool> enableTPPModel1;
ClassHelpers::buffer<0x35EA, bool> enableTPPModel2;
};
static PlayerDI_PH* Get();

View File

@ -8,9 +8,9 @@ namespace EGSDK::GamePH {
class EGameSDK_API PlayerHealthModule {
public:
union {
buffer<0x8, PlayerDI_PH*> pPlayerDI_PH;
buffer<0x2C, float> health;
buffer<0x3C, float> maxHealth;
ClassHelpers::buffer<0x8, PlayerDI_PH*> pPlayerDI_PH;
ClassHelpers::buffer<0x2C, float> health;
ClassHelpers::buffer<0x3C, float> maxHealth;
};
~PlayerHealthModule();

View File

@ -8,9 +8,9 @@ namespace EGSDK::GamePH {
class EGameSDK_API PlayerInfectionModule {
public:
union {
buffer<0x8, PlayerDI_PH*> pPlayerDI_PH;
buffer<0x20, float> maxImmunity;
buffer<0x2C, float> immunity;
ClassHelpers::buffer<0x8, PlayerDI_PH*> pPlayerDI_PH;
ClassHelpers::buffer<0x20, float> maxImmunity;
ClassHelpers::buffer<0x2C, float> immunity;
};
~PlayerInfectionModule();

View File

@ -7,7 +7,7 @@ namespace EGSDK::GamePH {
class EGameSDK_API PlayerState {
public:
union {
buffer<0x300, PlayerVariables*> pPlayerVariables;
ClassHelpers::buffer<0x300, PlayerVariables*> pPlayerVariables;
};
static PlayerState* Get();

View File

@ -7,7 +7,7 @@ namespace EGSDK::GamePH {
class EGameSDK_API SessionCooperativeDI {
public:
union {
buffer<0xE08, LocalClientDI*> pLocalClientDI;
ClassHelpers::buffer<0xE08, LocalClientDI*> pLocalClientDI;
};
static SessionCooperativeDI* Get();

View File

@ -9,7 +9,7 @@ namespace EGSDK {
static EGameSDK_API retType Get_## name () {\
static retType name = NULL;\
static int i = 0;\
if (Utils::Memory::IsValidPtr(name) || !GetModuleHandleA(moduleName) || i >= 50) return name;\
if (!Utils::Memory::IsBadReadPtr(name) || !GetModuleHandleA(moduleName) || i >= 50) return name;\
i++;\
return name=reinterpret_cast<retType>(Utils::SigScan::PatternScanner::FindPattern(moduleName, {pattern, type}));\
}
@ -22,54 +22,21 @@ namespace EGSDK {
#define AddStaticOffset2(name, moduleName, off) \
static EGameSDK_API DWORD64 Get_## name () {\
static DWORD64 name = 0;\
if (Utils::Memory::IsValidPtr(name)) return name;\
if (!Utils::Memory::IsBadReadPtr(name)) return name;\
return name=reinterpret_cast<DWORD64>(GetModuleHandleA(moduleName)) + static_cast<DWORD64>(off);\
}
#define AddVTOffset(name, moduleName, rttiName, retType)\
static EGameSDK_API retType GetVT_## name () {\
static retType VT_## name = NULL;\
static int i = 0;\
if (Utils::Memory::IsValidPtr(VT_## name)|| !GetModuleHandleA(moduleName) || i >= 50) return VT_## name;\
i++;\
return VT_## name=reinterpret_cast<retType>(Utils::RTTI::GetVTablePtr(moduleName, rttiName));\
}
struct Offsets {
// Input related
AddOffset(CInput, "engine_x64_rwdi.dll", "48 8B 0D [?? ?? ?? ?? 48 85 C9 74 ?? 48 8B 01 84 D2", Utils::SigScan::PatternType::RelativePointer, DWORD64**) // g_CInput
// Player vars related
AddVTOffset(FloatPlayerVariable, "gamedll_ph_x64_rwdi.dll", "FloatPlayerVariable", void*)
AddVTOffset(BoolPlayerVariable, "gamedll_ph_x64_rwdi.dll", "BoolPlayerVariable", void*)
AddVTOffset(TypedFieldMetaFloatPlayerVariable, "gamedll_ph_x64_rwdi.dll", "?$TypedFieldMeta@VFloatPlayerVariable@@@?$FieldsCollection@VPlayerVariables@@@constds", void*)
AddVTOffset(TypedFieldMetaBoolPlayerVariable, "gamedll_ph_x64_rwdi.dll", "?$TypedFieldMeta@VBoolPlayerVariable@@@?$FieldsCollection@VPlayerVariables@@@constds", void*)
//AddVTOffset(TypedFieldMetaFloatPlayerVariable, "gamedll_ph_x64_rwdi.dll", "?$TypedFieldMeta@VFloatPlayerVariable@@@?$FieldsCollection@VPlayerVariables@@@constds", void*)
//AddVTOffset(TypedFieldMetaBoolPlayerVariable, "gamedll_ph_x64_rwdi.dll", "?$TypedFieldMeta@VBoolPlayerVariable@@@?$FieldsCollection@VPlayerVariables@@@constds", void*)
AddOffset(LoadPlayerVars, "gamedll_ph_x64_rwdi.dll", "48 89 4C 24 ?? B8 ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 2B E0 48 8B 8C 24", Utils::SigScan::PatternType::Address, void*)
AddOffset(PlayerState, "gamedll_ph_x64_rwdi.dll", "48 8B 35 [?? ?? ?? ?? 4C 8B F2 48 8B F9", Utils::SigScan::PatternType::RelativePointer, void*)
// Game related
AddVTOffset(CBulletPhysicsCharacter, "engine_x64_rwdi.dll", "CBulletPhysicsCharacter", void*)
AddVTOffset(CGSObject, "engine_x64_rwdi.dll", "CGSObject", void*)
AddVTOffset(CInput, "engine_x64_rwdi.dll", "CInput", void*)
AddVTOffset(CLevel, "engine_x64_rwdi.dll", "CLevel", void*)
AddVTOffset(CLobbySteam, "engine_x64_rwdi.dll", "CLobbySteam", void*)
AddVTOffset(CVideoSettings, "engine_x64_rwdi.dll", "CVideoSettings", void*)
AddVTOffset(DayNightCycle, "gamedll_ph_x64_rwdi.dll", "DayNightCycle", void*)
AddVTOffset(FreeCamera, "gamedll_ph_x64_rwdi.dll", "FreeCamera", void*)
AddVTOffset(GameDI_PH, "gamedll_ph_x64_rwdi.dll", "GameDI_PH", void*)
AddVTOffset(InventoryContainerDI, "gamedll_ph_x64_rwdi.dll", "InventoryContainerDI", void*)
AddVTOffset(InventoryItem, "gamedll_ph_x64_rwdi.dll", "InventoryItem", void*)
AddVTOffset(InventoryMoney, "gamedll_ph_x64_rwdi.dll", "InventoryMoney", void*)
AddVTOffset(ItemDescWithContext, "gamedll_ph_x64_rwdi.dll", "ItemDescWithContext", void*)
AddVTOffset(LevelDI, "gamedll_ph_x64_rwdi.dll", "LevelDI", void*)
AddVTOffset(LocalClientDI, "gamedll_ph_x64_rwdi.dll", "LocalClientDI", void*)
AddVTOffset(LogicalPlayer, "gamedll_ph_x64_rwdi.dll", "LogicalPlayer", void*)
AddVTOffset(PlayerDI_PH, "gamedll_ph_x64_rwdi.dll", "PlayerDI_PH", void*)
AddVTOffset(PlayerState, "gamedll_ph_x64_rwdi.dll", "PlayerState", void*)
AddVTOffset(SessionCooperativeDI, "gamedll_ph_x64_rwdi.dll", "SessionCooperativeDI", void*)
AddVTOffset(TPPCameraDI, "gamedll_ph_x64_rwdi.dll", "TPPCameraDI", void*)
AddStaticOffset(gameDI_PH2_offset, 0x28)
AddStaticOffset(allowVelocityMod_offset, 0x5C)
AddStaticOffset(disableHeadCorrection_offset, 0x108)

View File

@ -13,10 +13,10 @@ namespace EGSDK::Utils {
static const BYTE SigScanWildCard = 0xAA;
static const std::string_view SigScanWildCardStr = "AA";
class SafeExecution {
private:
static int fail(unsigned int code, struct _EXCEPTION_POINTERS* ep);
class EGameSDK_API SafeExecution {
public:
static int fail(unsigned int code, struct _EXCEPTION_POINTERS* ep);
template<typename T = void*, typename R = T, typename... Args>
static T Execute(uint64_t ptr, R ret, Args... args) {
__try {
@ -40,25 +40,93 @@ namespace EGSDK::Utils {
extern EGameSDK_API const MODULEINFO GetModuleInfo(const char* szModule);
extern EGameSDK_API const FARPROC GetProcAddr(const std::string_view& module, const std::string_view& funcName);
extern EGameSDK_API const bool IsAddressValidMod(const DWORD64 ptr, const char* moduleName);
extern EGameSDK_API std::string BytesToIDAPattern(BYTE* bytes, size_t size);
extern EGameSDK_API std::string ConvertSigToScannerSig(const char* pattern, int* offsetToAddrInSig = nullptr);
extern EGameSDK_API DWORD64 CalcTargetAddrOfRelInst(DWORD64 addrOfInst, size_t opSize);
extern EGameSDK_API DWORD64 CalcTargetAddrOfRelativeInstr(DWORD64 addrOfInst, size_t opSize);
extern EGameSDK_API std::vector<DWORD64> GetXrefsTo(DWORD64 address, DWORD64 start, size_t size);
#pragma region Templates
template<typename ptrT> bool IsValidPtr(ptrT ptr) {
__try {
return !IsBadReadPtr(reinterpret_cast<void*>(ptr), sizeof(void*));
} __except(EXCEPTION_EXECUTE_HANDLER) {
return false;
/*
Copyright (c) 2017 Artem Boldarev <artem.boldarev@gmail.com>
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.
*/
// COPYRIGHT NOTICE - START
template <typename T>
bool IsBadMemPtr(const bool write, T* ptr, const std::size_t size) {
const auto min_ptr = 0x10000;
const auto max_ptr = 0x000F000000000000;
if (ptr == nullptr || reinterpret_cast<DWORD64>(ptr) < min_ptr || reinterpret_cast<DWORD64>(ptr) >= max_ptr)
return true;
DWORD mask = PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY;
if (write)
mask = PAGE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY;
auto current = reinterpret_cast<BYTE*>(ptr);
const auto last = current + size;
// So we are considering the region:
// [ptr, ptr+size)
while (current < last) {
MEMORY_BASIC_INFORMATION mbi;
// We couldn't get any information on this region.
// Let's not risk any read/write operation.
if (VirtualQuery(reinterpret_cast<LPCVOID>(current), &mbi, sizeof mbi) == 0)
return true;
// We can't perform our desired read/write operations in this region.
if ((mbi.Protect & mask) == 0)
return true;
// We can't access this region.
if (mbi.Protect & (PAGE_GUARD | PAGE_NOACCESS))
return true;
// Let's consider the next region.
current = reinterpret_cast<BYTE*>(mbi.BaseAddress) + mbi.RegionSize;
}
return false;
}
template<typename ptrT = void*> bool IsValidPtrMod(ptrT ptr, const char* moduleName, const bool checkForVT = true) {
return IsValidPtr<ptrT>(ptr) && IsAddressValidMod(checkForVT ? *(PDWORD64)(ptr) : (DWORD64)(ptr), moduleName);
template <typename T>
bool IsBadReadPtr(T* ptr, const std::size_t size) {
return IsBadMemPtr(false, ptr, size);
}
template <typename T>
bool IsBadReadPtr(T* ptr) {
return IsBadReadPtr(ptr, sizeof ptr);
}
template <typename T>
bool IsBadWritePtr(T* ptr, const std::size_t size) {
return IsBadMemPtr(true, ptr, size);
}
template <typename T>
bool IsBadWritePtr(T* ptr) {
return IsBadWritePtr(ptr, sizeof ptr);
}
// COPYRIGHT NOTICE - END
template <std::size_t Index, typename ReturnType = void, typename... Args> __forceinline ReturnType CallVT(void* instance, Args... args) {
using Fn = ReturnType(__thiscall*)(void*, Args...);

View File

@ -6,6 +6,9 @@
namespace EGSDK::Utils {
namespace RTTI {
extern EGameSDK_API DWORD64 GetVTablePtr(const char* moduleName, const char* tableName);
extern EGameSDK_API std::string GetVTableNameFromVTPtr(void* vtPtr);
extern EGameSDK_API std::string GetVTableName(void* classPtr);
extern EGameSDK_API bool IsClassVTableNameEqualTo(void* classPtr, std::string_view tableName);
extern EGameSDK_API bool IsVTableNameEqualTo(void* vtPtr, std::string_view tableName);
}
}

View File

@ -16,5 +16,7 @@ namespace EGSDK::Utils {
else
return std::to_string(val);
}
extern EGameSDK_API std::string GetSimpleTypeName(std::string fullName);
}
}

View File

@ -0,0 +1,10 @@
#include <EGSDK\ClassHelpers.h>
namespace EGSDK {
namespace ClassHelpers {
static volatile LONG disableVftableScanning = 0;
bool IsVftableScanningDisabled() { return _InterlockedCompareExchange(&disableVftableScanning, 0, 0) != 0; }
void SetIsVftableScanningDisabled(bool value) { _InterlockedExchange(&disableVftableScanning, value ? 1 : 0); }
}
}

View File

@ -20,6 +20,6 @@ namespace EGSDK::Engine {
return pCoPhysicsProperty ? pCoPhysicsProperty->pCBulletPhysicsCharacter : nullptr;
}
CBulletPhysicsCharacter* CBulletPhysicsCharacter::Get() {
return _SafeGetter<CBulletPhysicsCharacter>(GetOffset_CBulletPhysicsCharacter, "engine_x64_rwdi.dll", false, Offsets::GetVT_CBulletPhysicsCharacter);
return ClassHelpers::SafeGetter<CBulletPhysicsCharacter>(GetOffset_CBulletPhysicsCharacter, false);
}
}

View File

@ -9,6 +9,6 @@ namespace EGSDK::Engine {
return pCLevel ? pCLevel->pCGSObject : nullptr;
}
CGSObject* CGSObject::Get() {
return _SafeGetter<CGSObject>(GetOffset_CGSObject, "engine_x64_rwdi.dll", false, Offsets::GetVT_CGSObject);
return ClassHelpers::SafeGetter<CGSObject>(GetOffset_CGSObject, false);
}
}

View File

@ -9,6 +9,6 @@ namespace EGSDK::Engine {
return pCLevel2 ? pCLevel2->pCGSObject2 : nullptr;
}
CGSObject2* CGSObject2::Get() {
return _SafeGetter<CGSObject2>(GetOffset_CGSObject2, "engine_x64_rwdi.dll", false, nullptr);
return ClassHelpers::SafeGetter<CGSObject2>(GetOffset_CGSObject2, false);
}
}

View File

@ -9,6 +9,6 @@ namespace EGSDK::Engine {
return pCLobbySteam ? pCLobbySteam->pCGame : nullptr;
}
CGame* CGame::Get() {
return _SafeGetter<CGame>(GetOffset_CGame, "engine_x64_rwdi.dll", false, nullptr);
return ClassHelpers::SafeGetter<CGame>(GetOffset_CGame, false);
}
}

View File

@ -11,6 +11,6 @@ namespace EGSDK::Engine {
}
CInput* CInput::Get() {
return _SafeGetter<CInput>(Offsets::Get_CInput, "engine_x64_rwdi.dll", true, Offsets::GetVT_CInput);
return ClassHelpers::SafeGetter<CInput>(Offsets::Get_CInput, true);
}
}

View File

@ -9,6 +9,6 @@ namespace EGSDK::Engine {
return pCGame ? pCGame->pCLevel : nullptr;
}
CLevel* CLevel::Get() {
return _SafeGetter<CLevel>(GetOffset_CLevel, "engine_x64_rwdi.dll", false, Offsets::GetVT_CLevel);
return ClassHelpers::SafeGetter<CLevel>(GetOffset_CLevel, false);
}
}

View File

@ -9,6 +9,6 @@ namespace EGSDK::Engine {
return pCGSObject ? pCGSObject->pCLevel2 : nullptr;
}
CLevel2* CLevel2::Get() {
return _SafeGetter<CLevel2>(GetOffset_CLevel2, "engine_x64_rwdi.dll", false, nullptr);
return ClassHelpers::SafeGetter<CLevel2>(GetOffset_CLevel2, false);
}
}

View File

@ -4,6 +4,6 @@
namespace EGSDK::Engine {
CLobbySteam* CLobbySteam::Get() {
return _SafeGetter<CLobbySteam>(Offsets::Get_CLobbySteam, "engine_x64_rwdi.dll", true, Offsets::GetVT_CLobbySteam);
return ClassHelpers::SafeGetter<CLobbySteam>(Offsets::Get_CLobbySteam, true);
}
}

View File

@ -9,6 +9,6 @@ namespace EGSDK::Engine {
return pCGame ? pCGame->pCVideoSettings : nullptr;
}
CVideoSettings* CVideoSettings::Get() {
return _SafeGetter<CVideoSettings>(GetOffset_CVideoSettings, "engine_x64_rwdi.dll", false, Offsets::GetVT_CVideoSettings);
return ClassHelpers::SafeGetter<CVideoSettings>(GetOffset_CVideoSettings, false);
}
}

View File

@ -9,6 +9,6 @@ namespace EGSDK::Engine {
return pPlayerDI_PH ? pPlayerDI_PH->pCoPhysicsProperty : nullptr;
}
CoPhysicsProperty* CoPhysicsProperty::Get() {
return _SafeGetter<CoPhysicsProperty>(GetOffset_CoPhysicsProperty, "engine_x64_rwdi.dll", false, nullptr);
return ClassHelpers::SafeGetter<CoPhysicsProperty>(GetOffset_CoPhysicsProperty, false);
}
}

View File

@ -11,6 +11,6 @@ namespace EGSDK::GamePH {
}
DayNightCycle* DayNightCycle::Get() {
return _SafeGetter<DayNightCycle>(Offsets::Get_DayNightCycle, "gamedll_ph_x64_rwdi.dll", true, Offsets::GetVT_DayNightCycle);
return ClassHelpers::SafeGetter<DayNightCycle>(Offsets::Get_DayNightCycle, true);
}
}

View File

@ -8,6 +8,6 @@ namespace EGSDK::GamePH {
}
FreeCamera* FreeCamera::Get() {
return _SafeGetter<FreeCamera>(Offsets::Get_FreeCamera, "gamedll_ph_x64_rwdi.dll", true, Offsets::GetVT_FreeCamera);
return ClassHelpers::SafeGetter<FreeCamera>(Offsets::Get_FreeCamera, true);
}
}

View File

@ -17,6 +17,6 @@ namespace EGSDK::GamePH {
return pCGame ? pCGame->pGameDI_PH : nullptr;
}
GameDI_PH* GameDI_PH::Get() {
return _SafeGetter<GameDI_PH>(GetOffset_GameDI_PH, "gamedll_ph_x64_rwdi.dll", false, Offsets::GetVT_GameDI_PH);
return ClassHelpers::SafeGetter<GameDI_PH>(GetOffset_GameDI_PH, false);
}
}

View File

@ -9,6 +9,6 @@ namespace EGSDK::GamePH {
return pGameDI_PH ? reinterpret_cast<GameDI_PH2*>(reinterpret_cast<DWORD64>(pGameDI_PH) + Offsets::Get_gameDI_PH2_offset()) : nullptr;
}
GameDI_PH2* GameDI_PH2::Get() {
return _SafeGetter<GameDI_PH2>(GetOffset_GameDI_PH2, "gamedll_ph_x64_rwdi.dll", false, nullptr);
return ClassHelpers::SafeGetter<GameDI_PH2>(GetOffset_GameDI_PH2, false);
}
}

View File

@ -8,6 +8,6 @@ namespace EGSDK::GamePH {
return Utils::Memory::_SafeCallFunctionOffset<InventoryMoney*>(Offsets::Get_PlayerGetInventoryMoney, nullptr, pInventoryContainerDI, indexMaybe);
}
InventoryMoney* InventoryContainerDI::GetInventoryMoney(UINT indexMaybe) {
return _SafeGetter<InventoryMoney>(GetOffset_InventoryMoney, "gamedll_ph_x64_rwdi.dll", false, Offsets::GetVT_InventoryMoney, this, indexMaybe);
return ClassHelpers::SafeGetter<InventoryMoney>(GetOffset_InventoryMoney, false, {}, this, indexMaybe);
}
}

View File

@ -8,6 +8,6 @@ namespace EGSDK::GamePH {
return reinterpret_cast<ItemDescWithContext*>(reinterpret_cast<DWORD64>(pInventoryItem) + 0x40);
}
ItemDescWithContext* InventoryItem::GetItemDescCtx() {
return _SafeGetter<ItemDescWithContext>(GetOffset_ItemDescWithContext, "gamedll_ph_x64_rwdi.dll", false, Offsets::GetVT_ItemDescWithContext, this);
return ClassHelpers::SafeGetter<ItemDescWithContext>(GetOffset_ItemDescWithContext, false, {}, this);
}
}

View File

@ -70,7 +70,7 @@ namespace EGSDK::GamePH {
return pCLevel ? pCLevel->pLevelDI : nullptr;
}
LevelDI* LevelDI::Get() {
LevelDI* ptr = _SafeGetter<LevelDI>(GetOffset_LevelDI, "gamedll_ph_x64_rwdi.dll", false, Offsets::GetVT_LevelDI);
LevelDI* ptr = ClassHelpers::SafeGetter<LevelDI>(GetOffset_LevelDI, false);
if (!ptr)
ResetLoadTimer();

View File

@ -9,6 +9,6 @@ namespace EGSDK::GamePH {
return pSessionCooperativeDI ? pSessionCooperativeDI->pLocalClientDI : nullptr;
}
LocalClientDI* LocalClientDI::Get() {
return _SafeGetter<LocalClientDI>(GetOffset_LocalClientDI, "gamedll_ph_x64_rwdi.dll", false, Offsets::GetVT_LocalClientDI);
return ClassHelpers::SafeGetter<LocalClientDI>(GetOffset_LocalClientDI, false);
}
}

View File

@ -9,6 +9,6 @@ namespace EGSDK::GamePH{
return pCGSObject2 ? pCGSObject2->pLogicalPlayer : nullptr;
}
LogicalPlayer* LogicalPlayer::Get() {
return _SafeGetter<LogicalPlayer>(GetOffset_LogicalPlayer, "gamedll_ph_x64_rwdi.dll", false, Offsets::GetVT_LogicalPlayer);
return ClassHelpers::SafeGetter<LogicalPlayer>(GetOffset_LogicalPlayer, false);
}
}

View File

@ -10,14 +10,14 @@ namespace EGSDK::GamePH {
return Utils::Memory::_SafeCallFunctionOffset<InventoryItem*>(Offsets::Get_PlayerGetCurrentWeapon, nullptr, pPlayerDI_PH, indexMaybe);
}
InventoryItem* PlayerDI_PH::GetCurrentWeapon(UINT indexMaybe) {
return _SafeGetter<InventoryItem>(GetOffset_CurrentWeapon, "gamedll_ph_x64_rwdi.dll", false, Offsets::GetVT_InventoryItem, this, indexMaybe);
return ClassHelpers::SafeGetter<InventoryItem>(GetOffset_CurrentWeapon, false, {}, this, indexMaybe);
}
static InventoryContainerDI* GetOffset_InventoryContainer(PlayerDI_PH* pPlayerDI_PH) {
return reinterpret_cast<InventoryContainerDI*>(*reinterpret_cast<DWORD64*>(reinterpret_cast<DWORD64>(pPlayerDI_PH) + 0x470));
}
InventoryContainerDI* PlayerDI_PH::GetInventoryContainer() {
return _SafeGetter<InventoryContainerDI>(GetOffset_InventoryContainer, "gamedll_ph_x64_rwdi.dll", false, Offsets::GetVT_InventoryContainerDI, this);
return ClassHelpers::SafeGetter<InventoryContainerDI>(GetOffset_InventoryContainer, false, {}, this);
}
static PlayerDI_PH* GetOffset_PlayerDI_PH() {
@ -25,6 +25,6 @@ namespace EGSDK::GamePH {
return pLocalClientDI ? pLocalClientDI->pPlayerDI_PH : nullptr;
}
PlayerDI_PH* PlayerDI_PH::Get() {
return _SafeGetter<PlayerDI_PH>(GetOffset_PlayerDI_PH, "gamedll_ph_x64_rwdi.dll", false, Offsets::GetVT_PlayerDI_PH);
return ClassHelpers::SafeGetter<PlayerDI_PH>(GetOffset_PlayerDI_PH, false);
}
}

View File

@ -20,7 +20,7 @@ namespace EGSDK::GamePH {
return pPlayerHealthModule;
}
PlayerHealthModule* PlayerHealthModule::Get() {
return _SafeGetter<PlayerHealthModule>(GetOffset_PlayerHealthModule, nullptr, false, nullptr);
return ClassHelpers::SafeGetter<PlayerHealthModule>(GetOffset_PlayerHealthModule, false);
}
void PlayerHealthModule::EmplaceBack(PlayerHealthModule* ptr) {

View File

@ -20,7 +20,7 @@ namespace EGSDK::GamePH {
return pPlayerInfectionModule;
}
PlayerInfectionModule* PlayerInfectionModule::Get() {
return _SafeGetter<PlayerInfectionModule>(GetOffset_PlayerInfectionModule, nullptr, false, nullptr);
return ClassHelpers::SafeGetter<PlayerInfectionModule>(GetOffset_PlayerInfectionModule, false);
}
void PlayerInfectionModule::EmplaceBack(PlayerInfectionModule* ptr) {

View File

@ -4,6 +4,6 @@
namespace EGSDK::GamePH {
PlayerState* PlayerState::Get() {
return _SafeGetter<PlayerState>(Offsets::Get_PlayerState, "gamedll_ph_x64_rwdi.dll", true, Offsets::GetVT_PlayerState);
return ClassHelpers::SafeGetter<PlayerState>(Offsets::Get_PlayerState, true);
}
}

View File

@ -34,8 +34,8 @@ namespace EGSDK::GamePH {
static int offset = 0;
__try {
while (true) {
const bool isFloatPlayerVar = *(playerVarsGetter() + offset) == Offsets::GetVT_FloatPlayerVariable();
const bool isBoolPlayerVar = *(playerVarsGetter() + offset) == Offsets::GetVT_BoolPlayerVariable();
const bool isFloatPlayerVar = Utils::RTTI::IsClassVTableNameEqualTo(*(playerVarsGetter() + offset), "FloatPlayerVariable");
const bool isBoolPlayerVar = Utils::RTTI::IsClassVTableNameEqualTo(*(playerVarsGetter() + offset), "BoolPlayerVariable");
if (isFloatPlayerVar || isBoolPlayerVar) {
var.second.first = playerVarsGetter() + offset + VAR_LOC_OFFSET;
@ -71,8 +71,6 @@ namespace EGSDK::GamePH {
return;
if (!Get())
return;
if (!Offsets::GetVT_FloatPlayerVariable() || !Offsets::GetVT_BoolPlayerVariable())
return;
for (auto& var : playerVars)
processPlayerVar(reinterpret_cast<DWORD64**(*)()>(&Get), var);
@ -83,11 +81,11 @@ namespace EGSDK::GamePH {
#pragma region Player Variables Sorting
struct VarTypeFieldMeta {
PlayerVariables::PlayerVarType type;
void*(*getFieldMetaVT)();
std::string_view className;
};
const std::vector<VarTypeFieldMeta> varTypeFields = {
{ PlayerVariables::PlayerVarType::Float, Offsets::GetVT_TypedFieldMetaFloatPlayerVariable },
{ PlayerVariables::PlayerVarType::Bool, Offsets::GetVT_TypedFieldMetaBoolPlayerVariable }
{ PlayerVariables::PlayerVarType::Float, "constds::FieldsCollection<PlayerVariables>::TypedFieldMeta<FloatPlayerVariable>" },
{ PlayerVariables::PlayerVarType::Bool, "constds::FieldsCollection<PlayerVariables>::TypedFieldMeta<BoolPlayerVariable>" }
};
bool isRetInstruction(BYTE* address) {
@ -117,7 +115,7 @@ namespace EGSDK::GamePH {
continue;
}
playerVarName = reinterpret_cast<const char*>(Utils::Memory::CalcTargetAddrOfRelInst(reinterpret_cast<DWORD64>(funcAddress), 3));
playerVarName = reinterpret_cast<const char*>(Utils::Memory::CalcTargetAddrOfRelativeInstr(reinterpret_cast<DWORD64>(funcAddress), 3));
if (!playerVarName) {
funcAddress++;
continue;
@ -139,9 +137,8 @@ namespace EGSDK::GamePH {
continue;
}
DWORD64 startOfLoadVarFunc = Utils::Memory::CalcTargetAddrOfRelInst(reinterpret_cast<DWORD64>(funcAddress), 1);
DWORD64 startOfLoadVarFunc = Utils::Memory::CalcTargetAddrOfRelativeInstr(reinterpret_cast<DWORD64>(funcAddress), 1);
for (const auto& varType : varTypeFields) {
DWORD64 metaVTAddr = reinterpret_cast<DWORD64>(varType.getFieldMetaVT());
BYTE* loadVarFuncAddress = reinterpret_cast<BYTE*>(startOfLoadVarFunc);
DWORD64 metaVTAddrFromFunc = 0;
@ -152,15 +149,15 @@ namespace EGSDK::GamePH {
continue;
}
metaVTAddrFromFunc = Utils::Memory::CalcTargetAddrOfRelInst(reinterpret_cast<DWORD64>(loadVarFuncAddress), 3);
if (metaVTAddrFromFunc != metaVTAddr) {
metaVTAddrFromFunc = Utils::Memory::CalcTargetAddrOfRelativeInstr(reinterpret_cast<DWORD64>(loadVarFuncAddress), 3);
if (!Utils::RTTI::IsVTableNameEqualTo(reinterpret_cast<DWORD64*>(metaVTAddrFromFunc), varType.className)) {
metaVTAddrFromFunc = 0;
loadVarFuncAddress++;
continue;
}
}
if (metaVTAddr == metaVTAddrFromFunc) {
if (Utils::RTTI::IsVTableNameEqualTo(reinterpret_cast<DWORD64*>(metaVTAddrFromFunc), varType.className)) {
playerVarType = varType.type;
break;
}
@ -219,6 +216,6 @@ namespace EGSDK::GamePH {
return pPlayerState ? pPlayerState->pPlayerVariables : nullptr;
}
PlayerVariables* PlayerVariables::Get() {
return _SafeGetter<PlayerVariables>(GetOffset_PlayerVariables, "gamedll_ph_x64_rwdi.dll", false, nullptr);
return ClassHelpers::SafeGetter<PlayerVariables>(GetOffset_PlayerVariables, false);
}
}

View File

@ -9,6 +9,6 @@ namespace EGSDK::GamePH {
return pGameDI_PH ? pGameDI_PH->pSessionCooperativeDI : nullptr;
}
SessionCooperativeDI* SessionCooperativeDI::Get() {
return _SafeGetter<SessionCooperativeDI>(GetOffset_SessionCooperativeDI, "gamedll_ph_x64_rwdi.dll", false, Offsets::GetVT_SessionCooperativeDI);
return ClassHelpers::SafeGetter<SessionCooperativeDI>(GetOffset_SessionCooperativeDI, false);
}
}

View File

@ -17,6 +17,6 @@ namespace EGSDK::GamePH {
return pCoBaseCameraProxy->pTPPCameraDI;
}
TPPCameraDI* TPPCameraDI::Get() {
return _SafeGetter<TPPCameraDI>(GetOffset_TPPCameraDI, "gamedll_ph_x64_rwdi.dll", false, Offsets::GetVT_TPPCameraDI);
return ClassHelpers::SafeGetter<TPPCameraDI>(GetOffset_TPPCameraDI, false);
}
}

View File

@ -18,7 +18,7 @@ namespace EGSDK::GamePH {
return pLevelDI ? pLevelDI->GetTimeWeatherSystem() : nullptr;
}
CSystem* CSystem::Get() {
return _SafeGetter<CSystem>(GetOffset_CSystem, nullptr, false, nullptr);
return ClassHelpers::SafeGetter<CSystem>(GetOffset_CSystem, false);
}
}
}

View File

@ -50,14 +50,6 @@ namespace EGSDK::Utils {
return GetProcAddress(moduleHandle, funcName.data());
}
const bool IsAddressValidMod(const DWORD64 ptr, const char* moduleName) {
const MODULEINFO moduleInf = GetModuleInfo(moduleName);
const DWORD64 moduleEntryPoint = reinterpret_cast<DWORD64>(moduleInf.EntryPoint);
const DWORD64 moduleEndPoint = moduleEntryPoint + moduleInf.SizeOfImage;
return ptr && (ptr <= moduleEndPoint && ptr >= moduleEntryPoint);
}
std::string BytesToIDAPattern(BYTE* bytes, size_t size) {
std::stringstream idaPattern;
idaPattern << std::hex << std::uppercase << std::setfill('0');
@ -103,7 +95,7 @@ namespace EGSDK::Utils {
return patt;
}
DWORD64 CalcTargetAddrOfRelInst(DWORD64 addrOfInst, size_t opSize) {
DWORD64 CalcTargetAddrOfRelativeInstr(DWORD64 addrOfInst, size_t opSize) {
int offset = *reinterpret_cast<int*>(addrOfInst + opSize);
return addrOfInst + opSize + 4 + offset;

View File

@ -3,6 +3,8 @@
#include <Windows.h>
#include <sstream>
#include <iomanip>
#include <unordered_map>
#include <DbgHelp.h>
#include <memscan\memscan.h>
#include <EGSDK\Utils\RTTI.h>
#include <EGSDK\Utils\Sigscan.h>
@ -10,129 +12,65 @@
namespace EGSDK::Utils {
namespace RTTI {
static std::vector<DWORD64> getXrefsTo(DWORD64 moduleBaseAddr, DWORD64 address, DWORD64 start, size_t size) {
std::vector<DWORD64> xrefs = {};
static std::unordered_map<DWORD64, std::string> vtableAddressCache{};
const DWORD64 finalOffset = address - moduleBaseAddr;
const std::string idaPattern = Memory::BytesToIDAPattern(reinterpret_cast<BYTE*>(const_cast<DWORD64*>(&finalOffset)), 4);
std::string patt = Memory::ConvertSigToScannerSig(idaPattern.c_str());
static std::string _GetVTableNameFromVTPtr(void* vtPtr) {
if (Utils::Memory::IsBadReadPtr(vtPtr))
return "bad_read_vtable";
DWORD64 vtableStructAddr = reinterpret_cast<DWORD64>(vtPtr);
if (auto it = vtableAddressCache.find(vtableStructAddr); it != vtableAddressCache.end())
return it->second;
// Get the end of the section (in our case the end of the .rdata section)
const DWORD64 end = start + size;
DWORD64 objectLocatorAddr = *reinterpret_cast<DWORD64*>(vtableStructAddr - sizeof(DWORD64));
while (start && start < end) {
const auto scanner = memscan::mapped_region_t(start, end);
auto patternFind = scanner.find_pattern<ms_uptr_t>(patt);
DWORD64 baseOffset = *reinterpret_cast<DWORD64*>(objectLocatorAddr + 0x14);
DWORD64 baseAddr = objectLocatorAddr - baseOffset;
// 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 (!patternFind.has_value())
break;
DWORD classHierarchyDescriptorOffset = *reinterpret_cast<DWORD*>(objectLocatorAddr + 0x10);
DWORD64 classHierarchyDescriptorAddr = baseAddr + classHierarchyDescriptorOffset;
// 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(patternFind.value());
start = patternFind.value() + 8;
}
int baseClassCount = *reinterpret_cast<int*>(classHierarchyDescriptorAddr + 0x8);
if (baseClassCount == 0 || baseClassCount > 24)
return {};
return xrefs;
DWORD baseClassArrayOffset = *reinterpret_cast<DWORD*>(classHierarchyDescriptorAddr + 0xC);
DWORD64 baseClassArrayAddr = baseAddr + baseClassArrayOffset;
DWORD baseClassDescriptorOffset = *reinterpret_cast<DWORD*>(baseClassArrayAddr);
DWORD64 baseClassDescriptorAddr = baseAddr + baseClassDescriptorOffset;
DWORD typeDescriptorOffset = *reinterpret_cast<DWORD*>(baseClassDescriptorAddr);
DWORD64 typeDescriptorAddr = baseAddr + typeDescriptorOffset;
std::string decoratedClassName = "?" + std::string(reinterpret_cast<const char*>(typeDescriptorAddr + 0x14));
char outUndecoratedClassName[255]{};
UnDecorateSymbolName(decoratedClassName.c_str(), outUndecoratedClassName, sizeof(outUndecoratedClassName), UNDNAME_NAME_ONLY);
vtableAddressCache[vtableStructAddr] = outUndecoratedClassName;
return outUndecoratedClassName;
}
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;
static std::string _GetVTableName(void* classPtr) {
if (Utils::Memory::IsBadReadPtr(classPtr))
return "bad_read_class";
return _GetVTableNameFromVTPtr(*reinterpret_cast<DWORD64**>(classPtr));
}
DWORD64 GetVTablePtr(const char* moduleName, const char* tableName) {
if (!moduleName || !tableName)
return 0;
std::string GetVTableNameFromVTPtr(void* vtPtr) {
std::string result = _GetVTableNameFromVTPtr(vtPtr);
if (result.empty())
vtableAddressCache.try_emplace(*reinterpret_cast<DWORD64*>(vtPtr), "");
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 = Memory::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 = Memory::BytesToIDAPattern(reinterpret_cast<BYTE*>(const_cast<DWORD64*>(&objectLocator)), 8);
const DWORD64 vtableAddr = reinterpret_cast<DWORD64>(Utils::SigScan::PatternScanner::FindPattern(reinterpret_cast<void*>(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)
continue;
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<void*>(textStart), textSize, { idaPattern.c_str(), Utils::SigScan::PatternType::Address })) + 0x8;*/
}
}
// We for some odd reason didn't find any valid xrefs
return 0;
return result;
}
std::string GetVTableName(void* classPtr) {
std::string result = _GetVTableName(classPtr);
if (result.empty())
vtableAddressCache.try_emplace(*reinterpret_cast<DWORD64*>(classPtr), "");
return result;
}
bool IsClassVTableNameEqualTo(void* classPtr, std::string_view tableName) { return GetVTableName(classPtr) == tableName; }
bool IsVTableNameEqualTo(void* vtPtr, std::string_view tableName) { return GetVTableNameFromVTPtr(vtPtr) == tableName; }
}
}

View File

@ -30,5 +30,20 @@ namespace EGSDK::Utils {
str.replace(start_pos, from.length(), to);
return true;
}
std::string GetSimpleTypeName(std::string fullName) {
if (fullName.compare(0, 6, "class ") == 0)
fullName.erase(0, 6);
else if (fullName.compare(0, 7, "struct ") == 0)
fullName.erase(0, 7);
else if (fullName.compare(0, 5, "enum ") == 0)
fullName.erase(0, 5);
size_t pos = fullName.find_last_of("::");
if (pos != std::string::npos)
fullName.erase(0, pos + 1);
return fullName;
}
}
}

View File

@ -5,8 +5,7 @@
namespace EGT::ImGui_impl {
namespace Win32 {
extern void EnableMouseHook();
extern void DisableMouseHook();
extern void ToggleMouseHook(bool value);
extern void Init(HWND hwnd);
}

View File

@ -4,6 +4,7 @@
namespace EGT::Menu {
namespace Debug {
extern bool disableLowLevelMouseHook;
extern bool disableVftableScanning;
class Tab : MenuTab {
public:

View File

@ -11,6 +11,7 @@
#include <EGT\Menu\Teleport.h>
#include <EGT\Menu\Weapon.h>
#include <EGT\Menu\World.h>
#include <EGT\Menu\Debug.h>
#include <EGT\Config\Config.h>
namespace EGT::Config {
@ -258,7 +259,13 @@ namespace EGT::Config {
{ "Misc:GameChecks", "DisableDataPAKsCRCCheck", true, &Menu::Misc::disableDataPAKsCRCCheck, OPTION },
{ "Misc:GameChecks", "IncreaseDataPAKsLimit", true, &Menu::Misc::increaseDataPAKsLimit, OPTION },
{ "World:Time", "SlowMotionSpeed", 0.4f, &Menu::World::slowMotionSpeed, Float },
{ "World:Time", "SlowMotionTransitionTime", 1.0f, &Menu::World::slowMotionTransitionTime, Float }
{ "World:Time", "SlowMotionTransitionTime", 1.0f, &Menu::World::slowMotionTransitionTime, Float },
#ifdef _DEBUG
{ "Debug:Misc", "DisableLowLevelMouseHook", true, &Menu::Debug::disableLowLevelMouseHook, OPTION },
#else
{ "Debug:Misc", "DisableLowLevelMouseHook", false, &Menu::Debug::disableLowLevelMouseHook, OPTION },
#endif
{ "Debug:Misc", "DisableVftableScanning", false, &Menu::Debug::disableVftableScanning, OPTION },
});
std::vector<ConfigEntry> configVariables(configVariablesDefault.begin(), configVariablesDefault.end());
static constexpr const char* configFileName = "EGameTools.ini";

View File

@ -15,6 +15,7 @@
#include <EGT\Engine\Engine_Hooks.h>
#include <EGT\Menu\Menu.h>
#include <EGT\Menu\Misc.h>
#include <EGT\Menu\Debug.h>
#include <DbgHelp.h>
#include <thread>
#include <semaphore>
@ -292,6 +293,9 @@ namespace EGT::Core {
Config::InitConfig();
threads.emplace_back(Config::ConfigLoop);
SPDLOG_WARN("Setting vftable scanning to: {}", Menu::Debug::disableVftableScanning);
EGSDK::ClassHelpers::SetIsVftableScanningDisabled(Menu::Debug::disableVftableScanning);
SPDLOG_WARN("Creating symlink for loading files");
CreateSymlinkForLoadingFiles();

View File

@ -130,8 +130,8 @@ namespace EGT::Engine {
struct mount_path {
union {
const char* gamePath;
EGSDK::buffer<0x8, const char*> pakPath;
EGSDK::buffer<0x10, const char*> fullPakPath;
EGSDK::ClassHelpers::buffer<0x8, const char*> pakPath;
EGSDK::ClassHelpers::buffer<0x10, const char*> fullPakPath;
};
};

View File

@ -118,7 +118,7 @@ namespace EGT::ImGui_impl {
}
}
void EnableMouseHook() {
static void EnableMouseHook() {
if (oMouseProc)
return;
@ -135,13 +135,14 @@ namespace EGT::ImGui_impl {
MouseHkMsgLoop();
}).detach();
}
void DisableMouseHook() {
static void DisableMouseHook() {
if (!oMouseProc)
return;
UnhookWindowsHookEx(oMouseProc);
oMouseProc = nullptr;
}
void ToggleMouseHook(bool value) { value ? EnableMouseHook() : DisableMouseHook(); }
void Init(HWND hwnd) {
gHwnd = hwnd;

View File

@ -1,4 +1,5 @@
#include <ImGui\imguiex.h>
#include <EGSDK\ClassHelpers.h>
#include <EGT\ImGui_impl\Win32_impl.h>
#include <EGT\Menu\Debug.h>
@ -66,6 +67,7 @@ namespace EGT::Menu {
#else
bool disableLowLevelMouseHook = false;
#endif
bool disableVftableScanning = false;
static void RenderClassAddrPair(const std::pair<std::string_view, void*(*)()>* pair) {
const float maxInputTextWidth = ImGui::CalcTextSize("0x0000000000000000").x;
@ -97,8 +99,10 @@ namespace EGT::Menu {
void Tab::Update() {}
void Tab::Render() {
ImGui::SeparatorText("Misc##Debug");
if (ImGui::Checkbox("Disable Low Level Mouse Hook", &disableLowLevelMouseHook, "Disables the low level mouse hook that is used to capture mouse input in the game"))
disableLowLevelMouseHook ? ImGui_impl::Win32::DisableMouseHook() : ImGui_impl::Win32::EnableMouseHook();
if (ImGui::Checkbox("Disable Low Level Mouse Hook", &disableLowLevelMouseHook, "Disables the low level mouse hook that is used to capture mouse input in the game; this option is used for debugging purposes"))
ImGui_impl::Win32::ToggleMouseHook(disableLowLevelMouseHook);
if (ImGui::Checkbox("Disable Vftable Scanning", &disableVftableScanning, "Disables the vftable scanning for classes that are used in the game and used to validate a class in memory; this option is used for debugging purposes"))
EGSDK::ClassHelpers::SetIsVftableScanningDisabled(disableVftableScanning);
ImGui::SeparatorText("Class addresses##Debug");
if (ImGui::CollapsingHeader("GamePH", ImGuiTreeNodeFlags_None)) {
ImGui::Indent();