merge dev into master

This commit is contained in:
EricPlayZ
2024-11-03 21:59:24 +02:00
36 changed files with 7123 additions and 5008 deletions

View File

@ -45,7 +45,6 @@
<ClCompile Include="source\game\GamePH\PlayerDI_PH.cpp" />
<ClCompile Include="source\game\GamePH\PlayerHealthModule.cpp" />
<ClCompile Include="source\game\GamePH\PlayerInfectionModule.cpp" />
<ClCompile Include="source\game\GamePH\PlayerObjProperties.cpp" />
<ClCompile Include="source\game\GamePH\PlayerState.cpp" />
<ClCompile Include="source\game\GamePH\PlayerVariables.cpp" />
<ClCompile Include="source\game\GamePH\SessionCooperativeDI.cpp" />
@ -92,6 +91,14 @@
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="source\kiero.cpp" />
<ClCompile Include="source\memscan\memscan.c">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="source\memscan\util\util.c">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="source\menu\camera.cpp" />
<ClCompile Include="source\menu\debug.cpp" />
<ClCompile Include="source\menu\init.cpp" />
@ -150,7 +157,6 @@
<ClInclude Include="source\game\GamePH\PlayerDI_PH.h" />
<ClInclude Include="source\game\GamePH\PlayerHealthModule.h" />
<ClInclude Include="source\game\GamePH\PlayerInfectionModule.h" />
<ClInclude Include="source\game\GamePH\PlayerObjProperties.h" />
<ClInclude Include="source\game\GamePH\PlayerState.h" />
<ClInclude Include="source\game\GamePH\PlayerVariables.h" />
<ClInclude Include="source\game\GamePH\SessionCooperativeDI.h" />
@ -177,6 +183,8 @@
<ClInclude Include="source\ImGui\imstb_truetype.h" />
<ClInclude Include="source\ImGui\misc\freetype\imgui_freetype.h" />
<ClInclude Include="source\kiero.h" />
<ClInclude Include="source\memscan\memscan.h" />
<ClInclude Include="source\memscan\util\util.h" />
<ClInclude Include="source\menu\camera.h" />
<ClInclude Include="source\menu\debug.h" />
<ClInclude Include="source\menu\init.h" />
@ -269,7 +277,7 @@
</Optimization>
<IntrinsicFunctions>
</IntrinsicFunctions>
<AdditionalIncludeDirectories>pch;source\spdlog\include;source\ImGui\freetype\include;source\ImGui;source\MinHook;</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>pch;source\spdlog\include;source\ImGui\freetype\include;source\ImGui;source\MinHook;source\memscan;</AdditionalIncludeDirectories>
<PrecompiledHeaderOutputFile>$(IntDir)$(TargetName)_$(PlatformTarget).pch</PrecompiledHeaderOutputFile>
<DebugInformationFormat>OldStyle</DebugInformationFormat>
<MultiProcessorCompilation>
@ -284,7 +292,7 @@
</OptimizeReferences>
<EnableCOMDATFolding>
</EnableCOMDATFolding>
<AdditionalLibraryDirectories>source\spdlog\lib;source\ImGui\freetype\lib;source\MinHook\lib;source\CrashRpt\lib;</AdditionalLibraryDirectories>
<AdditionalLibraryDirectories>source\spdlog\lib;source\ImGui\freetype\lib;source\MinHook\lib;</AdditionalLibraryDirectories>
<AssemblyDebug>true</AssemblyDebug>
<ImportLibrary />
<FixedBaseAddress>
@ -306,7 +314,7 @@
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<LanguageStandard>stdcpplatest</LanguageStandard>
<AdditionalIncludeDirectories>pch;source\spdlog\include;source\ImGui\freetype\include;source\ImGui;source\MinHook;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>pch;source\spdlog\include;source\ImGui\freetype\include;source\ImGui;source\MinHook;source\memscan;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<DebugInformationFormat>OldStyle</DebugInformationFormat>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
<PrecompiledHeaderOutputFile>$(IntDir)$(TargetName)_$(PlatformTarget).pch</PrecompiledHeaderOutputFile>

View File

@ -209,6 +209,12 @@
<ClCompile Include="source\menu\teleport.cpp">
<Filter>menu</Filter>
</ClCompile>
<ClCompile Include="source\memscan\memscan.c">
<Filter>memscan</Filter>
</ClCompile>
<ClCompile Include="source\memscan\util\util.c">
<Filter>memscan\util</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="source\kiero.h" />
@ -447,6 +453,12 @@
<ClInclude Include="source\menu\teleport.h">
<Filter>menu</Filter>
</ClInclude>
<ClInclude Include="source\memscan\memscan.h">
<Filter>memscan</Filter>
</ClInclude>
<ClInclude Include="source\memscan\util\util.h">
<Filter>memscan\util</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Filter Include="MinHook">
@ -491,5 +503,11 @@
<Filter Include="ImGui\misc\freetype">
<UniqueIdentifier>{122420d5-ac60-4778-80bf-1384697fcc44}</UniqueIdentifier>
</Filter>
<Filter Include="memscan">
<UniqueIdentifier>{1e82ab1f-550e-4fac-8fac-0888e1e62873}</UniqueIdentifier>
</Filter>
<Filter Include="memscan\util">
<UniqueIdentifier>{7e81ee9f-f74a-4af8-8a10-7530b26c3c86}</UniqueIdentifier>
</Filter>
</ItemGroup>
</Project>

View File

@ -37,6 +37,8 @@
#include <assert.h>
#include <unordered_map>
#include <unordered_set>
#include <semaphore>
#include <atomic>
#include <dxgi.h>
#include <dxgi1_4.h>
@ -65,6 +67,7 @@
#include <imstb_textedit.h>
#include <imstb_truetype.h>
#include <MinHook.h>
#include <memscan.h>
#include "..\source\config\ini.h"
#include "..\source\game\Engine\GameSpeedHandler.h"

View File

@ -145,6 +145,11 @@ I have some things planned for the next updates, but time will decide when I'll
R"(- Added compatibility with v1.18.0 "Community QoL" update
- Fixed an issue with classes not always getting detected correctly for everyone (Debug tab; if you still run into issues, please open a bug report!))" },
{ "v1.2.3",
R"(- Fixed co-op issues related to Player and Weapon options, such as God Mode, Player Health, Player Immunity, Weapon Durability sliders not working (if you still run into issues, please open a bug report!))" }
R"(- Fixed co-op issues related to Player and Weapon options, such as God Mode, Player Health, Player Immunity, Weapon Durability sliders not working (if you still run into issues, please open a bug report!))" },
{ "v1.2.4",
R"(- Added compatibility with v1.19 Tower Raid: Halloween Run update
- Improved CPU performance at game startup when using the mod, this should stop the system from freezing for some people when starting up the game
- Improved memory signature scanning, increasing reliability and performance; if you encounter issues with classses in the Debug menu being NULL, please open up a bug report!
- Improved MountDataPaks hook error detection (the error related to MountDataPaks should not show up in the console as often anymore))" }
};
}

File diff suppressed because it is too large Load Diff

View File

@ -1,12 +1,14 @@
#include <pch.h>
#include "config\config.h"
#include "core.h"
#include "game\Engine\engine_hooks.h"
#include "game\GamePH\LevelDI.h"
#include "game\GamePH\PlayerHealthModule.h"
#include "game\GamePH\PlayerInfectionModule.h"
#include "game\GamePH\PlayerVariables.h"
#include "game\GamePH\gameph_misc.h"
#include "menu\menu.h"
#include "menu\misc.h"
#pragma region KeyBindOption
bool KeyBindOption::wasAnyKeyPressed = false;
@ -38,11 +40,15 @@ namespace Core {
#pragma endregion
// Core
bool exiting = false;
std::atomic<bool> exiting = false;
static std::vector<std::thread> threads{};
static HANDLE keepAliveEvent{};
int rendererAPI = 0;
DWORD gameVer = 0;
std::counting_semaphore<4> maxHookThreads(4);
static void LoopHookRenderer() {
while (true) {
if (exiting)
@ -170,7 +176,14 @@ namespace Core {
GamePH::PlayerHealthModule::UpdateClassAddr();
GamePH::PlayerInfectionModule::UpdateClassAddr();
static bool mountDataPaksErrorShown = false;
if (!mountDataPaksErrorShown && Engine::Hooks::mountDataPaksRanWith8Count < 3 && Menu::Misc::increaseDataPAKsLimit.GetValue() && GamePH::PlayerVariables::Get()) {
spdlog::error("MountDataPaks hook ran less than 3 times with the data PAKs limit set to 8. This means the increased data PAKs limit might not work correctly! If this error message appears and your data PAKs past \"data7.pak\" have not loaded, please contact author.");
mountDataPaksErrorShown = true;
}
}
#ifndef EXCP_HANDLER_DISABLE_DEBUG
static bool WriteMiniDump(PEXCEPTION_POINTERS pExceptionPointers) {
HANDLE hFile = CreateFileA("EGameTools-dump.dmp", GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
if (hFile == INVALID_HANDLE_VALUE)
@ -193,8 +206,7 @@ namespace Core {
if (WriteMiniDump(ExceptionInfo)) {
spdlog::info("Mini-dump written to \"EGameTools-dump.dmp\". Please send this to mod author for further help!");
errorMsg = "EGameTools encountered a fatal error that caused the game to crash.\n\nA file \"" + Utils::Files::GetCurrentProcDirectory() + "\\EGameTools-dump.dmp\" has been generated. Please send this file to the author of the mod!\n\nThe game will now close once you press OK.";
}
else {
} else {
spdlog::error("Failed to write mini-dump.");
errorMsg = "EGameTools encountered a fatal error that caused the game to crash.\n\nEGameTools failed to generate a crash dump file unfortunately, which means it is harder to find the cause of the crash.\n\nThe game will now close once you press OK.";
}
@ -202,6 +214,7 @@ namespace Core {
MessageBoxA(nullptr, errorMsg.c_str(), "Fatal game error", MB_ICONERROR | MB_OK | MB_SETFOREGROUND);
exit(0);
}
#endif
static void GameVersionCheck() {
try {
gameVer = GamePH::GetCurrentGameVersion();
@ -220,39 +233,61 @@ namespace Core {
EnableConsole();
InitLogger();
#ifndef EXCP_HANDLER_DISABLE_DEBUG
SetUnhandledExceptionFilter(CrashHandler);
#endif
spdlog::warn("Getting game version");
GameVersionCheck();
spdlog::warn("Initializing config");
Config::InitConfig();
std::thread(Config::ConfigLoop).detach();
threads.emplace_back(Config::ConfigLoop);
CreateSymlinkForLoadingFiles();
for (auto& hook : *Utils::Hook::HookBase::GetInstances()) {
spdlog::warn("Hooking \"{}\"", hook->name.data());
std::thread([&hook]() {
if (hook->HookLoop())
threads.emplace_back([&hook]() {
maxHookThreads.acquire();
if (hook->isHooking) {
spdlog::warn("Hooking \"{}\"", hook->name.data());
while (hook->isHooking)
Sleep(10);
if (hook->isHooked)
spdlog::info("Hooked \"{}\"!", hook->name.data());
} else if (hook->isHooked)
spdlog::info("Hooked \"{}\"!", hook->name.data());
else {
spdlog::warn("Hooking \"{}\"", hook->name.data());
if (hook->HookLoop())
spdlog::info("Hooked \"{}\"!", hook->name.data());
}
maxHookThreads.release();
}).detach();
}
spdlog::warn("Sorting Player Variables");
std::thread([]() {
threads.emplace_back([]() {
GamePH::PlayerVariables::SortPlayerVars();
spdlog::info("Player Variables sorted");
}).detach();
spdlog::warn("Hooking DX11/DX12 renderer");
std::thread([]() {
threads.emplace_back([]() {
LoopHookRenderer();
spdlog::info("Hooked \"DX11/DX12 renderer\"!");
}).detach();
const HANDLE proc = GetCurrentProcess();
WaitForSingleObject(proc, INFINITE);
keepAliveEvent = CreateEventA(nullptr, TRUE, FALSE, nullptr);
WaitForSingleObject(keepAliveEvent, INFINITE);
for (auto& thread : threads) {
if (thread.joinable())
thread.join();
}
return TRUE;
}
@ -269,5 +304,7 @@ namespace Core {
MH_DisableHook(MH_ALL_HOOKS);
MH_Uninitialize();
spdlog::info("Unhooked everything");
SetEvent(keepAliveEvent);
}
}

View File

@ -3,12 +3,14 @@
#include <imgui.h>
#include <ranges>
#include <set>
#include <atomic>
#include "..\utils\values.h"
#ifndef VK_NONE
#ifdef _DEBUG
#define LLMH_IMPL_DISABLE_DEBUG // this is for disabling low-level mouse hook in case ure trying to debug and u dont want ur pc to die lol
#define EXCP_HANDLER_DISABLE_DEBUG // this is for disabling exception handling in case ure trying to debug and u dont want ur debugger to keep crying about errors lol
#endif
#define VK_NONE -1
@ -16,9 +18,9 @@
#define VK_MWHEELUP 0x101
#endif
constexpr const char* MOD_VERSION_STR = "v1.2.2";
constexpr DWORD MOD_VERSION = 10202;
constexpr DWORD GAME_VER_COMPAT = 11800;
constexpr const char* MOD_VERSION_STR = "v1.2.4";
constexpr DWORD MOD_VERSION = 10204;
constexpr DWORD GAME_VER_COMPAT = 11900;
struct Key {
constexpr Key(std::string_view name, int code, ImGuiKey imGuiCode) : name(name), code(code), imGuiCode(imGuiCode) {}
@ -224,7 +226,7 @@ private:
};
namespace Core {
extern bool exiting;
extern std::atomic<bool> exiting;
extern int rendererAPI;
extern DWORD gameVer;

View File

@ -3,6 +3,8 @@
namespace Core {
extern void DisableConsole();
extern std::counting_semaphore<4> maxHookThreads;
extern DWORD64 WINAPI MainThread(HMODULE hModule);
extern void Cleanup();
}
@ -22,6 +24,7 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD64 ul_reason_for_call, LPVOID lpRese
switch (ul_reason_for_call) {
case DLL_PROCESS_ATTACH: {
MH_Initialize();
Engine::Hooks::MountDataPaksHook.HookLoop();
Engine::Hooks::AuthenticateDataAddNewFileHook.HookLoop();
Engine::Hooks::FsCheckZipCrcHook.HookLoop();

View File

@ -6,9 +6,9 @@ namespace Engine {
class CBulletPhysicsCharacter {
public:
union {
buffer<0x8A0, Vector3> playerPos2;
buffer<0x8B8, Vector3> playerPos;
buffer<0xC38, float> playerDownwardVelocity;
buffer<0xA08, Vector3> playerPos2;
buffer<0xA20, Vector3> playerPos;
buffer<0xDA0, float> playerDownwardVelocity;
};
static Vector3 posBeforeFreeze;

View File

@ -1,15 +1,15 @@
#include <pch.h>
#include "..\GamePH\PlayerObjProperties.h"
#include "..\GamePH\PlayerDI_PH.h"
#include "CoPhysicsProperty.h"
namespace Engine {
CoPhysicsProperty* CoPhysicsProperty::Get() {
__try {
GamePH::PlayerObjProperties* pPlayerObjProperties = GamePH::PlayerObjProperties::Get();
if (!pPlayerObjProperties)
GamePH::PlayerDI_PH* pPlayerDI_PH = GamePH::PlayerDI_PH::Get();
if (!pPlayerDI_PH)
return nullptr;
CoPhysicsProperty* ptr = pPlayerObjProperties->pCoPhysicsProperty;
CoPhysicsProperty* ptr = pPlayerDI_PH->pCoPhysicsProperty;
if (!Utils::Memory::IsValidPtrMod(ptr, "engine_x64_rwdi.dll"))
return nullptr;

View File

@ -201,18 +201,15 @@ namespace Engine {
#pragma endregion
#pragma region MountDataPaks
int mountDataPaksRanWith8Count = 0;
static DWORD64 detourMountDataPaks(DWORD64 a1, UINT a2, UINT a3, DWORD64* a4, DWORD64(*a5)(DWORD64, DWORD, DWORD64, char*, int), INT16 a6, DWORD64 a7, UINT a8);
Utils::Hook::MHook<LPVOID, DWORD64(*)(DWORD64, UINT, UINT, DWORD64*, DWORD64(*)(DWORD64, DWORD, DWORD64, char*, int), INT16, DWORD64, UINT)> MountDataPaksHook{ "MountDataPaks", &Offsets::Get_MountDataPaks, &detourMountDataPaks };
static DWORD64 detourMountDataPaks(DWORD64 a1, UINT a2, UINT a3, DWORD64* a4, DWORD64(*a5)(DWORD64, DWORD, DWORD64, char*, int), INT16 a6, DWORD64 a7, UINT a8) {
if (Menu::Misc::increaseDataPAKsLimit.GetValue()) {
static int i = 0;
if (a8 == 8)
i++;
else if (i < 3) {
i++;
spdlog::error("MountDataPaks hook ran less than 3 times with the data PAKs limit set to 8. This means the increased data PAKs limit might not work correctly! If this error message appears and your data PAKs past \"data7.pak\" have not loaded, please contact author.");
}
mountDataPaksRanWith8Count++;
a8 = 200;
}

View File

@ -7,5 +7,6 @@ namespace Engine {
namespace Hooks {
extern bool switchedFreeCamByGamePause;
extern Vector3 freeCamPosBeforeGamePause;
extern int mountDataPaksRanWith8Count;
}
}

View File

@ -9,7 +9,7 @@ namespace GamePH {
public:
union {
buffer<0x130, SessionCooperativeDI*> pSessionCooperativeDI;
buffer<0x880, bool> blockPauseGameOnPlayerAfk;
buffer<0x908, bool> blockPauseGameOnPlayerAfk;
};
float GetGameTimeDelta();

View File

@ -2,7 +2,7 @@
#include "..\Engine\CLevel.h"
#include "..\offsets.h"
#include "LevelDI.h"
#include "PlayerObjProperties.h"
#include "PlayerDI_PH.h"
namespace GamePH {
bool LevelDI::IsLoading() {
@ -20,7 +20,7 @@ namespace GamePH {
static Utils::Time::Timer loadTimer{ 7500 };
static bool isStillLoading = false;
if (IsLoading() || !GamePH::PlayerObjProperties::Get()) {
if (IsLoading() || !GamePH::PlayerDI_PH::Get()) {
isStillLoading = true;
return false;
}

View File

@ -3,12 +3,17 @@
#include "InventoryItem.h"
#include "InventoryContainerDI.h"
namespace Engine {
class CoPhysicsProperty;
}
namespace GamePH {
class PlayerDI_PH {
public:
union {
buffer<0x35E1, bool> enableTPPModel1;
buffer<0x35E2, bool> enableTPPModel2;
buffer<0xF0, Engine::CoPhysicsProperty*> pCoPhysicsProperty;
buffer<0x35E9, bool> enableTPPModel1;
buffer<0x35EA, bool> enableTPPModel2;
};
static PlayerDI_PH* Get();

View File

@ -1,20 +0,0 @@
#include <pch.h>
#include "..\offsets.h"
#include "PlayerObjProperties.h"
namespace GamePH {
PlayerObjProperties* PlayerObjProperties::Get() {
__try {
if (!Offsets::Get_g_PlayerObjProperties())
return nullptr;
PlayerObjProperties* ptr = *reinterpret_cast<PlayerObjProperties**>(Offsets::Get_g_PlayerObjProperties());
if (!Utils::Memory::IsValidPtrMod(ptr, "gamedll_ph_x64_rwdi.dll"))
return nullptr;
return ptr;
} __except (EXCEPTION_EXECUTE_HANDLER) {
return nullptr;
}
}
}

View File

@ -1,19 +0,0 @@
#pragma once
#include "..\buffer.h"
namespace Engine {
class CoPhysicsProperty;
}
namespace GamePH {
class PlayerObjProperties {
public:
union {
buffer<0xF0, Engine::CoPhysicsProperty*> pCoPhysicsProperty;
//buffer<0x2E80, bool> isOutOfBounds;
//buffer<0x2E84, float> outOfBoundsTimer;
};
static PlayerObjProperties* Get();
};
}

View File

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

View File

@ -0,0 +1,935 @@
#include "memscan.h"
#include <memory.h> // memcmp
#include <string.h> // strlen
/* Types */
typedef struct {
/* example: {0x55, 0x8B, 0xEC} */
const ms_ubyte_t* m_data;
/* the former will result in '3' */
ms_usize_t m_size;
/* the n-th instance of a pattern within range, 0 - first */
ms_usize_t m_match;
} ms_bytes_t;
typedef struct {
/* "55 8B EC" => ms_bytes_t({0x55, 0x8B, 0xEC}, 3, match)
* must be null terminated */
const char* m_data;
/* the n-th instance of a pattern within range, 0 - first */
ms_usize_t m_match;
} ms_bytes_str_t;
typedef struct {
/* example: {0x8B, 0x0D} */
const ms_ubyte_t* m_data;
/* the former will result in '3' */
ms_usize_t m_size;
/* the n-th instance of a match within range, 0 - first */
ms_usize_t m_match;
ms_follow_direction_t m_direction;
} ms_find_bytes_t;
typedef struct {
/* "8B 0C" => ms_find_bytes_t({0x8B, 0x0C}, 2, match, direction)
* must be null terminated */
const char* m_data;
/* the n-th instance of a match within range, 0 - first */
ms_usize_t m_match;
ms_follow_direction_t m_direction;
} ms_find_str_t;
typedef struct {
/* numerical form of pointer to look for */
ms_uptr_t m_pointer;
/* the n-th instance of a match within range, 0 - first */
ms_usize_t m_match;
bool m_swap_endianness;
} ms_xref_t;
typedef struct {
/* the string to look for */
const char* m_data;
/* string size */
ms_usize_t m_size;
/* the n-th instance of the reversed endianness address to the first
* character of the string, 0 - first */
ms_usize_t m_match;
} ms_string_xref_t;
typedef enum {
/* won't be reached if MEMSCAN_UNSAFE_OPTIMIZATIONS are on */
MS_FOLLOW_STATUS_NO_VALID_FOLLOW_INFO = 0,
/* */
/* overbounds on start */
MS_FOLLOW_STATUS_FAIL_LHS,
/* overbounds on end */
MS_FOLLOW_STATUS_FAIL_RHS,
/* broke before reaching match */
MS_FOLLOW_STATUS_INCOMPLETE,
/* follow has succeeded, state was set to OK */
MS_FOLLOW_STATUS_OK
} ms_follow_status_t;
typedef struct {
ms_uptr_t m_address;
ms_follow_status_t m_status;
} ms_follow_t;
/* Methods */
/* Static */
static ms_follow_t
follow_until(const ms_uptr_t copy, const ms_uptr_t start, const ms_uptr_t end,
const ms_find_bytes_t* restrict find_bytes)
{
/* data */
ms_follow_t result = {0};
ms_uptr_t address = copy;
#if !MEMSCAN_UNSAFE_OPTIMIZATIONS
if (find_bytes == NULL ||
(find_bytes != NULL &&
(find_bytes->m_data == NULL || find_bytes->m_size == 0))) {
result.m_address = 0;
result.m_status = MS_FOLLOW_STATUS_NO_VALID_FOLLOW_INFO;
goto return_label;
}
#endif
/* the following will run at least once, so you can follow regardless of
* current address. it will break upon a NULL address, or after the first
* overbounds byte, or upon a match in memory. */
ms_usize_t match = MEMSCAN_FIRST_MATCH;
int status;
retry_label:
status = memcmp((void*)address, find_bytes->m_data, find_bytes->m_size);
while ((void*)address != NULL && address >= (start - 1) &&
address <= (end + 1) && status != 0) {
address +=
find_bytes->m_direction == MS_FOLLOW_DIRECTION_BACKWARDS ? -1 : 1;
status = memcmp((void*)address, find_bytes->m_data, find_bytes->m_size);
}
result.m_address = address;
/* if the address is smaller than start, then it is overbounds on the lhs,
* otherwise, if it is smaller than end, it is overbounds no the rhs,
* otherwise, if status is != 0, it is OK, otherwise, it is an incomplete
* follow */
result.m_status = (result.m_address < start
? MS_FOLLOW_STATUS_FAIL_LHS
: (result.m_address > end
? MS_FOLLOW_STATUS_FAIL_RHS
: (status != 0 ? MS_FOLLOW_STATUS_INCOMPLETE
: MS_FOLLOW_STATUS_OK)));
/* verify match */
if (match != find_bytes->m_match &&
result.m_status == MS_FOLLOW_STATUS_OK) {
++match;
address +=
find_bytes->m_direction == MS_FOLLOW_DIRECTION_BACKWARDS ? -1 : 1;
goto retry_label;
}
return_label:
return result;
}
/* */
static ms_result_t
memscan_find_pattern_bb_impl(const ms_uptr_t start, const ms_uptr_t end,
const ms_bytes_t* restrict bytes,
const ms_find_bytes_t* restrict find_bytes)
{
ms_result_t result = {0};
result.m_address = 0;
#if !MEMSCAN_UNSAFE_OPTIMIZATIONS
if (bytes == NULL || (bytes && bytes->m_data == NULL)) {
result.m_status = MS_RESULT_NO_VALID_BYTES_INFO;
goto return_label;
}
#endif
/* data */
/* this value is meant to be used to deduce how many actual bytes were input
* to the formation, scaling to every platform's sizeof(unsigned char) */
const ms_usize_t bytes_len = bytes->m_size / sizeof *bytes->m_data;
/* here, bytes->m_size is used because we're doing pointer arithmetic */
const ms_ubyte_t* finish = (ms_ubyte_t*)(end - bytes->m_size);
ms_usize_t match = MEMSCAN_FIRST_MATCH;
#if !MEMSCAN_UNSAFE_OPTIMIZATIONS
if (start >= (ms_uptr_t)finish) {
result.m_status = MS_RESULT_NO_VALID_SPAN;
goto return_label;
}
#endif
/* loop over every byte from start to end */
for (ms_ubyte_t* current = (ms_ubyte_t*)start; current < finish;
++current) {
ms_result_status_t status = MS_RESULT_STATUS_FOUND;
/* run a scan from current byte to current byte + bytes_len to verify if
* we have a match */
for (ms_usize_t walker = 0; walker < bytes_len; ++walker) {
/* if the current byte at walker indice isn't equal to the current
* signature byte if it isn't our wildcard */
if (current[walker] != bytes->m_data[walker] &&
bytes->m_data[walker] != k_memscan_wildcard) {
/* declare the state as not found for the current iteration
* and go to the next one */
status = MS_RESULT_STATUS_NOT_FOUND;
break;
}
}
/* if the state wasn't altered (as in, the checks have passed and the
* current byte matches our signature) */
if (status == MS_RESULT_STATUS_FOUND) {
/* verify n-th match */
if (match != bytes->m_match) {
++match;
continue;
}
/* pass our address and status to result */
result.m_address = (ms_uptr_t)current;
result.m_status = status;
/* attempt a follow */
const ms_follow_t follow =
follow_until(result.m_address, start, end, find_bytes);
switch (follow.m_status) {
case MS_FOLLOW_STATUS_OK: {
/* change the result address to the follow result,
* started from the original one */
result.m_address = follow.m_address;
} break;
case MS_FOLLOW_STATUS_FAIL_LHS:
case MS_FOLLOW_STATUS_FAIL_RHS: {
/* this is prioritized over MS_FOLLOW_STATUS_INCOMPLETE */
/* deduce if it was either a left side overbounds
* failure or right side overbounds failure for the
* possibility of still allowing a result depending on
* user response */
result.m_status =
follow.m_status == MS_FOLLOW_STATUS_FAIL_LHS
? MS_RESULT_STATUS_FOUND_FOLLOW_FAIL_LHS
: MS_RESULT_STATUS_FOUND_FOLLOW_FAIL_RHS;
} break;
case MS_FOLLOW_STATUS_INCOMPLETE: {
/* a condition to break the follow was met before there was
* a match in memory between the seeked byte-code and the
* byte-code at from it's current address for the seeked
* byte-code length */
result.m_status =
MS_RESULT_STATUS_FOUND_FOLLOW_FAIL_INCOMPLETE;
} break;
}
goto return_label;
}
}
/* we haven't found an address for our signature, so we will set result
* state to NOT FOUND */
result.m_status = MS_RESULT_STATUS_NOT_FOUND;
return_label:
return result;
}
/* */
static ms_result_t
memscan_find_pattern_bs_impl(const ms_uptr_t start, const ms_uptr_t end,
const ms_bytes_t* restrict bytes,
const ms_find_str_t* restrict find_bytes)
{
ms_result_t result = {0};
result.m_address = 0;
if (find_bytes == NULL)
result = memscan_find_pattern_bb_impl(start, end, bytes, NULL);
else {
/* data */
ms_pattern_t pattern =
util_build_pattern(find_bytes->m_data, strlen(find_bytes->m_data));
ms_find_bytes_t find_bytes_from_str = {0};
find_bytes_from_str.m_data = pattern.m_data;
find_bytes_from_str.m_size = pattern.m_size;
find_bytes_from_str.m_match = find_bytes->m_match;
find_bytes_from_str.m_direction = find_bytes->m_direction;
/* pass converted data to base implementation */
result = memscan_find_pattern_bb_impl(start, end, bytes,
&find_bytes_from_str);
util_free_pattern(&pattern);
}
return result;
}
/* */
static ms_result_t
memscan_find_pattern_sb_impl(const ms_uptr_t start, const ms_uptr_t end,
const ms_bytes_str_t* restrict bytes,
const ms_find_bytes_t* restrict find_bytes)
{
ms_result_t result = {0};
result.m_address = 0;
if (bytes == NULL || (bytes && bytes->m_data == NULL))
result = memscan_find_pattern_bb_impl(start, end, NULL, find_bytes);
else {
/* data */
ms_pattern_t pattern =
util_build_pattern(bytes->m_data, strlen(bytes->m_data));
ms_bytes_t bytes_from_str = {0};
bytes_from_str.m_data = pattern.m_data;
bytes_from_str.m_size = pattern.m_size;
bytes_from_str.m_match = bytes->m_match;
/* pass converted data to base implementation */
result = memscan_find_pattern_bb_impl(start, end, &bytes_from_str,
find_bytes);
util_free_pattern(&pattern);
}
return result;
}
/* */
static ms_result_t
memscan_find_pattern_ss_impl(const ms_uptr_t start, const ms_uptr_t end,
const ms_bytes_str_t* restrict bytes,
const ms_find_str_t* restrict find_bytes)
{
ms_result_t result = {0};
result.m_address = 0;
if (find_bytes == NULL)
result = memscan_find_pattern_sb_impl(start, end, bytes, NULL);
else {
/* data */
ms_pattern_t find_pattern =
util_build_pattern(find_bytes->m_data, strlen(find_bytes->m_data));
ms_find_bytes_t find_bytes_from_str = {0};
find_bytes_from_str.m_data = find_pattern.m_data;
find_bytes_from_str.m_size = find_pattern.m_size;
find_bytes_from_str.m_match = find_bytes->m_match;
find_bytes_from_str.m_direction = find_bytes->m_direction;
/* pass data to our string, bytes wrapper */
result = memscan_find_pattern_sb_impl(start, end, bytes,
&find_bytes_from_str);
util_free_pattern(&find_pattern);
}
return result;
}
/* */
static ms_result_t
memscan_find_pattern_nfb_impl(const ms_uptr_t start, const ms_uptr_t end,
const ms_bytes_t* restrict bytes)
{
/* pass data to our base implementation. the data being NULL
* will prevent a follow from happening */
return memscan_find_pattern_bb_impl(start, end, bytes, NULL);
}
/* */
static ms_result_t
memscan_find_pattern_nfs_impl(const ms_uptr_t start, const ms_uptr_t end,
const ms_bytes_str_t* restrict bytes)
{
/* pass data to our string, bytes wrapper. the data being NULL
* will prevent a follow from happening */
return memscan_find_pattern_sb_impl(start, end, bytes, NULL);
}
/* */
static ms_result_t
memscan_find_xref_b_impl(const ms_uptr_t start, const ms_uptr_t end,
const ms_xref_t* restrict xref,
const ms_find_bytes_t* restrict find_bytes)
{
ms_result_t result = {0};
result.m_address = 0;
#if !MEMSCAN_UNSAFE_OPTIMIZATIONS
if (xref == NULL /* data is handled in the pattern finder */) {
result.m_status = MS_RESULT_NO_VALID_BYTES_INFO;
goto return_label;
}
#endif
/* data */
ms_bytes_t from_xref = {0};
from_xref.m_data =
util_ptr_to_byteset(xref->m_pointer, xref->m_swap_endianness);
from_xref.m_size = MEMSCAN_BYTESET_SIZE;
from_xref.m_match = xref->m_match;
/* pass data to our no follow, bytes wrapper */
result = memscan_find_pattern_bb_impl(start, end, &from_xref, find_bytes);
return_label:
return result;
}
/* */
static ms_result_t
memscan_find_xref_s_impl(const ms_uptr_t start, const ms_uptr_t end,
const ms_xref_t* restrict xref,
const ms_find_str_t* restrict find_bytes)
{
ms_result_t result = {0};
result.m_address = 0;
if (find_bytes == NULL)
result = memscan_find_xref_b_impl(start, end, xref, NULL);
else {
/* data */
ms_pattern_t pattern =
util_build_pattern(find_bytes->m_data, strlen(find_bytes->m_data));
ms_find_bytes_t find_bytes_from_str = {0};
find_bytes_from_str.m_data = pattern.m_data;
find_bytes_from_str.m_size = pattern.m_size;
find_bytes_from_str.m_match = find_bytes->m_match;
find_bytes_from_str.m_direction = find_bytes->m_direction;
result =
memscan_find_xref_b_impl(start, end, xref, &find_bytes_from_str);
util_free_pattern(&pattern);
}
return result;
}
/* */
static ms_result_t
memscan_find_xref_nf_impl(const ms_uptr_t start, const ms_uptr_t end,
const ms_xref_t* restrict xref)
{
return memscan_find_xref_b_impl(start, end, xref, NULL);
}
/* */
static ms_result_t
memscan_find_string_b_impl(const ms_uptr_t start, const ms_uptr_t end,
const ms_string_xref_t* restrict string,
const ms_find_bytes_t* restrict find_bytes)
{
ms_result_t result = {0};
result.m_address = 0;
#if !MEMSCAN_UNSAFE_OPTIMIZATIONS
if (string == NULL /* data is handled in the pattern finder */) {
result.m_status = MS_RESULT_NO_VALID_BYTES_INFO;
goto return_label;
}
#endif
/* data */
ms_bytes_t bytes = {0};
bytes.m_data = (const ms_ubyte_t*)string->m_data;
bytes.m_size = string->m_size;
bytes.m_match = 0;
ms_result_t string_find = memscan_find_pattern_nfb_impl(start, end, &bytes);
if (string_find.m_status != MS_RESULT_STATUS_FOUND) {
result.m_status = string_find.m_status;
goto return_label;
}
ms_xref_t xref = {0};
xref.m_pointer = string_find.m_address;
xref.m_match = string->m_match;
xref.m_swap_endianness = true;
result = memscan_find_xref_b_impl(start, end, &xref, find_bytes);
return_label:
return result;
}
static ms_result_t
memscan_find_string_s_impl(const ms_uptr_t start, const ms_uptr_t end,
const ms_string_xref_t* restrict string,
const ms_find_str_t* restrict find_bytes)
{
ms_result_t result = {0};
result.m_address = 0;
if (find_bytes == NULL)
result = memscan_find_string_b_impl(start, end, string, NULL);
else {
/* data */
ms_pattern_t pattern =
util_build_pattern(find_bytes->m_data, strlen(find_bytes->m_data));
ms_find_bytes_t find_bytes_from_str = {0};
find_bytes_from_str.m_data = pattern.m_data;
find_bytes_from_str.m_size = pattern.m_size;
find_bytes_from_str.m_match = find_bytes->m_match;
find_bytes_from_str.m_direction = find_bytes->m_direction;
result = memscan_find_string_b_impl(start, end, string,
&find_bytes_from_str);
util_free_pattern(&pattern);
}
return result;
}
static ms_result_t
memscan_find_string_nf_impl(const ms_uptr_t start, const ms_uptr_t end,
const ms_string_xref_t* restrict string)
{
return memscan_find_string_b_impl(start, end, string, NULL);
}
/* Prototype Bodies */
ms_result_t
memscan_find_pattern_bb(const ms_uptr_t start, const ms_uptr_t end,
const ms_ubyte_t* pattern,
const ms_usize_t pattern_size,
const ms_usize_t pattern_nth_match,
const ms_ubyte_t* follow_pattern,
const ms_usize_t follow_pattern_size,
const ms_usize_t follow_nth_match,
const ms_follow_direction_t follow_direction)
{
/* data */
ms_bytes_t bytes = {0};
bytes.m_data = pattern;
bytes.m_size = pattern_size;
bytes.m_match = pattern_nth_match;
ms_find_bytes_t find = {0};
find.m_data = follow_pattern;
find.m_size = follow_pattern_size;
find.m_match = follow_nth_match;
find.m_direction = follow_direction;
/* pass structured data to manager */
return memscan_find_pattern_bb_impl(start, end, &bytes, &find);
}
/* */
ms_result_t
memscan_find_pattern_bs(const ms_uptr_t start, const ms_uptr_t end,
const ms_ubyte_t* pattern,
const ms_usize_t pattern_size,
const ms_usize_t pattern_nth_match,
const char* follow_pattern,
const ms_usize_t follow_nth_match,
const ms_follow_direction_t follow_direction)
{
/* data */
ms_bytes_t bytes = {0};
bytes.m_data = pattern;
bytes.m_size = pattern_size;
bytes.m_match = pattern_nth_match;
ms_find_str_t find = {0};
find.m_data = follow_pattern;
find.m_match = follow_nth_match;
find.m_direction = follow_direction;
/* pass structured data to manager */
return memscan_find_pattern_bs_impl(start, end, &bytes, &find);
}
/* */
ms_result_t
memscan_find_pattern_sb(const ms_uptr_t start, const ms_uptr_t end,
const char* pattern, const ms_usize_t pattern_nth_match,
const ms_ubyte_t* follow_pattern,
const ms_usize_t follow_pattern_size,
const ms_usize_t follow_nth_match,
const ms_follow_direction_t follow_direction)
{
/* data */
ms_bytes_str_t bytes = {0};
bytes.m_data = pattern;
bytes.m_match = pattern_nth_match;
ms_find_bytes_t find = {0};
find.m_data = follow_pattern;
find.m_size = follow_pattern_size;
find.m_match = follow_nth_match;
find.m_direction = follow_direction;
/* pass structured data to manager */
return memscan_find_pattern_sb_impl(start, end, &bytes, &find);
}
/* */
ms_result_t
memscan_find_pattern_ss(const ms_uptr_t start, const ms_uptr_t end,
const char* pattern, const ms_usize_t pattern_nth_match,
const char* follow_pattern,
const ms_usize_t follow_nth_match,
const ms_follow_direction_t follow_direction)
{
/* data */
ms_bytes_str_t bytes = {0};
bytes.m_data = pattern;
bytes.m_match = pattern_nth_match;
ms_find_str_t find = {0};
find.m_data = follow_pattern;
find.m_match = follow_nth_match;
find.m_direction = follow_direction;
/* pass structured data to manager */
return memscan_find_pattern_ss_impl(start, end, &bytes, &find);
}
/* */
ms_result_t
memscan_find_pattern_nfb(const ms_uptr_t start, const ms_uptr_t end,
const ms_ubyte_t* pattern,
const ms_usize_t pattern_size,
const ms_usize_t pattern_nth_match)
{
/* data */
ms_bytes_t bytes = {0};
bytes.m_data = pattern;
bytes.m_size = pattern_size;
bytes.m_match = pattern_nth_match;
/* pass structured data to manager */
return memscan_find_pattern_nfb_impl(start, end, &bytes);
}
/* */
ms_result_t
memscan_find_pattern_nfs(const ms_uptr_t start, const ms_uptr_t end,
const char* pattern,
const ms_usize_t pattern_nth_match)
{
/* data */
ms_bytes_str_t bytes = {0};
bytes.m_data = pattern;
bytes.m_match = pattern_nth_match;
/* pass structured data to manager */
return memscan_find_pattern_nfs_impl(start, end, &bytes);
}
/* */
ms_result_t
memscan_find_xref_b(const ms_uptr_t start, const ms_uptr_t end,
const ms_uptr_t content, const ms_usize_t content_nth_match,
bool swap_endianness, const ms_ubyte_t* follow_pattern,
const ms_usize_t follow_pattern_size,
const ms_usize_t follow_nth_match,
const ms_follow_direction_t follow_direction)
{
/* data */
ms_xref_t xref = {0};
xref.m_pointer = content;
xref.m_match = content_nth_match;
xref.m_swap_endianness = swap_endianness;
ms_find_bytes_t find_bytes = {0};
find_bytes.m_data = follow_pattern;
find_bytes.m_size = follow_pattern_size;
find_bytes.m_match = follow_nth_match;
find_bytes.m_direction = follow_direction;
/* pass structured data to manager */
return memscan_find_xref_b_impl(start, end, &xref, &find_bytes);
}
/* */
ms_result_t
memscan_find_xref_at_b(const ms_uptr_t start, const ms_uptr_t end,
const ms_uptr_t address, const ms_usize_t nth_match,
bool swap_endianness, const ms_ubyte_t* follow_pattern,
const ms_usize_t follow_pattern_size,
const ms_usize_t follow_nth_match,
const ms_follow_direction_t follow_direction)
{
return memscan_find_xref_b(start, end, *(ms_uptr_t*)address, nth_match,
swap_endianness, follow_pattern,
follow_pattern_size, follow_nth_match,
follow_direction);
}
/* */
ms_result_t
memscan_find_xref_s(const ms_uptr_t start, const ms_uptr_t end,
const ms_uptr_t content, const ms_usize_t content_nth_match,
bool swap_endianness, const char* follow_pattern,
const ms_usize_t follow_nth_match,
const ms_follow_direction_t follow_direction)
{
/* data */
ms_xref_t xref = {0};
xref.m_pointer = content;
xref.m_match = content_nth_match;
xref.m_swap_endianness = swap_endianness;
ms_find_str_t find = {0};
find.m_data = follow_pattern;
find.m_match = follow_nth_match;
find.m_direction = follow_direction;
/* pass structured data to manager */
return memscan_find_xref_s_impl(start, end, &xref, &find);
}
/* */
ms_result_t
memscan_find_xref_at_s(const ms_uptr_t start, const ms_uptr_t end,
const ms_uptr_t address, const ms_usize_t nth_match,
bool swap_endianness, const char* follow_pattern,
const ms_usize_t follow_nth_match,
const ms_follow_direction_t follow_direction)
{
return memscan_find_xref_s(start, end, *(ms_uptr_t*)address, nth_match,
swap_endianness, follow_pattern,
follow_nth_match, follow_direction);
}
/* */
ms_result_t
memscan_find_xref_nf(const ms_uptr_t start, const ms_uptr_t end,
const ms_uptr_t content,
const ms_usize_t content_nth_match, bool swap_endianness)
{
/* data */
ms_xref_t xref = {0};
xref.m_pointer = content;
xref.m_match = content_nth_match;
xref.m_swap_endianness = swap_endianness;
/* pass structured data to manager */
return memscan_find_xref_nf_impl(start, end, &xref);
}
/* */
ms_result_t
memscan_find_xref_at_nf(const ms_uptr_t start, const ms_uptr_t end,
const ms_uptr_t address, const ms_usize_t nth_match,
bool swap_endianness)
{
return memscan_find_xref_nf(start, end, *(ms_uptr_t*)address, nth_match,
swap_endianness);
}
/* */
extern ms_result_t
memscan_find_string_b(const ms_uptr_t start, const ms_uptr_t end,
const char* text, const ms_usize_t text_size,
const ms_usize_t nth_match,
const ms_ubyte_t* follow_pattern,
const ms_usize_t follow_pattern_size,
const ms_usize_t follow_nth_match,
const ms_follow_direction_t follow_direction)
{
/* data */
ms_string_xref_t xref = {0};
xref.m_data = text;
xref.m_size = text_size;
xref.m_match = nth_match;
ms_find_bytes_t find_bytes = {0};
find_bytes.m_data = follow_pattern;
find_bytes.m_size = follow_pattern_size;
find_bytes.m_match = follow_nth_match;
find_bytes.m_direction = follow_direction;
/* pass structured data to manager */
return memscan_find_string_b_impl(start, end, &xref, &find_bytes);
}
/* */
ms_result_t
memscan_find_string_s(const ms_uptr_t start, const ms_uptr_t end,
const char* text, const ms_usize_t text_size,
const ms_usize_t nth_match, const char* follow_pattern,
const ms_usize_t follow_nth_match,
const ms_follow_direction_t follow_direction)
{
/* data */
ms_string_xref_t xref = {0};
xref.m_data = text;
xref.m_size = text_size;
xref.m_match = nth_match;
ms_find_str_t find = {0};
find.m_data = follow_pattern;
find.m_match = follow_nth_match;
find.m_direction = follow_direction;
/* pass structured data to manager */
return memscan_find_string_s_impl(start, end, &xref, &find);
}
/* */
ms_result_t
memscan_find_string_nf(const ms_uptr_t start, const ms_uptr_t end,
const char* text, const ms_usize_t text_size,
const ms_usize_t nth_match)
{
/* data */
ms_string_xref_t xref = {0};
xref.m_data = text;
xref.m_size = text_size;
xref.m_match = nth_match;
/* pass structured data to manager */
return memscan_find_string_nf_impl(start, end, &xref);
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,102 @@
#include "util.h"
#include <stdlib.h>
#include <string.h>
ms_pattern_t
util_build_pattern(const char* data, const ms_usize_t data_size)
{
ms_pattern_t result = {0};
result.m_data = NULL;
result.m_size = 0;
#if !UTIL_UNSAFE_OPTIMIZATIONS
if (data == NULL) {
result.m_status = MS_BUILD_STATUS_NO_DATA;
goto leave;
}
#endif
size_t len = data_size;
#if !UTIL_UNSAFE_OPTIMIZATIONS
if (len == 0) {
result.m_status = MS_BUILD_STATUS_SHORT_DATA;
goto leave;
}
#endif
/* data */
char* start = (char*)data;
char* end = (char*)(data + len);
/* precompute allocation size */
ms_usize_t size = 0;
for (char* current = start; current < end; ++current)
++size;
ms_ubyte_t* bytes = (ms_ubyte_t*)malloc(size * sizeof *bytes);
/* prefetched */
ms_usize_t indice = 0;
for (char* current = start; current < end; ++current) {
/* hex substring conversion */
bytes[indice++] = (ms_ubyte_t)strtoul(current, &current, 16);
}
result.m_data = bytes;
result.m_size = indice;
result.m_status = MS_BUILD_STATUS_OK;
leave:
return result;
}
/* */
ms_free_t
util_free_pattern(ms_pattern_t* pattern)
{
#if !UTIL_UNSAFE_OPTIMIZATIONS
if (pattern == NULL) {
return MS_FREE_NO;
}
#endif
ms_free_t result = MS_FREE_NO;
if (pattern->m_status == MS_BUILD_STATUS_OK) {
free(pattern->m_data);
pattern->m_data = NULL;
result = MS_FREE_YES;
}
pattern->m_size = 0;
return result;
}
ms_ubyte_t*
util_ptr_to_byteset(const ms_uptr_t num, bool swap_endianness)
{
/* data */
static ms_ubyte_t bytes[MEMSCAN_BYTESET_SIZE] = {0};
for (ms_usize_t i = 0; i < MEMSCAN_BYTESET_SIZE; ++i) {
/* shift formation to get current indice */
bytes[swap_endianness ? i : MEMSCAN_BYTESET_SIZE - i - 1] =
(ms_ubyte_t)(num >> (i * CHAR_BIT));
}
return bytes;
}

View File

@ -0,0 +1,88 @@
#pragma once
/* Includes */
#include <limits.h>
#include <stdbool.h>
#include <stdint.h>
/* Types */
typedef uint8_t ms_ubyte_t;
typedef uintptr_t ms_uptr_t;
typedef size_t ms_usize_t;
/* Extern */
#if __cplusplus
#define MEMSCAN_EXTERN extern "C"
#else
#define MEMSCAN_EXTERN extern
#endif
typedef enum {
/* nothing to free, or there's a condition preventing the process */
MS_FREE_NO = 0,
/* the data was found present and then freed */
MS_FREE_YES
} ms_free_t;
typedef enum {
/* won't be reached unless UTIL_UNSAFE_OPTIMIZATIONS is off */
/* passed data was NULL */
MS_BUILD_STATUS_NO_DATA = 0,
/* data len was 0 */
MS_BUILD_STATUS_SHORT_DATA,
/* */
/* generation has succeeded, status was set to OK */
MS_BUILD_STATUS_OK
} ms_build_status_t;
typedef struct {
ms_ubyte_t* m_data;
ms_usize_t m_size;
ms_build_status_t m_status;
} ms_pattern_t;
/* Methods */
/**
* @brief Generate byte code array from byte-code style string
*
* @param data Example: "AA BB CC DD EE FF", equivalent to
* (ms_ubyte_t*)"\xAA\xBB\xCC\xDD\xEE\xFF"
* @param data_size Size of 'data'
* @return Refer to ms_pattern_t for documentation
*/
MEMSCAN_EXTERN ms_pattern_t
util_build_pattern(const char* data, const ms_usize_t data_size);
/**
* @brief Deallocate pattern array after usage
*
* @param pattern Reference to the pattern construct
* @return Refer to ms_free_t for documentation
*/
MEMSCAN_EXTERN ms_free_t
util_free_pattern(ms_pattern_t* pattern);
/**
* @brief Convert pointer in numerical form to byteset of MEMSCAN_BYTESET_SIZE
* bytes
*
* @param num Value to convert
* @param swap_endianness Whether to swap endianness or not
* @return Value as a MEMSCAN_BYTESET_SIZE bytes array
*/
MEMSCAN_EXTERN ms_ubyte_t*
util_ptr_to_byteset(const ms_uptr_t num, bool swap_endianness);
/* Constants */
#define MEMSCAN_BYTESET_SIZE (sizeof(ms_uptr_t) / sizeof(ms_ubyte_t))
#define MEMSCAN_POINTER_BITS (sizeof(ms_uptr_t) * CHAR_BIT)

View File

@ -7,6 +7,7 @@
#include "..\game\GamePH\LevelDI.h"
#include "..\game\GamePH\PlayerDI_PH.h"
#include "..\game\GamePH\PlayerVariables.h"
#include "..\game\GamePH\PlayerDI_PH.h"
#include "..\game\GamePH\gameph_misc.h"
#include "..\offsets.h"
#include "camera.h"

View File

@ -10,7 +10,6 @@
#include "..\game\GamePH\LogicalPlayer.h"
#include "..\game\GamePH\PlayerDI_PH.h"
#include "..\game\GamePH\PlayerHealthModule.h"
#include "..\game\GamePH\PlayerObjProperties.h"
#include "..\game\GamePH\PlayerState.h"
#include "..\game\GamePH\PlayerVariables.h"
#include "..\game\GamePH\SessionCooperativeDI.h"
@ -41,7 +40,6 @@ namespace Menu {
{ "LogicalPlayer", reinterpret_cast<LPVOID(*)()>(&GamePH::LogicalPlayer::Get) },
{ "PlayerDI_PH", reinterpret_cast<LPVOID(*)()>(&GamePH::PlayerDI_PH::Get) },
{ "PlayerHealthModule", reinterpret_cast<LPVOID(*)()>(&GamePH::PlayerHealthModule::Get) },
{ "PlayerObjProperties", reinterpret_cast<LPVOID(*)()>(&GamePH::PlayerObjProperties::Get) },
{ "PlayerState", reinterpret_cast<LPVOID(*)()>(&GamePH::PlayerState::Get) },
{ "PlayerVariables", reinterpret_cast<LPVOID(*)()>(&GamePH::PlayerVariables::Get) },
{ "SessionCooperativeDI", reinterpret_cast<LPVOID(*)()>(&GamePH::SessionCooperativeDI::Get) },

View File

@ -218,12 +218,12 @@ namespace Menu {
ImGuiStyle* style = &ImGui::GetStyle();
style->WindowTitleAlign = ImVec2(0.5f, 0.5f);
style->WindowPadding = ImVec2(15, 15);
style->WindowPadding = ImVec2(13, 13);
style->WindowRounding = 5.0f;
style->ChildRounding = 4.0f;
style->FramePadding = ImVec2(5, 5);
style->FramePadding = ImVec2(4, 4);
style->FrameRounding = 4.0f;
style->ItemSpacing = ImVec2(12, 8);
style->ItemSpacing = ImVec2(10, 6);
style->ItemInnerSpacing = ImVec2(8, 6);
style->IndentSpacing = 25.0f;
style->ScrollbarSize = 15.0f;

View File

@ -22,6 +22,8 @@ namespace Menu {
Option firstTimeRunning{};
Option hasSeenChangelog{};
int currentTabIndex = 0;
void Render() {
ImGui::StyleScaleAllSizes(&ImGui::GetStyle(), scale, &defStyle);
ImGui::GetIO().FontGlobalScale = scale;
@ -47,6 +49,7 @@ namespace Menu {
ImGui::SetNextWindowBgAlpha(static_cast<float>(opacity) / 100.0f);
ImGui::SetNextWindowSizeConstraints(ImVec2(std::fmax(minWndSize.x - GImGui->Style.WindowPadding.x * 2.0f, ImGui::CalcTextSize(Core::gameVer > GAME_VER_COMPAT ? "Please wait for a new mod update." : "Upgrade your game version to one that the mod supports.").x), remainingHeight), ImVec2(maxWndSize.x - GImGui->Style.WindowPadding.x * 2.0f, remainingHeight));
if (ImGui::BeginChild("##TabChild", ImVec2(0.0f, 0.0f), ImGuiChildFlags_AlwaysAutoResize | ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_Border)) {
currentTabIndex = tab.first;
childWidth = ImGui::GetItemRectSize().x;
tab.second->Render();
ImGui::EndChild();

View File

@ -12,7 +12,6 @@ namespace Menu {
virtual void Update() {};
std::string_view tabName{};
private:
int tabIndex{};
};
@ -27,5 +26,7 @@ namespace Menu {
extern Option firstTimeRunning;
extern Option hasSeenChangelog;
extern int currentTabIndex;
extern void Render();
}

View File

@ -6730,6 +6730,71 @@ namespace Menu {
else
GamePH::PlayerVariables::ChangePlayerVar(varName, std::any_cast<bool>(itDef->second.first));
}
static bool shouldDisplayVariable(const std::string& key, const std::string& searchFilter) {
if (searchFilter.empty()) return true;
// Convert searchFilter to lowercase
std::string lowerFilter = searchFilter;
std::transform(lowerFilter.begin(), lowerFilter.end(), lowerFilter.begin(), ::tolower);
// Convert key to lowercase and check if it contains the filter
std::string lowerKey = key;
std::transform(lowerKey.begin(), lowerKey.end(), lowerKey.begin(), ::tolower);
return lowerKey.find(lowerFilter) != std::string::npos;
}
static void renderDebugInfo(const std::string& key, const std::pair<void*, std::string>& val) {
const float maxInputTextWidth = ImGui::CalcTextSize("0x0000000000000000").x;
static std::string labelID{};
labelID = "##DebugAddrInputText" + std::string(key);
DWORD64 finalAddr = val.second == "float" ? reinterpret_cast<DWORD64>(reinterpret_cast<float*>(val.first)) : reinterpret_cast<DWORD64>(reinterpret_cast<bool*>(val.first));
std::stringstream ss;
if (finalAddr)
ss << "0x" << std::uppercase << std::hex << finalAddr;
else
ss << "NULL";
static std::string addrString{};
addrString = ss.str();
ImGui::SameLine();
//ImGui::SetCursorPosY(ImGui::GetCursorPosY() - ((ImGui::GetFrameHeight() - ImGui::GetTextLineHeight()) / 2.0f));
ImGui::SetNextItemWidth(maxInputTextWidth);
ImGui::PushStyleColor(ImGuiCol_Text, finalAddr ? IM_COL32(0, 255, 0, 255) : IM_COL32(255, 0, 0, 255));
ImGui::InputText(labelID.c_str(), const_cast<char*>(addrString.c_str()), strlen(addrString.c_str()), ImGuiInputTextFlags_ReadOnly);
ImGui::PopStyleColor();
}
static void renderPlayerVariable(const std::string& key, const std::pair<void*, std::string>& val) {
float* floatVarAddr = nullptr;
bool* boolVarAddr = nullptr;
if (val.second == "float") {
floatVarAddr = reinterpret_cast<float*>(val.first);
float newValue = *floatVarAddr;
if (ImGui::InputFloat(key.c_str(), &newValue)) {
*floatVarAddr = newValue;
*(floatVarAddr + 1) = newValue;
}
} else if (val.second == "bool") {
boolVarAddr = reinterpret_cast<bool*>(val.first);
bool newValue = *boolVarAddr;
if (ImGui::Checkbox(key.c_str(), &newValue)) {
*boolVarAddr = newValue;
*(boolVarAddr + 1) = newValue;
}
}
ImGui::SameLine();
static std::string restoreBtnName{};
restoreBtnName = "Restore##" + key;
if (ImGui::Button(restoreBtnName.c_str(), "Restores player variable to default"))
RestoreVariableToDefault(key);
if (debugEnabled)
renderDebugInfo(key, val);
}
static void HandlePlayerVariablesList() {
if (!playerVariables.GetValue())
return;
@ -6737,6 +6802,7 @@ namespace Menu {
ImGui::BeginDisabled(!GamePH::PlayerVariables::gotPlayerVars); {
if (ImGui::CollapsingHeader("Player variables list", ImGuiTreeNodeFlags_None)) {
ImGui::Indent();
if (ImGui::Button("Save variables to file", "Saves current player variables to chosen file inside the file dialog"))
ImGuiFileDialog::Instance()->OpenDialog("ChooseSCRPath", "Choose Folder", nullptr, saveSCRPath.empty() ? "." : saveSCRPath);
ImGui::SameLine();
@ -6755,67 +6821,13 @@ namespace Menu {
ImGui::Separator();
ImGui::InputTextWithHint("##VarsSearch", "Search variables", playerVarsSearchFilter, 64);
std::string restoreBtnName{};
for (auto const& [key, val] : GamePH::PlayerVariables::playerVars) {
if (!val.first)
if (!val.first || !shouldDisplayVariable(key, playerVarsSearchFilter))
continue;
std::string lowerSearch = key.data();
std::string lowerFilter = playerVarsSearchFilter;
std::transform(lowerSearch.begin(), lowerSearch.end(), lowerSearch.begin(), tolower);
std::transform(lowerFilter.begin(), lowerFilter.end(), lowerFilter.begin(), tolower);
if (lowerSearch.find(std::string(lowerFilter)) == std::string::npos)
continue;
float* floatVarAddr = nullptr;
bool* boolVarAddr = nullptr;
if (val.second == "float") {
floatVarAddr = reinterpret_cast<float*>(val.first);
float newValue = *floatVarAddr;
if (ImGui::InputFloat(key.data(), &newValue)) {
*floatVarAddr = newValue;
*(floatVarAddr + 1) = newValue;
}
} else if (val.second == "bool") {
boolVarAddr = reinterpret_cast<bool*>(val.first);
bool newValue = *boolVarAddr;
if (ImGui::Checkbox(key.data(), &newValue)) {
*boolVarAddr = newValue;
*(boolVarAddr + 1) = newValue;
}
}
ImGui::SameLine();
restoreBtnName = std::string("Restore##") + std::string(key);
if (ImGui::Button(restoreBtnName.c_str(), "Restores player variable to default"))
RestoreVariableToDefault(key);
if (debugEnabled) {
const float maxInputTextWidth = ImGui::CalcTextSize("0x0000000000000000").x;
static std::string labelID{};
labelID = "##DebugAddrInputText" + std::string(key);
const DWORD64 finalAddr = floatVarAddr ? reinterpret_cast<DWORD64>(floatVarAddr) : reinterpret_cast<DWORD64>(boolVarAddr);
std::stringstream ss{};
if (finalAddr)
ss << "0x" << std::uppercase << std::hex << finalAddr;
else
ss << "NULL";
static std::string addrString{};
addrString = ss.str();
ImGui::SameLine();
ImGui::SetCursorPosY(ImGui::GetCursorPosY() - ((ImGui::GetFrameHeight() - ImGui::GetTextLineHeight()) / 2.0f));
ImGui::SetNextItemWidth(maxInputTextWidth);
ImGui::PushStyleColor(ImGuiCol_Text, finalAddr ? IM_COL32(0, 255, 0, 255) : IM_COL32(255, 0, 0, 255));
ImGui::InputText(labelID.c_str(), const_cast<char*>(addrString.c_str()), strlen(addrString.c_str()), ImGuiInputTextFlags_ReadOnly);
ImGui::PopStyleColor();
}
renderPlayerVariable(key, val);
}
ImGui::Unindent();
}
ImGui::EndDisabled();

View File

@ -5,7 +5,9 @@
#define AddOffset(name, moduleName, pattern, type, retType)\
static retType Get_## name () {\
static retType name = NULL;\
if (Utils::Memory::IsValidPtr(name)) return name;\
static int i = 0;\
if (Utils::Memory::IsValidPtr(name) || !GetModuleHandleA(moduleName) || i >= 10) return name;\
i++;\
return name=reinterpret_cast<retType>(Utils::SigScan::PatternScanner::FindPattern(moduleName, {pattern, type}));\
}
#define AddStaticOffset(name, off)\
@ -18,13 +20,15 @@ static DWORD64 Get_## name () {\
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);\
return name=reinterpret_cast<DWORD64>(GetModuleHandleA(moduleName)) + static_cast<DWORD64>(off);\
}
#define AddVTOffset(name, moduleName, rttiName, retType)\
static retType GetVT_## name () {\
static retType VT_## name = NULL;\
if (Utils::Memory::IsValidPtr(VT_## name)) return VT_## name;\
static int i = 0;\
if (Utils::Memory::IsValidPtr(VT_## name)|| !GetModuleHandleA(moduleName) || i >= 10) return VT_## name;\
i++;\
return VT_## name=reinterpret_cast<retType>(Utils::RTTI::GetVTablePtr(moduleName, rttiName));\
}
@ -38,7 +42,7 @@ struct Offsets {
AddVTOffset(TypedFieldMetaFloatPlayerVariable, "gamedll_ph_x64_rwdi.dll", "?$TypedFieldMeta@VFloatPlayerVariable@@@?$FieldsCollection@VPlayerVariables@@@constds", LPVOID)
AddVTOffset(TypedFieldMetaBoolPlayerVariable, "gamedll_ph_x64_rwdi.dll", "?$TypedFieldMeta@VBoolPlayerVariable@@@?$FieldsCollection@VPlayerVariables@@@constds", LPVOID)
AddOffset(LoadPlayerVars, "gamedll_ph_x64_rwdi.dll", "48 89 4C 24 ?? B8 ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 2B E0 48 8B 8C 24", Utils::SigScan::PatternType::Address, LPVOID)
AddOffset(PlayerState, "gamedll_ph_x64_rwdi.dll", "4C 8B 35 [?? ?? ?? ?? 4C 8B E2", Utils::SigScan::PatternType::RelativePointer, LPVOID)
AddOffset(PlayerState, "gamedll_ph_x64_rwdi.dll", "48 8B 35 [?? ?? ?? ?? 4C 8B F2 48 8B F9", Utils::SigScan::PatternType::RelativePointer, LPVOID)
// Game related
AddVTOffset(CBulletPhysicsCharacter, "engine_x64_rwdi.dll", "CBulletPhysicsCharacter", LPVOID)
@ -67,7 +71,7 @@ struct Offsets {
AddStaticOffset(allowVelocityMod_offset, 0x5C)
AddStaticOffset(disableHeadCorrection_offset, 0x108)
AddOffset(CLobbySteam, "engine_x64_rwdi.dll", "48 8B 05 [?? ?? ?? ?? 48 85 C0 74 ?? 48 83 C0", Utils::SigScan::PatternType::RelativePointer, LPVOID)
AddOffset(g_PlayerObjProperties, "gamedll_ph_x64_rwdi.dll", "48 89 0D [?? ?? ?? ?? E8 ?? ?? ?? ?? 48 85 C0", Utils::SigScan::PatternType::RelativePointer, LPVOID)
//AddOffset(g_PlayerDI_PH, "gamedll_ph_x64_rwdi.dll", "48 89 0D [?? ?? ?? ?? E8 ?? ?? ?? ?? 48 85 C0", Utils::SigScan::PatternType::RelativePointer, LPVOID) // also PlayerObjProperties
AddOffset(g_DayNightCycle, "gamedll_ph_x64_rwdi.dll", "48 8B 0D [?? ?? ?? ?? 48 85 C9 74 ?? E8 ?? ?? ?? ?? 84 C0 74 ?? B0 ?? 48 83 C4 ?? C3 32 C0", Utils::SigScan::PatternType::RelativePointer, LPVOID)
//AddOffset(g_CameraFPPDI, "gamedll_ph_x64_rwdi.dll", "48 89 05 [?? ?? ?? ?? 40 84 FF", PatternType::RelativePointer, DWORD64*)
AddOffset(g_FreeCamera, "gamedll_ph_x64_rwdi.dll", "48 89 05 [?? ?? ?? ?? 48 89 4C 24", Utils::SigScan::PatternType::RelativePointer, DWORD64*)

View File

@ -1,6 +1,7 @@
#pragma once
#include <functional>
#include <set>
#include <atomic>
#include "..\MinHook\MinHook.h"
namespace Utils {
@ -30,7 +31,9 @@ namespace Utils {
virtual bool HookLoop() { return false; };
const std::string_view name;
const std::string_view name{};
std::atomic<bool> isHooking = false;
std::atomic<bool> isHooked = false;
};
template <typename GetTargetOffsetRetType>
class ByteHook : HookBase {
@ -38,13 +41,18 @@ namespace Utils {
ByteHook(const std::string_view& name, GetTargetOffsetRetType(*pGetOffsetFunc)(), unsigned char* patchBytes, size_t bytesAmount, Option* optionRef = nullptr) : HookBase(name), pGetOffsetFunc(pGetOffsetFunc), patchBytes(patchBytes), bytesAmount(bytesAmount), optionRef(optionRef) {}
bool HookLoop() override {
if (hooked || (optionRef && !optionRef->GetValue()))
if (isHooked || (optionRef && !optionRef->GetValue()))
return true;
timeSpentHooking = Utils::Time::Timer(180000);
if (isHooking)
return true;
isHooking = true;
timeSpentHooking = Utils::Time::Timer(120000);
while (true) {
if (timeSpentHooking.DidTimePass()) {
spdlog::error("Failed hooking \"{}\" after 60 seconds", name);
spdlog::error("Failed hooking \"{}\" after 120 seconds", name);
isHooking = false;
return false;
}
if (!pGetOffsetFunc || !pGetOffsetFunc())
@ -60,13 +68,16 @@ namespace Utils {
}
memcpy_s(pGetOffsetFunc(), bytesAmount, patchBytes, bytesAmount);
VirtualProtect(pGetOffsetFunc(), bytesAmount, originalProtection, &oldProtection);
hooked = true;
isHooked = true;
isHooking = false;
return true;
Sleep(10);
}
}
void Enable() {
if (hooked)
if (isHooked)
return;
DWORD originalProtection = 0;
@ -79,10 +90,10 @@ namespace Utils {
}
memcpy_s(pGetOffsetFunc(), bytesAmount, patchBytes, bytesAmount);
VirtualProtect(pGetOffsetFunc(), bytesAmount, originalProtection, &oldProtection);
hooked = true;
isHooked = true;
}
void Disable() {
if (!hooked)
if (!isHooked)
return;
DWORD originalProtection = 0;
@ -91,11 +102,11 @@ namespace Utils {
VirtualProtect(pGetOffsetFunc(), bytesAmount, PAGE_EXECUTE_READWRITE, &originalProtection);
memcpy_s(pGetOffsetFunc(), bytesAmount, origBytes, bytesAmount);
VirtualProtect(pGetOffsetFunc(), bytesAmount, originalProtection, &oldProtection);
hooked = false;
isHooked = false;
}
void Toggle() {
if (!optionRef) {
hooked ? Disable() : Enable();
isHooked ? Disable() : Enable();
return;
}
@ -109,9 +120,7 @@ namespace Utils {
unsigned char* patchBytes = nullptr;
size_t bytesAmount = 0;
bool hooked = false;
Utils::Time::Timer timeSpentHooking{ 180000 };
Utils::Time::Timer timeSpentHooking{ 120000 };
};
template <typename GetTargetOffsetRetType, typename OrigType>
class MHook : HookBase {
@ -121,11 +130,16 @@ namespace Utils {
bool HookLoop() override {
if (pOriginal)
return true;
timeSpentHooking = Utils::Time::Timer(180000);
if (isHooking)
return true;
isHooking = true;
timeSpentHooking = Utils::Time::Timer(120000);
while (true) {
if (timeSpentHooking.DidTimePass()) {
spdlog::error("Failed hooking function \"{}\" after 60 seconds", name);
spdlog::error("Failed hooking function \"{}\" after 120 seconds", name);
isHooking = false;
return false;
}
if (!pGetOffsetFunc)
@ -135,8 +149,12 @@ namespace Utils {
pTarget = reinterpret_cast<OrigType>(pGetOffsetFunc());
else if (!pOriginal && MH_CreateHook(pTarget, pDetour, reinterpret_cast<LPVOID*>(&pOriginal)) == MH_OK) {
MH_EnableHook(pTarget);
isHooked = true;
isHooking = false;
return true;
}
Sleep(10);
}
}
@ -146,7 +164,7 @@ namespace Utils {
GetTargetOffsetRetType(*pGetOffsetFunc)() = nullptr;
OrigType pDetour = nullptr;
Utils::Time::Timer timeSpentHooking{ 180000 };
Utils::Time::Timer timeSpentHooking{ 120000 };
};
template <typename GetTargetOffsetRetType, typename OrigType>
class VTHook : HookBase {
@ -156,11 +174,16 @@ namespace Utils {
bool HookLoop() override {
if (pOriginal)
return true;
timeSpentHooking = Utils::Time::Timer(180000);
if (isHooking)
return true;
isHooking = true;
timeSpentHooking = Utils::Time::Timer(120000);
while (true) {
if (timeSpentHooking.DidTimePass()) {
spdlog::error("Failed hooking function \"{}\" after 60 seconds", name);
spdlog::error("Failed hooking function \"{}\" after 120 seconds", name);
isHooking = false;
return false;
}
if (!pGetOffsetFunc)
@ -170,8 +193,14 @@ namespace Utils {
pInstance = pGetOffsetFunc();
else if (!pOriginal) {
HookVT(pInstance, pDetour, reinterpret_cast<LPVOID*>(&pOriginal), offset);
return true;
if (pOriginal) {
isHooked = true;
isHooking = false;
return true;
}
}
Sleep(10);
}
}
@ -181,7 +210,7 @@ namespace Utils {
LPVOID pInstance = nullptr;
OrigType pDetour = nullptr;
Utils::Time::Timer timeSpentHooking{ 180000 };
Utils::Time::Timer timeSpentHooking{ 120000 };
DWORD offset = 0x0;
};

View File

@ -3,13 +3,23 @@
namespace Utils {
namespace Memory {
const MODULEINFO GetModuleInfo(const char* szModule) {
const HMODULE hModule = GetModuleHandle(szModule);
if (!szModule)
return MODULEINFO();
static std::unordered_map<std::string_view, MODULEINFO> moduleInfoCache;
auto it = moduleInfoCache.find(szModule);
if (it != moduleInfoCache.end())
return it->second;
HMODULE hModule = GetModuleHandle(szModule);
if (hModule == 0)
return MODULEINFO();
MODULEINFO moduleInfo{};
GetModuleInformation(GetCurrentProcess(), hModule, &moduleInfo, sizeof(MODULEINFO));
return moduleInfo;
moduleInfoCache[szModule] = moduleInfo;
return moduleInfoCache[szModule];
}
const FARPROC GetProcAddr(const std::string_view& module, const std::string_view& funcName) {
const HMODULE moduleHandle = GetModuleHandleA(module.data());
@ -27,13 +37,13 @@ namespace Utils {
return ptr && (ptr <= moduleEndPoint && ptr >= moduleEntryPoint);
}
static std::string bytesToIDAPattern(BYTE* bytes, size_t size) {
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)
if (currentByte != SigScanWildCard)
idaPattern << std::setw(2) << currentByte;
else
idaPattern << "??";
@ -44,6 +54,34 @@ namespace Utils {
return idaPattern.str();
}
std::string ConvertSigToScannerSig(const char* pattern, int* offsetToAddrInSig) {
size_t len = strlen(pattern);
std::string patt{};
int bytesCounted = 0;
for (size_t i = 0; i < len;) {
if (pattern[i] == ' ' || i == len - 1)
bytesCounted++;
if (pattern[i] == '[') {
i++;
if (offsetToAddrInSig)
*offsetToAddrInSig = static_cast<int>(bytesCounted);
continue;
}
if (pattern[i] == '?') {
patt += SigScanWildCardStr;
i += (pattern[static_cast<DWORD64>(i) + 1] == '?') ? 2 : 1;
} else {
patt.push_back(pattern[i]);
i++;
}
}
return patt;
}
DWORD64 CalcTargetAddrOfRelInst(DWORD64 addrOfInst, size_t opSize) {
int offset = *reinterpret_cast<int*>(addrOfInst + opSize);
@ -52,7 +90,7 @@ namespace Utils {
std::vector<DWORD64> GetXrefsTo(DWORD64 address, DWORD64 start, size_t size) {
std::vector<DWORD64> xrefs = {};
const std::string idaPattern = bytesToIDAPattern(reinterpret_cast<BYTE*>(&address), 8);
const std::string idaPattern = BytesToIDAPattern(reinterpret_cast<BYTE*>(&address), 8);
const DWORD64 end = start + size;
while (start && start < end) {

View File

@ -5,11 +5,17 @@
namespace Utils {
namespace Memory {
static const BYTE SigScanWildCard = 0xAA;
static const std::string_view SigScanWildCardStr = "AA";
extern const MODULEINFO GetModuleInfo(const char* szModule);
extern const FARPROC GetProcAddr(const std::string_view& module, const std::string_view& funcName);
extern const bool IsAddressValidMod(const DWORD64 ptr, const char* moduleName);
extern std::string BytesToIDAPattern(BYTE* bytes, size_t size);
extern std::string ConvertSigToScannerSig(const char* pattern, int* offsetToAddrInSig = nullptr);
extern DWORD64 CalcTargetAddrOfRelInst(DWORD64 addrOfInst, size_t opSize);
std::vector<DWORD64> GetXrefsTo(DWORD64 address, DWORD64 start, size_t size);

View File

@ -1,45 +1,30 @@
#include "pch.h"
#include <pch.h>
namespace Utils {
namespace RTTI {
static std::string bytesToIDAPattern(BYTE* bytes, size_t size, bool allowUnkBytes = true) {
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 || !allowUnkBytes)
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);
const std::string idaPattern = Memory::BytesToIDAPattern(reinterpret_cast<BYTE*>(const_cast<DWORD64*>(&finalOffset)), 4);
std::string patt = Memory::ConvertSigToScannerSig(idaPattern.c_str());
// 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}));
const auto scanner = memscan::mapped_region_t(start, end);
auto patternFind = scanner.find_pattern<ms_uptr_t>(patt);
// 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)
if (!patternFind.has_value())
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;
xrefs.push_back(patternFind.value());
start = patternFind.value() + 8;
}
return xrefs;
@ -83,7 +68,7 @@ namespace Utils {
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());
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)
@ -113,7 +98,7 @@ namespace Utils {
// 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, false);
idaPattern = Memory::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;

View File

@ -38,92 +38,16 @@ namespace Utils {
}
LPVOID PatternScanner::FindPattern(LPVOID startAddress, DWORD64 searchSize, const Pattern& pattern) {
size_t len = strlen(pattern.pattern);
if (len == 0)
return nullptr;
DWORD64 pos = 0;
size_t byteCount = 1;
uint32_t i = 0;
while (i < len - 1) {
if (pattern.pattern[i] == ' ')
byteCount++;
i++;
}
BYTE* patt = reinterpret_cast<BYTE*>(malloc(byteCount + 1));
if (!patt)
return nullptr;
BYTE* mask = reinterpret_cast<BYTE*>(malloc(byteCount + 1));
if (!mask) {
free(patt);
return nullptr;
}
int offset = 0;
int bytesCounted = 0;
i = 0;
while (i < len - 1) {
if (pattern.pattern[i] == '[') {
i++;
offset = bytesCounted;
}
std::string patt = Memory::ConvertSigToScannerSig(pattern.pattern, &offset);
if (pattern.pattern[i] == '\0')
break;
if (pattern.pattern[i] == '?' && pattern.pattern[i + 1] == '?') {
mask[bytesCounted] = '?';
patt[bytesCounted] = '\0';
} else {
BYTE hn = pattern.pattern[i] > '9' ? pattern.pattern[i] - 'A' + 10 : pattern.pattern[i] - '0';
BYTE ln = pattern.pattern[i + 1] > '9' ? pattern.pattern[i + 1] - 'A' + 10 : pattern.pattern[i + 1] - '0';
BYTE n = (hn << 4) | ln;
mask[bytesCounted] = 'x';
patt[bytesCounted] = n;
}
bytesCounted++;
i += 2;
while (i < len && (pattern.pattern[i] == ' ' || pattern.pattern[i] == '\t' || pattern.pattern[i] == '\r' || pattern.pattern[i] == '\n'))
i++;
}
if (bytesCounted <= byteCount)
mask[bytesCounted] = '\0';
const auto scanner = memscan::mapped_region_t(reinterpret_cast<DWORD64>(startAddress), reinterpret_cast<DWORD64>(startAddress) + searchSize);
auto patternFind = scanner.find_pattern<ms_uptr_t>(patt);
LPVOID ret = nullptr;
DWORD64 retAddress = reinterpret_cast<DWORD64>(startAddress);
DWORD64 endAddress = retAddress + searchSize;
size_t searchLen = bytesCounted;
while (retAddress < endAddress) {
__try {
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;
}
retAddress++;
} __except (EXCEPTION_EXECUTE_HANDLER) {
retAddress++;
}
}
free(patt);
free(mask);
if (patternFind.has_value())
ret = reinterpret_cast<LPVOID>(patternFind.value() + offset);
switch (pattern.type) {
case PatternType::Pointer:

View File

@ -4,36 +4,24 @@ namespace Utils {
namespace Time {
Timer::Timer(long timeMs) : timeToPass(std::chrono::milliseconds(timeMs)), timePassed(false) {
start = std::chrono::time_point_cast<std::chrono::milliseconds>(clock::now());
end = start + timeToPass;
}
const long long Timer::GetTimePassed() {
if (timePassed)
return -1;
const auto end = clock::now();
const auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
const auto currentClock = clock::now();
const auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(currentClock - start);
const long long timePassedMs = duration.count();
if (timePassedMs < 0) {
start = std::chrono::time_point_cast<std::chrono::milliseconds>(clock::now());
return -1;
}
return timePassedMs;
}
const bool Timer::DidTimePass() {
if (timePassed)
return timePassed;
return true;
const auto end = clock::now();
const auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
const long long timePassedMs = duration.count();
if (timePassedMs < 0) {
start = std::chrono::time_point_cast<std::chrono::milliseconds>(clock::now());
return false;
}
if (duration >= timeToPass)
if (clock::now() >= end)
timePassed = true;
return timePassed;
}

View File

@ -5,7 +5,7 @@
namespace Utils {
namespace Time {
class Timer {
using clock = std::chrono::system_clock;
using clock = std::chrono::steady_clock;
using time_point_type = std::chrono::time_point<clock, std::chrono::milliseconds>;
public:
std::chrono::milliseconds timeToPass;
@ -16,6 +16,7 @@ namespace Utils {
const bool DidTimePass();
private:
time_point_type start;
time_point_type end;
bool timePassed;
};