From 0ee417db70d141e1d424be1f92866c4ecd470b72 Mon Sep 17 00:00:00 2001 From: EricPlayZ Date: Sun, 3 Nov 2024 17:53:49 +0200 Subject: [PATCH] - Added compatibility with v1.19 Tower Raid: Halloween Run update - Improved CPU performance at game startup when using the mod --- EGameTools/EGameTools.vcxproj | 16 +- EGameTools/EGameTools.vcxproj.filters | 24 +- EGameTools/pch/pch.h | 1 + EGameTools/source/changelog.h | 10 +- EGameTools/source/core.cpp | 57 +- EGameTools/source/core.h | 7 +- EGameTools/source/dllmain.cpp | 11 +- .../game/Engine/CBulletPhysicsCharacter.h | 6 +- .../source/game/Engine/engine_hooks.cpp | 1 - EGameTools/source/game/GamePH/GameDI_PH.h | 2 +- EGameTools/source/game/GamePH/LocalClientDI.h | 4 +- .../source/game/GamePH/PlayerObjProperties.h | 2 + EGameTools/source/game/GamePH/PlayerState.h | 2 +- EGameTools/source/game/GamePH/game_hooks.cpp | 14 +- EGameTools/source/game/GamePH/gameph_misc.cpp | 6 +- .../source/game/GamePH/gen_TPPModel.cpp | 21 - EGameTools/source/game/GamePH/gen_TPPModel.h | 14 - EGameTools/source/memscan/memscan.c | 935 +++++++++++++++ EGameTools/source/memscan/memscan.h | 1037 +++++++++++++++++ EGameTools/source/memscan/util/util.c | 102 ++ EGameTools/source/memscan/util/util.h | 88 ++ EGameTools/source/menu/camera.cpp | 6 +- EGameTools/source/menu/debug.cpp | 2 - EGameTools/source/menu/menu.cpp | 3 + EGameTools/source/menu/menu.h | 3 +- EGameTools/source/menu/world.cpp | 2 +- EGameTools/source/offsets.h | 8 +- EGameTools/source/utils/hook.h | 24 +- EGameTools/source/utils/memory.cpp | 34 +- EGameTools/source/utils/memory.h | 6 + EGameTools/source/utils/rtti.cpp | 35 +- EGameTools/source/utils/sigscan.cpp | 103 +- 32 files changed, 2340 insertions(+), 246 deletions(-) delete mode 100644 EGameTools/source/game/GamePH/gen_TPPModel.cpp delete mode 100644 EGameTools/source/game/GamePH/gen_TPPModel.h create mode 100644 EGameTools/source/memscan/memscan.c create mode 100644 EGameTools/source/memscan/memscan.h create mode 100644 EGameTools/source/memscan/util/util.c create mode 100644 EGameTools/source/memscan/util/util.h diff --git a/EGameTools/EGameTools.vcxproj b/EGameTools/EGameTools.vcxproj index b52e0ee..74e392d 100644 --- a/EGameTools/EGameTools.vcxproj +++ b/EGameTools/EGameTools.vcxproj @@ -35,7 +35,6 @@ - @@ -93,6 +92,14 @@ NotUsing + + NotUsing + NotUsing + + + NotUsing + NotUsing + @@ -140,7 +147,6 @@ - @@ -179,6 +185,8 @@ + + @@ -271,7 +279,7 @@ - pch;source\spdlog\include;source\ImGui\freetype\include;source\ImGui;source\MinHook; + pch;source\spdlog\include;source\ImGui\freetype\include;source\ImGui;source\MinHook;source\memscan; $(IntDir)$(TargetName)_$(PlatformTarget).pch @@ -301,7 +309,7 @@ Use pch.h stdcpplatest - pch;source\spdlog\include;source\ImGui\freetype\include;source\ImGui;source\MinHook;%(AdditionalIncludeDirectories) + pch;source\spdlog\include;source\ImGui\freetype\include;source\ImGui;source\MinHook;source\memscan;%(AdditionalIncludeDirectories) None false $(IntDir)$(TargetName)_$(PlatformTarget).pch diff --git a/EGameTools/EGameTools.vcxproj.filters b/EGameTools/EGameTools.vcxproj.filters index 454d1a9..4a72163 100644 --- a/EGameTools/EGameTools.vcxproj.filters +++ b/EGameTools/EGameTools.vcxproj.filters @@ -133,9 +133,6 @@ game\GamePH - - game\GamePH - game\GamePH @@ -212,6 +209,12 @@ menu + + memscan + + + memscan\util + @@ -364,9 +367,6 @@ game\GamePH - - game\GamePH - game\GamePH @@ -453,6 +453,12 @@ menu + + memscan + + + memscan\util + @@ -497,5 +503,11 @@ {122420d5-ac60-4778-80bf-1384697fcc44} + + {1e82ab1f-550e-4fac-8fac-0888e1e62873} + + + {7e81ee9f-f74a-4af8-8a10-7530b26c3c86} + \ No newline at end of file diff --git a/EGameTools/pch/pch.h b/EGameTools/pch/pch.h index 501c99c..1677226 100644 --- a/EGameTools/pch/pch.h +++ b/EGameTools/pch/pch.h @@ -66,6 +66,7 @@ #include #include #include +#include #include "..\source\config\ini.h" #include "..\source\game\Engine\GameSpeedHandler.h" diff --git a/EGameTools/source/changelog.h b/EGameTools/source/changelog.h index 7c640a4..5b8bdf0 100644 --- a/EGameTools/source/changelog.h +++ b/EGameTools/source/changelog.h @@ -109,7 +109,7 @@ With all of that said, here is finally what this update brings: - Added "Teleport to Waypoint" (Teleport) - WARNING: if the waypoint is selected to track an object/item on the map, Teleport to Waypoint will not work, if so just set the waypoint nearby instead - WARNING: your player height won't change when teleporting, so make sure you catch yourself if you fall under the map because of the teleportation -- Added "Saved Locations" in Teleport menu, with the ability of saving, deleting and teleporting to said locations; these locations are saved in the config file and will contain a name and a set of coordinates for each location; to reset back to the default list, remove the list from inside the config file and go back into the game +- Added "Saved Locations" in Teleport menu, with the ability of saving, deleting and teleporting to said locations; these locations are saved in the config file and will contain a name and a set of coordinates for each location; to reset back to the default list, remove the list from inside the config file and go back into the game; thank you to @Synsteric on Discord for helping me make the default list! - Added "Increase Data PAKs Limit" (Misc; requires game restart to apply) - you can now add more than 8 data PAKs, e.g. data8.pak, data9.pak, data10.pak, etc, up to 200 PAKs in total - Added "Disable Data PAKs CRC Check" (Misc; requires game restart to apply) - stops the game from scanning data PAKs, which allows you to use data PAK mods in multiplayer as well - Added "Disable Savegame CRC Check" (Misc; requires game restart to apply) - stops the game from falsely saying your savegame is corrupt whenever you modify it @@ -132,6 +132,12 @@ NOTE: Any mods that are put inside "EGameTools\UserModFiles" as a regular file ( I'm soon starting my exams and won't really have the time to update the mod the way I did right now. I had a 2 week leave and so I had plenty of time to further develop this mod. -If anyone is looking to help with development, I'm all eyes and ears! Thank you!)" } +If anyone is looking to help with development, I'm all eyes and ears! Thank you!)" }, + { "v1.2.1", + R"(- Added compatibility with v1.17.2 Tower Raid update +- Added a .PDB file included by default with the mod, for debugging purposes in case the game crashes, now I can more easily detect the cause of a game crash! +- Added a crash handler which handles game crashes and generates a "EGameTools-dump.dmp" file in the game's exe directory for debugging purposes; if you encounter a crash and this file gets generated, please send it to me anywhere you can, for example on Discord, so I can try to find out the cause of the game crash! +- Fixed "Game Speed" (World) not getting applied with the mod menu opened while having another tab selected other than the World tab +I have some things planned for the next updates, but time will decide when I'll be able to work on the updates. I'm almost done with my exams!)" } }; } \ No newline at end of file diff --git a/EGameTools/source/core.cpp b/EGameTools/source/core.cpp index fef118d..06399a5 100644 --- a/EGameTools/source/core.cpp +++ b/EGameTools/source/core.cpp @@ -20,7 +20,7 @@ namespace Core { DWORD prev_mode = 0; const HANDLE hInput = GetStdHandle(STD_INPUT_HANDLE); GetConsoleMode(hInput, &prev_mode); - SetConsoleMode(hInput, prev_mode & ENABLE_EXTENDED_FLAGS); + SetConsoleMode(hInput, prev_mode & ENABLE_EXTENDED_FLAGS); } static FILE* f = nullptr; @@ -43,7 +43,7 @@ namespace Core { int rendererAPI = 0; DWORD gameVer = 0; - static std::counting_semaphore<4> maxHookThreads(4); + std::counting_semaphore<4> maxHookThreads(4); static void LoopHookRenderer() { while (true) { @@ -173,31 +173,37 @@ namespace Core { GamePH::PlayerHealthModule::UpdateClassAddr(); GamePH::PlayerInfectionModule::UpdateClassAddr(); } - /*static bool WriteMiniDump(PEXCEPTION_POINTERS pExceptionPointers) { +#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) return false; MINIDUMP_EXCEPTION_INFORMATION mdei{}; - mdei.ThreadId = GetCurrentThreadId(); mdei.ExceptionPointers = pExceptionPointers; mdei.ClientPointers = false; - int success = MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, MiniDumpNormal, &mdei, nullptr, nullptr); + int success = MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, MiniDumpNormal, (pExceptionPointers ? &mdei : nullptr), nullptr, nullptr); CloseHandle(hFile); return success; - } - static long WINAPI VectoredExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo) { - spdlog::error("VEH threw an exception with code {}. Trying to continue execution, writing mini-dump in the mean time.", ExceptionInfo->ExceptionRecord->ExceptionCode); + } + static long WINAPI CrashHandler(PEXCEPTION_POINTERS ExceptionInfo) { + spdlog::error("Crash Handler threw an exception with code {}. Game is exiting, writing mini-dump in the mean time.", ExceptionInfo->ExceptionRecord->ExceptionCode); + std::string errorMsg = ""; - if (WriteMiniDump(ExceptionInfo)) + if (WriteMiniDump(ExceptionInfo)) { spdlog::info("Mini-dump written to \"EGameTools-dump.dmp\". Please send this to mod author for further help!"); - else + 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 { 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."; + } - return EXCEPTION_CONTINUE_EXECUTION; - }*/ + MessageBoxA(nullptr, errorMsg.c_str(), "Fatal game error", MB_ICONERROR | MB_OK | MB_SETFOREGROUND); + exit(0); + } +#endif static void GameVersionCheck() { try { gameVer = GamePH::GetCurrentGameVersion(); @@ -216,7 +222,9 @@ namespace Core { EnableConsole(); InitLogger(); - //AddVectoredExceptionHandler(0, &VectoredExceptionHandler); +#ifndef EXCP_HANDLER_DISABLE_DEBUG + AddVectoredExceptionHandler(0, &VectoredExceptionHandler); +#endif spdlog::warn("Getting game version"); GameVersionCheck(); @@ -227,23 +235,16 @@ namespace Core { CreateSymlinkForLoadingFiles(); - while (true) { - if (!GetModuleHandle("gamedll_ph_x64_rwdi.dll") || !GetModuleHandle("engine_x64_rwdi.dll")) - continue; + for (auto& hook : *Utils::Hook::HookBase::GetInstances()) { + std::thread([&hook]() { + maxHookThreads.acquire(); - for (auto& hook : *Utils::Hook::HookBase::GetInstances()) { - std::thread([&hook]() { - maxHookThreads.acquire(); + spdlog::warn("Hooking \"{}\"", hook->name.data()); + if (hook->HookLoop()) + spdlog::info("Hooked \"{}\"!", hook->name.data()); - spdlog::warn("Hooking \"{}\"", hook->name.data()); - if (hook->HookLoop()) - spdlog::info("Hooked \"{}\"!", hook->name.data()); - - maxHookThreads.release(); - }).detach(); - } - - break; + maxHookThreads.release(); + }).detach(); } spdlog::warn("Sorting Player Variables"); diff --git a/EGameTools/source/core.h b/EGameTools/source/core.h index 16c31ea..a5ccc0c 100644 --- a/EGameTools/source/core.h +++ b/EGameTools/source/core.h @@ -9,6 +9,7 @@ #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 +17,9 @@ #define VK_MWHEELUP 0x101 #endif -constexpr const char* MOD_VERSION_STR = "v1.2.3"; -constexpr DWORD MOD_VERSION = 10203; -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) {} diff --git a/EGameTools/source/dllmain.cpp b/EGameTools/source/dllmain.cpp index f6f83eb..4569a7f 100644 --- a/EGameTools/source/dllmain.cpp +++ b/EGameTools/source/dllmain.cpp @@ -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,10 +24,11 @@ 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(); - Engine::Hooks::FsOpenHook.HookLoop(); + + std::thread([]() { Core::maxHookThreads.acquire(); Engine::Hooks::MountDataPaksHook.HookLoop(); Core::maxHookThreads.release(); }).detach(); + std::thread([]() { Core::maxHookThreads.acquire(); Engine::Hooks::AuthenticateDataAddNewFileHook.HookLoop(); Core::maxHookThreads.release(); }).detach(); + std::thread([]() { Core::maxHookThreads.acquire(); Engine::Hooks::FsCheckZipCrcHook.HookLoop(); Core::maxHookThreads.release(); }).detach(); + std::thread([]() { Core::maxHookThreads.acquire(); Engine::Hooks::FsOpenHook.HookLoop(); Core::maxHookThreads.release(); }).detach(); DisableThreadLibraryCalls(hModule); hMainThread = CreateThread(nullptr, 0, (LPTHREAD_START_ROUTINE)Core::MainThread, hModule, 0, nullptr); diff --git a/EGameTools/source/game/Engine/CBulletPhysicsCharacter.h b/EGameTools/source/game/Engine/CBulletPhysicsCharacter.h index 9a74242..22a93fe 100644 --- a/EGameTools/source/game/Engine/CBulletPhysicsCharacter.h +++ b/EGameTools/source/game/Engine/CBulletPhysicsCharacter.h @@ -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; diff --git a/EGameTools/source/game/Engine/engine_hooks.cpp b/EGameTools/source/game/Engine/engine_hooks.cpp index 2139e8b..602417a 100644 --- a/EGameTools/source/game/Engine/engine_hooks.cpp +++ b/EGameTools/source/game/Engine/engine_hooks.cpp @@ -2,7 +2,6 @@ #include "..\GamePH\GameDI_PH.h" #include "..\GamePH\LevelDI.h" #include "..\GamePH\gameph_misc.h" -#include "..\GamePH\gen_TPPModel.h" #include "..\core.h" #include "..\menu\camera.h" #include "..\menu\misc.h" diff --git a/EGameTools/source/game/GamePH/GameDI_PH.h b/EGameTools/source/game/GamePH/GameDI_PH.h index fd2e8d1..7689134 100644 --- a/EGameTools/source/game/GamePH/GameDI_PH.h +++ b/EGameTools/source/game/GamePH/GameDI_PH.h @@ -9,7 +9,7 @@ namespace GamePH { public: union { buffer<0x130, SessionCooperativeDI*> pSessionCooperativeDI; - buffer<0x880, bool> blockPauseGameOnPlayerAfk; + buffer<0x908, bool> blockPauseGameOnPlayerAfk; }; float GetGameTimeDelta(); diff --git a/EGameTools/source/game/GamePH/LocalClientDI.h b/EGameTools/source/game/GamePH/LocalClientDI.h index 84ab55c..ffa501d 100644 --- a/EGameTools/source/game/GamePH/LocalClientDI.h +++ b/EGameTools/source/game/GamePH/LocalClientDI.h @@ -2,12 +2,12 @@ #include "..\buffer.h" namespace GamePH { - class gen_TPPModel; + class PlayerObjProperties; class LocalClientDI { public: union { - buffer<0x90, gen_TPPModel*> pgen_TPPModel; + buffer<0x90, PlayerObjProperties*> pPlayerObjProperties; }; static LocalClientDI* Get(); diff --git a/EGameTools/source/game/GamePH/PlayerObjProperties.h b/EGameTools/source/game/GamePH/PlayerObjProperties.h index d9e9695..8485e35 100644 --- a/EGameTools/source/game/GamePH/PlayerObjProperties.h +++ b/EGameTools/source/game/GamePH/PlayerObjProperties.h @@ -12,6 +12,8 @@ namespace GamePH { buffer<0xF0, Engine::CoPhysicsProperty*> pCoPhysicsProperty; //buffer<0x2E80, bool> isOutOfBounds; //buffer<0x2E84, float> outOfBoundsTimer; + buffer<0x35E9, bool> enableTPPModel1; + buffer<0x35EA, bool> enableTPPModel2; }; static PlayerObjProperties* Get(); diff --git a/EGameTools/source/game/GamePH/PlayerState.h b/EGameTools/source/game/GamePH/PlayerState.h index 58ba884..5f6f10a 100644 --- a/EGameTools/source/game/GamePH/PlayerState.h +++ b/EGameTools/source/game/GamePH/PlayerState.h @@ -7,7 +7,7 @@ namespace GamePH { class PlayerState { public: union { - buffer<0x2B0, PlayerVariables*> playerVars; + buffer<0x300, PlayerVariables*> playerVars; }; static PlayerState* Get(); diff --git a/EGameTools/source/game/GamePH/game_hooks.cpp b/EGameTools/source/game/GamePH/game_hooks.cpp index d09804b..a057d77 100644 --- a/EGameTools/source/game/GamePH/game_hooks.cpp +++ b/EGameTools/source/game/GamePH/game_hooks.cpp @@ -11,7 +11,7 @@ #include "LevelDI.h" #include "PlayerHealthModule.h" #include "PlayerInfectionModule.h" -#include "gen_TPPModel.h" +#include "PlayerObjProperties.h" namespace GamePH { namespace Hooks { @@ -161,20 +161,20 @@ namespace GamePH { static Utils::Hook::MHook ShowTPPModelFunc3Hook{ "ShowTPPModelFunc3", &Offsets::Get_ShowTPPModelFunc3, &detourShowTPPModelFunc3 }; static void detourShowTPPModelFunc3(DWORD64 tppFunc2Addr, bool showTPPModel) { - gen_TPPModel* pgen_TPPModel = gen_TPPModel::Get(); - if (!pgen_TPPModel) { + PlayerObjProperties* playerObjProperties = PlayerObjProperties::Get(); + if (!playerObjProperties) { ShowTPPModelFunc3Hook.pOriginal(tppFunc2Addr, showTPPModel); return; } if (!showTPPModel && prevUseTPPModel) { - pgen_TPPModel->enableTPPModel2 = true; - pgen_TPPModel->enableTPPModel1 = true; + playerObjProperties->enableTPPModel2 = true; + playerObjProperties->enableTPPModel1 = true; } ShowTPPModelFunc3Hook.pOriginal(tppFunc2Addr, showTPPModel); if (showTPPModel && prevUseTPPModel) { - pgen_TPPModel->enableTPPModel2 = false; - pgen_TPPModel->enableTPPModel1 = false; + playerObjProperties->enableTPPModel2 = false; + playerObjProperties->enableTPPModel1 = false; } else prevUseTPPModel = showTPPModel; } diff --git a/EGameTools/source/game/GamePH/gameph_misc.cpp b/EGameTools/source/game/GamePH/gameph_misc.cpp index 00216a2..5415436 100644 --- a/EGameTools/source/game/GamePH/gameph_misc.cpp +++ b/EGameTools/source/game/GamePH/gameph_misc.cpp @@ -1,7 +1,7 @@ #include #include "..\offsets.h" #include "GameDI_PH.h" -#include "gen_TPPModel.h" +#include "PlayerObjProperties.h" namespace GamePH { const DWORD GetCurrentGameVersion() { @@ -59,8 +59,8 @@ namespace GamePH { void(*pShowTPPModelFunc3)(DWORD64 tppFunc2Addr, bool showTPPModel) = (decltype(pShowTPPModelFunc3))Offsets::Get_ShowTPPModelFunc3(); if (!pShowTPPModelFunc3) return; - gen_TPPModel* pgen_TPPModel = gen_TPPModel::Get(); - if (!pgen_TPPModel) + PlayerObjProperties* playerObjProperties = PlayerObjProperties::Get(); + if (!playerObjProperties) return; pShowTPPModelFunc3(tppFunc2Addr, showTPPModel); diff --git a/EGameTools/source/game/GamePH/gen_TPPModel.cpp b/EGameTools/source/game/GamePH/gen_TPPModel.cpp deleted file mode 100644 index d534caf..0000000 --- a/EGameTools/source/game/GamePH/gen_TPPModel.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include -#include "LocalClientDI.h" -#include "gen_TPPModel.h" - -namespace GamePH { - gen_TPPModel* gen_TPPModel::Get() { - __try { - LocalClientDI* pLocalClientDI = LocalClientDI::Get(); - if (!pLocalClientDI) - return nullptr; - - gen_TPPModel* ptr = pLocalClientDI->pgen_TPPModel; - if (!Utils::Memory::IsValidPtrMod(ptr, "gamedll_ph_x64_rwdi.dll")) - return nullptr; - - return ptr; - } __except (EXCEPTION_EXECUTE_HANDLER) { - return nullptr; - } - } -} \ No newline at end of file diff --git a/EGameTools/source/game/GamePH/gen_TPPModel.h b/EGameTools/source/game/GamePH/gen_TPPModel.h deleted file mode 100644 index 5b8b49c..0000000 --- a/EGameTools/source/game/GamePH/gen_TPPModel.h +++ /dev/null @@ -1,14 +0,0 @@ - #pragma once -#include "..\buffer.h" - -namespace GamePH { - class gen_TPPModel { - public: - union { - buffer<0x2DB9, bool> enableTPPModel1; - buffer<0x2DBA, bool> enableTPPModel2; - }; - - static gen_TPPModel* Get(); - }; -} \ No newline at end of file diff --git a/EGameTools/source/memscan/memscan.c b/EGameTools/source/memscan/memscan.c new file mode 100644 index 0000000..352dfcd --- /dev/null +++ b/EGameTools/source/memscan/memscan.c @@ -0,0 +1,935 @@ +#include "memscan.h" + +#include // memcmp +#include // 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); +} diff --git a/EGameTools/source/memscan/memscan.h b/EGameTools/source/memscan/memscan.h new file mode 100644 index 0000000..6eeb687 --- /dev/null +++ b/EGameTools/source/memscan/memscan.h @@ -0,0 +1,1037 @@ +#pragma once + +/* Includes */ + +#include + +/* Utility */ + +#include "util/util.h" + +/* Types */ + +typedef enum { + /* the search goes backwards (substracts from current address) */ + MS_FOLLOW_DIRECTION_BACKWARDS = 0, + + /* the search goes forwards (adds to current address) */ + MS_FOLLOW_DIRECTION_FORWARDS +} ms_follow_direction_t; + +typedef enum { + /* won't be reached if MEMSCAN_UNSAFE_OPTIMIZATIONS are on */ + + MS_RESULT_NO_VALID_BYTES_INFO = 0, + + /* finish <= start */ + MS_RESULT_NO_VALID_SPAN, + + /* */ + + /* generic response if not found */ + MS_RESULT_STATUS_NOT_FOUND, + + /* follow led to address being < start and thus was not applied */ + MS_RESULT_STATUS_FOUND_FOLLOW_FAIL_LHS, + + /* follow led to address being > end and thus was not applied */ + MS_RESULT_STATUS_FOUND_FOLLOW_FAIL_RHS, + + /* broke before reaching a match in memory, the former 2 are pioritized */ + MS_RESULT_STATUS_FOUND_FOLLOW_FAIL_INCOMPLETE, + + /* pattern was found, follow has succeeded, status set to FOUND */ + MS_RESULT_STATUS_FOUND +} ms_result_status_t; + +typedef struct { + ms_uptr_t m_address; + ms_result_status_t m_status; +} ms_result_t; + +/* Methods */ + +/* Patterns */ + +/** + * @brief Bytes, Bytes pattern-scanning function + * + * @param start Position to start scanning from in global address space + * @param end Position to stop scanning at in global address space (expected to + * be, at the very least, start + pattern_size, failure to meet this condition + * will be evidenciated in the return) + * @param pattern Array of ubytes to match in memory, may contain + * [k_memscan_wildcard], which allows a mismatch at the opcode's position + * @param pattern_size Size of pattern + * @param pattern_nth_match N-th pattern repetition to select, starts at + * MEMSCAN_FIRST_MATCH + * @param follow_pattern Array of ubytes to look for, from pattern scan + * address, in a specified direction. Spanned from start to end (failures to + * meet conditions will be evidentiated in the return) + * @param follow_pattern_size Size of follow_pattern + * @param follow_nth_match Same as pattern_nth_match, but with the + * follow_pattern + * @param follow_direction refer to ms_follow_direction_t for documentation + * @return Refer to ms_result_t for documentation + */ +MEMSCAN_EXTERN 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); + +/** + * @brief Bytes, String pattern-scanning function + * + * @param start Position to start scanning from in global address space + * @param end Position to stop scanning at in global address space (expected to + * be, at the very least, start + pattern_size, failure to meet this condition + * will be evidenciated in the return) + * @param pattern Array of ubytes to match in memory, may contain + * [k_memscan_wildcard], which allows a mismatch at the opcode's position + * @param pattern_size Size of pattern + * @param pattern_nth_match N-th pattern repetition to select, starts at + * MEMSCAN_FIRST_MATCH + * @param follow_pattern Byte-code style string that will be turned into a + * ms_ubyte_t array for evaluation, example: "8B 4D" + * @param follow_nth_match Same as pattern_nth_match, but with the + * follow_pattern + * @param follow_direction refer to ms_follow_direction_t for documentation + * @return Refer to ms_result_t for documentation + */ +MEMSCAN_EXTERN 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); + +/** + * @brief String, Bytes pattern-scanning function + * + * @param start Position to start scanning from in global address space + * @param end Position to stop scanning at in global address space (expected to + * be, at the very least, start + pattern_size, failure to meet this condition + * will be evidenciated in the return) + * @param pattern Byte-code style string that will be turned into a ms_ubyte_t + * array for evaluation, may contain [k_memscan_wildcard], which allows a + * mismatch at the opcode's position. example: "55 8B EC CC CC CC CC" + * @param pattern_nth_match N-th pattern repetition to select, starts at + * MEMSCAN_FIRST_MATCH + * @param follow_pattern Array of ubytes to look for, from pattern scan + * address, in a specified direction. Spanned from start to end (failures to + * meet conditions will be evidentiated in the return) + * @param follow_pattern_size Size of follow_pattern + * @param follow_nth_match Same as pattern_nth_match, but with the + * follow_pattern + * @param follow_direction refer to ms_follow_direction_t for documentation + * @return Refer to ms_result_t for documentation + */ +MEMSCAN_EXTERN 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); + +/** + * @brief String, String pattern-scanning function + * + * @param start Position to start scanning from in global address space + * @param end Position to stop scanning at in global address space (expected to + * be, at the very least, start + pattern_size, failure to meet this condition + * will be evidenciated in the return) + * @param pattern Byte-code style string that will be turned into a ms_ubyte_t + * array for evaluation, may contain [k_memscan_wildcard], which allows a + * mismatch at the opcode's position. example: "55 8B EC CC CC CC CC" + * @param pattern_nth_match N-th pattern repetition to select, starts at + * MEMSCAN_FIRST_MATCH + * @param follow_pattern Byte-code style string that will be turned into a + * ms_ubyte_t array for evaluation, example: "8B 4D" + * @param follow_nth_match Same as pattern_nth_match, but with the + * follow_pattern + * @param follow_direction refer to ms_follow_direction_t for documentation + * @return Refer to ms_result_t for documentation + */ +MEMSCAN_EXTERN 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); + +/** + * @brief No follow, Bytes pattern-scanning function + * + * @param start Position to start scanning from in global address space + * @param end Position to stop scanning at in global address space (expected to + * be, at the very least, start + pattern_size, failure to meet this condition + * will be evidenciated in the return) + * @param pattern Array of ubytes to match in memory, may contain + * [k_memscan_wildcard], which allows a mismatch at the opcode's position + * @param pattern_size Size of pattern + * @param pattern_nth_match N-th pattern repetition to select, starts at + * MEMSCAN_FIRST_MATCH + * @return Refer to ms_result_t for documentation + */ +MEMSCAN_EXTERN 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); + +/** + * @brief No follow, String pattern-scanning function + * + * @param start Position to start scanning from in global address space + * @param end Position to stop scanning at in global address space (expected to + * be, at the very least, start + pattern_size, failure to meet this condition + * will be evidenciated in the return) + * @param pattern Byte-code style string that will be turned into a ms_ubyte_t + * array for evaluation, may contain [k_memscan_wildcard], which allows a + * mismatch at the opcode's position. example: "55 8B EC CC CC CC CC" + * @param pattern_nth_match N-th pattern repetition to select, starts at + * MEMSCAN_FIRST_MATCH + * @return Refer to ms_result_t for documentation + */ +MEMSCAN_EXTERN 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); + +/** + * @brief Xref Bytes Follow finder from a sequence + * + * @param start Position to start scanning from in global address space + * @param end Position to stop scanning at in global address space (expected to + * be, at the very least, start + MEMSCAN_BYTESET_SIZE, failure to meet this + * condition will be evidenciated in the return) + * @param content Content to find in global address space + * @param nth_match N-th repetition of content, starts at MEMSCAN_FIRST_MATCH + * @param swap_endianness Whether the content should have it's endianness + * swapped + * @param follow_pattern Array of ms_ubyte_t to look for, from xref address, in + * a specified direction. Spanned from start to end (failures to meet conditions + * will be evidentiated in the return) + * @param follow_pattern_size Size of follow_pattern + * @param follow_nth_match Same as nth_match, but with the follow_pattern + * @param follow_direction refer to ms_follow_direction_t for documentation + * @return Refer to ms_result_t for documentation + */ +MEMSCAN_EXTERN 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); + +/** + * @brief Xref Bytes Follow finder from a reference to pointer + * + * @param start Position to start scanning from in global address space + * @param end Position to stop scanning at in global address space (expected to + * be, at the very least, start + MEMSCAN_BYTESET_SIZE, failure to meet this + * condition will be evidenciated in the return) + * @param address Reference to address to find references to in global address + * space + * @param nth_match N-th repetition of reference to select, starts at + * MEMSCAN_FIRST_MATCH, for clarification, matches are looked for from start, + * not from address + * @param swap_endianness Whether the contents at address to have their + * endianness swapped + * @param follow_pattern Array of ubytes to look for, from xref address, in + * a specified direction. Spanned from start to end (failures to meet conditions + * will be evidentiated in the return) + * @param follow_pattern_size Size of follow_pattern + * @param follow_nth_match Same as nth_match, but with the follow_pattern + * @param follow_direction refer to ms_follow_direction_t for documentation + * @return Refer to ms_result_t for documentation + */ +MEMSCAN_EXTERN 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); + +/** + * @brief Xref String Follow finder from a sequence + * + * @param start Position to start scanning from in global address space + * @param end Position to stop scanning at in global address space (expected to + * be, at the very least, start + MEMSCAN_BYTESET_SIZE, failure to meet this + * condition will be evidenciated in the return) + * @param content Content to find in global address space + * @param nth_match N-th repetition of content, starts at MEMSCAN_FIRST_MATCH + * @param swap_endianness Whether the content should have it's endianness + * swapped + * @param follow_pattern Byte-code style string that will be turned into a + * ms_ubyte_t array for evaluation, example: "8B 4D" + * @param follow_nth_match Same as nth_match, but with the follow_pattern + * @param follow_direction refer to ms_follow_direction_t for documentation + * @return Refer to ms_result_t for documentation + */ +MEMSCAN_EXTERN 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); + +/** + * @brief Xref String Follow finder from a reference to pointer + * + * @param start Position to start scanning from in global address space + * @param end Position to stop scanning at in global address space (expected to + * be, at the very least, start + MEMSCAN_BYTESET_SIZE, failure to meet this + * condition will be evidenciated in the return) + * @param address Reference to address to find references to in global address + * space + * @param nth_match N-th repetition of reference to select, starts at + * MEMSCAN_FIRST_MATCH, for clarification, matches are looked for from start, + * not from address + * @param swap_endianness Whether the contents at address to have their + * endianness swapped + * @param follow_pattern Byte-code style string that will be turned into a + * ms_ubyte_t array for evaluation, example: "8B 4D" + * @param follow_nth_match Same as nth_match, but with the follow_pattern + * @param follow_direction refer to ms_follow_direction_t for documentation + * @return Refer to ms_result_t for documentation + */ +MEMSCAN_EXTERN 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); + +/** + * @brief Xref No follow finder from a sequence + * + * @param start Position to start scanning from in global address space + * @param end Position to stop scanning at in global address space (expected to + * be, at the very least, start + MEMSCAN_BYTESET_SIZE, failure to meet this + * condition will be evidenciated in the return) + * @param content Content to find in global address space + * @param nth_match N-th repetition of content, starts at MEMSCAN_FIRST_MATCH + * @param swap_endianness Whether the content should have it's endianness + * swapped + * @return Refer to ms_result_t for documentation + */ +MEMSCAN_EXTERN 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); + +/** + * @brief Xref No follow finder from a reference to pointer + * + * @param start Position to start scanning from in global address space + * @param end Position to stop scanning at in global address space (expected to + * be, at the very least, start + MEMSCAN_BYTESET_SIZE, failure to meet this + * condition will be evidenciated in the return) + * @param address Reference to address to find references to in global address + * space + * @param nth_match N-th repetition of reference to select, starts at + * MEMSCAN_FIRST_MATCH, for clarification, matches are looked for from start, + * not from address + * @param swap_endianness Whether the contents at address to have their + * endianness swapped + * @return Refer to ms_result_t for documentation + */ +MEMSCAN_EXTERN 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); + +/** + * @brief String, Bytes follow finder from string start pointer to reversed + * endianness address scan + * + * @param start Position to start scanning from in global address space + * @param end Position to stop scanning at in global address space (expected to + * be, at the very least, start + MEMSCAN_BYTESET_SIZE, failure to meet this + * condition will be evidenciated in the return) + * @param text Compile-time string to find (should be found in '.rdata'/it's + * variations for Windows, or maybe batched in '.text', etc...) + * @param text_size Size of the 'text' + * @param nth_match N-th reference to the address of the first character of the + * string with reversed endianness in memory + * @param follow_pattern Array of ubytes to look for, from string xref + * address, in a specified direction. Spanned from start to end (failures to + * meet conditions will be evidentiated in the return) + * @param follow_pattern_size Size of follow_pattern + * @param follow_nth_match Same as nth_match, but with the follow_pattern + * @param follow_direction refer to ms_follow_direction_t for documentation + * @return Refer to ms_result_t for documentation + */ +MEMSCAN_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); + +/** + * @brief String, String follow finder from string start pointer to reversed + * endianness address scan + * + * @param start Position to start scanning from in global address space + * @param end Position to stop scanning at in global address space (expected to + * be, at the very least, start + MEMSCAN_BYTESET_SIZE, failure to meet this + * condition will be evidenciated in the return) + * @param text Compile-time string to find (should be found in '.rdata'/it's + * variations for Windows, or maybe batched in '.text', etc...) + * @param text_size Size of the 'text' + * @param nth_match N-th reference to the address of the first character of the + * string with reversed endianness in memory + * @param follow_pattern Byte-code style string that will be turned into a + * ms_ubyte_t array for evaluation, example: "8B 4D" + * @param follow_nth_match Same as nth_match, but with the follow_pattern + * @param follow_direction refer to ms_follow_direction_t for documentation + * @return Refer to ms_result_t for documentation + */ +MEMSCAN_EXTERN 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); + +/** + * @brief String No Follow finder from string start pointer to reversed + * endianness address scan + * + * @param start Position to start scanning from in global address space + * @param end Position to stop scanning at in global address space (expected to + * be, at the very least, start + MEMSCAN_BYTESET_SIZE, failure to meet this + * condition will be evidenciated in the return) + * @param text Compile-time string to find (should be found in '.rdata'/it's + * variations for Windows, or maybe batched in '.text', etc...) + * @param text_size Size of the 'text' + * @param nth_match N-th reference to the address of the first character of the + * string with reversed endianness in memory + * @return Refer to ms_result_t for documentation + */ +MEMSCAN_EXTERN 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); + +/* Detail */ + +static const ms_ubyte_t k_memscan_wildcard = 0xAA; + +/* Constants */ + +#define MEMSCAN_FIRST_MATCH (0) + +/* Wrapper */ + +#if __cplusplus +#include + +#ifdef MEMSCAN_CPP_EXCEPTIONS +#include +#endif + +#ifdef MEMSCAN_CPP_EXCEPTIONS +#define MEMSCAN_CPP_NOEXCEPT noexcept(false) +#else +#define MEMSCAN_CPP_NOEXCEPT noexcept(true) +#endif +#include +#ifdef _WIN32 +#include +#include +#include +#endif + +namespace memscan +{ +#ifdef _WIN32 +[[nodiscard]] inline static auto +NtCurrentPeb() noexcept +{ + return NtCurrentTeb()->ProcessEnvironmentBlock; +} +#endif + +struct mapped_region_t { + /* constructors */ + + explicit mapped_region_t() = delete; + + [[nodiscard]] mapped_region_t(const ms_uptr_t start, + const ms_uptr_t end) noexcept + : m_start(start), + m_end(end) + { + } + +#ifdef _WIN32 + [[nodiscard]] mapped_region_t(const HMODULE module) MEMSCAN_CPP_NOEXCEPT + : m_start(reinterpret_cast(module)) + { + const auto* const dos_header = + reinterpret_cast(m_start); + +#if !MEMSCAN_UNSAFE_OPTIMIZATIONS + if (dos_header == nullptr) { +#ifdef MEMSCAN_CPP_EXCEPTIONS + throw std::runtime_error( + "Failed initializing module: invalid DOS header"); +#endif + return; + } +#endif + + const auto* const nt_headers = reinterpret_cast( + m_start + static_cast(dos_header->e_lfanew)); + +#if !MEMSCAN_UNSAFE_OPTIMIZATIONS + if (nt_headers == nullptr) { +#ifdef MEMSCAN_CPP_EXCEPTIONS + throw std::runtime_error( + "Failed initializing module: invalid NT headers"); +#endif + return; + } +#endif + + m_end = m_start + + static_cast(nt_headers->OptionalHeader.SizeOfImage); + } +#endif + +#ifdef _WIN32 + [[nodiscard]] mapped_region_t(const std::wstring_view module_in_list) + MEMSCAN_CPP_NOEXCEPT + { + const auto* const ldr = NtCurrentPeb()->Ldr; + +#if !MEMSCAN_UNSAFE_OPTIMIZATIONS + if (ldr == nullptr) { +#ifdef MEMSCAN_CPP_EXCEPTIONS + throw std::runtime_error( + "Failed initializing module: invalid PEB Ldr"); +#endif + return; + } +#endif + + const auto* const head = reinterpret_cast( + reinterpret_cast(ldr) + sizeof(LIST_ENTRY)); + const auto* curr = head->Flink; + while (curr != nullptr && curr != head) { + const auto* const data = + reinterpret_cast(curr); + if (data->FullDllName.Length == 0) { + curr = curr->Flink; + continue; + } + + // `Length` is more rather a ptrdiff from buffer, not the actual + // normal definition of length. + const auto module_name = std::wstring_view{ + data->FullDllName.Buffer, + data->FullDllName.Length / sizeof *data->FullDllName.Buffer}; + if (module_name.ends_with(module_in_list)) { + (*this) = + mapped_region_t(static_cast(data->DllBase)); + return; + } + + curr = curr->Flink; + } + +#ifdef MEMSCAN_CPP_EXCEPTIONS + throw std::runtime_error( + "Failed initializing module: Unable to find module in list"); +#endif + } +#endif + + /* implementations */ + + /* C-style casts are used for automatic cast deduction */ + + template + static std::optional + find_pattern(const ms_uptr_t start, const ms_uptr_t end, + const ms_ubyte_t (&pattern)[N], const ms_usize_t nth_match, + const ms_ubyte_t (&follow)[N2], + const ms_usize_t follow_nth_match, + const ms_follow_direction_t follow_direction) noexcept + { + auto&& result = + memscan_find_pattern_bb(start, end, pattern, N, nth_match, follow, + N2, follow_nth_match, follow_direction); + + constexpr auto status = AcceptFailedFollow + ? MS_RESULT_STATUS_FOUND_FOLLOW_FAIL_LHS + : MS_RESULT_STATUS_FOUND; + if (result.m_status >= status) + return (T)result.m_address; + + return std::nullopt; + } + + template + static std::optional + find_pattern(const ms_uptr_t start, const ms_uptr_t end, + const std::string_view pattern, const ms_usize_t nth_match, + const ms_ubyte_t (&follow)[N], + const ms_usize_t follow_nth_match, + const ms_follow_direction_t follow_direction) noexcept + { + auto&& result = memscan_find_pattern_sb( + start, end, pattern.data(), nth_match, follow, N, follow_nth_match, + follow_direction); + + constexpr auto status = AcceptFailedFollow + ? MS_RESULT_STATUS_FOUND_FOLLOW_FAIL_LHS + : MS_RESULT_STATUS_FOUND; + if (result.m_status >= status) + return (T)result.m_address; + + return std::nullopt; + } + + template + static std::optional + find_pattern(const ms_uptr_t start, const ms_uptr_t end, + const ms_ubyte_t (&pattern)[N], const ms_usize_t nth_match, + const std::string_view follow, + const ms_usize_t follow_nth_match, + const ms_follow_direction_t follow_direction) noexcept + { + auto&& result = memscan_find_pattern_bs( + start, end, pattern, N, nth_match, follow.data(), follow_nth_match, + follow_direction); + + constexpr auto status = AcceptFailedFollow + ? MS_RESULT_STATUS_FOUND_FOLLOW_FAIL_LHS + : MS_RESULT_STATUS_FOUND; + if (result.m_status >= status) + return (T)result.m_address; + + return std::nullopt; + } + + template + static std::optional + find_pattern(const ms_uptr_t start, const ms_uptr_t end, + const std::string_view pattern, const ms_usize_t nth_match, + const std::string_view follow, + const ms_usize_t follow_nth_match, + const ms_follow_direction_t follow_direction) noexcept + { + auto&& result = memscan_find_pattern_ss( + start, end, pattern.data(), nth_match, follow.data(), + follow_nth_match, follow_direction); + + constexpr auto status = AcceptFailedFollow + ? MS_RESULT_STATUS_FOUND_FOLLOW_FAIL_LHS + : MS_RESULT_STATUS_FOUND; + if (result.m_status >= status) + return (T)result.m_address; + + return std::nullopt; + } + + template + static std::optional + find_pattern(const ms_uptr_t start, const ms_uptr_t end, + const ms_ubyte_t (&pattern)[N], + const ms_usize_t nth_match = MEMSCAN_FIRST_MATCH) noexcept + { + auto&& result = + memscan_find_pattern_nfb(start, end, pattern, N, nth_match); + + if (result.m_status == MS_RESULT_STATUS_FOUND) + return (T)result.m_address; + + return std::nullopt; + } + + template + static std::optional + find_pattern(const ms_uptr_t start, const ms_uptr_t end, + const std::string_view pattern, + const ms_usize_t nth_match = MEMSCAN_FIRST_MATCH) noexcept + { + auto&& result = + memscan_find_pattern_nfs(start, end, pattern.data(), nth_match); + + if (result.m_status == MS_RESULT_STATUS_FOUND) + return (T)result.m_address; + + return std::nullopt; + } + + template + static std::optional + find_xref(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)[N], + const ms_usize_t follow_nth_match, + const ms_follow_direction_t follow_direction) noexcept + + { + auto&& result = memscan_find_xref_b( + start, end, content, content_nth_match, swap_endianness, follow, N, + follow_nth_match, follow_direction); + + constexpr auto status = AcceptFailedFollow + ? MS_RESULT_STATUS_FOUND_FOLLOW_FAIL_LHS + : MS_RESULT_STATUS_FOUND; + if (result.m_status >= status) + return (T)result.m_address; + + return std::nullopt; + } + + template + static std::optional + find_xref(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 std::string_view follow, + const ms_usize_t follow_nth_match, + const ms_follow_direction_t follow_direction) noexcept + + { + auto&& result = memscan_find_xref_s( + start, end, content, content_nth_match, swap_endianness, + follow.data(), follow_nth_match, follow_direction); + + constexpr auto status = AcceptFailedFollow + ? MS_RESULT_STATUS_FOUND_FOLLOW_FAIL_LHS + : MS_RESULT_STATUS_FOUND; + if (result.m_status >= status) + return (T)result.m_address; + + return std::nullopt; + } + + template + static std::optional + find_xref(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) noexcept + { + auto&& result = memscan_find_xref_nf( + start, end, content, content_nth_match, swap_endianness); + + if (result.m_status == MS_RESULT_STATUS_FOUND) + return (T)result.m_address; + + return std::nullopt; + } + + template + static std::optional + find_xref_at(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)[N], + const ms_usize_t follow_nth_match, + const ms_follow_direction_t follow_direction) noexcept + + { + auto&& result = memscan_find_xref_at_b( + start, end, address, nth_match, swap_endianness, follow, N, + follow_nth_match, follow_direction); + + constexpr auto status = AcceptFailedFollow + ? MS_RESULT_STATUS_FOUND_FOLLOW_FAIL_LHS + : MS_RESULT_STATUS_FOUND; + if (result.m_status >= status) + return (T)result.m_address; + + return std::nullopt; + } + + template + static std::optional + find_xref_at(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 std::string_view follow, + const ms_usize_t follow_nth_match, + const ms_follow_direction_t follow_direction) noexcept + + { + auto&& result = memscan_find_xref_at_s( + start, end, address, nth_match, swap_endianness, follow.data(), + follow_nth_match, follow_direction); + + constexpr auto status = AcceptFailedFollow + ? MS_RESULT_STATUS_FOUND_FOLLOW_FAIL_LHS + : MS_RESULT_STATUS_FOUND; + if (result.m_status >= status) + return (T)result.m_address; + + return std::nullopt; + } + + template + static std::optional + find_xref_at(const ms_uptr_t start, const ms_uptr_t end, + const ms_uptr_t address, const ms_usize_t nth_match, + bool swap_endianness) noexcept + { + auto&& result = memscan_find_xref_at_nf(start, end, address, nth_match, + swap_endianness); + + if (result.m_status == MS_RESULT_STATUS_FOUND) + return (T)result.m_address; + + return std::nullopt; + } + + template + static std::optional + find_string(const ms_uptr_t start, const ms_uptr_t end, + const std::string_view string, const ms_usize_t nth_match, + const ms_ubyte_t (&follow)[N], + const ms_usize_t follow_nth_match, + const ms_follow_direction_t follow_direction) noexcept + { + auto&& result = memscan_find_string_b( + start, end, string.data(), string.size(), nth_match, follow, N, + follow_nth_match, follow_direction); + + constexpr auto status = AcceptFailedFollow + ? MS_RESULT_STATUS_FOUND_FOLLOW_FAIL_LHS + : MS_RESULT_STATUS_FOUND; + if (result.m_status >= status) + return (T)result.m_address; + + return std::nullopt; + } + + template + static std::optional + find_string(const ms_uptr_t start, const ms_uptr_t end, + const std::string_view string, const ms_usize_t nth_match, + const std::string_view follow, + const ms_usize_t follow_nth_match, + const ms_follow_direction_t follow_direction) noexcept + { + auto&& result = memscan_find_string_s( + start, end, string.data(), string.size(), nth_match, follow.data(), + follow_nth_match, follow_direction); + + constexpr auto status = AcceptFailedFollow + ? MS_RESULT_STATUS_FOUND_FOLLOW_FAIL_LHS + : MS_RESULT_STATUS_FOUND; + if (result.m_status >= status) + return (T)result.m_address; + + return std::nullopt; + } + + template + static std::optional + find_string(const ms_uptr_t start, const ms_uptr_t end, + const std::string_view string, + const ms_usize_t nth_match = MEMSCAN_FIRST_MATCH) noexcept + { + auto&& result = memscan_find_string_nf(start, end, string.data(), + string.size(), nth_match); + + if (result.m_status >= MS_RESULT_STATUS_FOUND) + return (T)result.m_address; + + return std::nullopt; + } + + /* injections */ + + template + std::optional + find_pattern(const ms_ubyte_t (&pattern)[N], const ms_usize_t nth_match, + const ms_ubyte_t (&follow)[N2], + const ms_usize_t follow_nth_match, + const ms_follow_direction_t follow_direction) const noexcept + { + return find_pattern( + m_start, m_end, pattern, nth_match, follow, follow_nth_match, + follow_direction); + } + + template + std::optional + find_pattern(const std::string_view pattern, const ms_usize_t nth_match, + const ms_ubyte_t (&follow)[N], + const ms_usize_t follow_nth_match, + const ms_follow_direction_t follow_direction) const noexcept + { + return find_pattern( + m_start, m_end, pattern, nth_match, follow, follow_nth_match, + follow_direction); + } + + template + std::optional + find_pattern(const ms_ubyte_t (&pattern)[N], const ms_usize_t nth_match, + const std::string_view follow, + const ms_usize_t follow_nth_match, + const ms_follow_direction_t follow_direction) const noexcept + { + return find_pattern( + m_start, m_end, pattern, nth_match, follow, follow_nth_match, + follow_direction); + } + + template + std::optional + find_pattern(const std::string_view pattern, const ms_usize_t nth_match, + const std::string_view follow, + const ms_usize_t follow_nth_match, + const ms_follow_direction_t follow_direction) const noexcept + { + return find_pattern( + m_start, m_end, pattern, nth_match, follow, follow_nth_match, + follow_direction); + } + + template + std::optional + find_pattern(const ms_ubyte_t (&pattern)[N], + const ms_usize_t match = MEMSCAN_FIRST_MATCH) const noexcept + { + return find_pattern(m_start, m_end, pattern, match); + } + + template + std::optional find_pattern( + const std::string_view pattern, + const ms_usize_t nth_match = MEMSCAN_FIRST_MATCH) const noexcept + { + return find_pattern(m_start, m_end, pattern, nth_match); + } + + template + std::optional + find_xref(const ms_uptr_t content, const ms_usize_t content_nth_match, + bool swap_endianness, const ms_ubyte_t (&follow)[N], + const ms_usize_t follow_nth_match, + const ms_follow_direction_t follow_direction) const noexcept + + { + return find_xref( + content, content_nth_match, swap_endianness, follow, + follow_nth_match, follow_direction); + } + + template + std::optional + find_xref(const ms_uptr_t content, const ms_usize_t content_nth_match, + bool swap_endianness, const std::string_view follow, + const ms_usize_t follow_nth_match, + const ms_follow_direction_t follow_direction) const noexcept + + { + return find_xref( + m_start, m_end, content, content_nth_match, swap_endianness, follow, + follow_nth_match, follow_direction); + } + + template + std::optional find_xref(const ms_uptr_t content, + const ms_usize_t content_nth_match, + bool swap_endianness) const noexcept + { + return find_xref(m_start, m_end, content, content_nth_match, + swap_endianness); + } + + template + std::optional + find_xref_at(const ms_uptr_t address, const ms_usize_t nth_match, + bool swap_endianness, const ms_ubyte_t (&follow)[N], + const ms_usize_t follow_nth_match, + const ms_follow_direction_t follow_direction) const noexcept + + { + return find_xref_at( + m_start, m_end, address, nth_match, swap_endianness, follow, + follow_nth_match, follow_direction); + } + + template + std::optional + find_xref_at(const ms_uptr_t address, const ms_usize_t nth_match, + bool swap_endianness, const std::string_view follow, + const ms_usize_t follow_nth_match, + const ms_follow_direction_t follow_direction) const noexcept + + { + return find_xref_at( + m_start, m_end, address, nth_match, swap_endianness, follow, + follow_nth_match, follow_direction); + } + + template + std::optional find_xref_at(const ms_uptr_t address, + const ms_usize_t nth_match, + bool swap_endianness) const noexcept + { + return find_xref_at(m_start, m_end, address, nth_match, + swap_endianness); + } + + template + std::optional + find_string(const std::string_view string, const ms_usize_t nth_match, + const ms_ubyte_t (&follow)[N], + const ms_usize_t follow_nth_match, + const ms_follow_direction_t follow_direction) const noexcept + { + return find_string( + m_start, m_end, string, nth_match, follow, follow_nth_match, + follow_direction); + } + + template + std::optional + find_string(const std::string_view string, const ms_usize_t nth_match, + const std::string_view follow, + const ms_usize_t follow_nth_match, + const ms_follow_direction_t follow_direction) const noexcept + { + return find_string( + m_start, m_end, string, nth_match, follow, follow_nth_match, + follow_direction); + } + + template + std::optional + find_string(const std::string_view string, + const ms_usize_t nth_match = MEMSCAN_FIRST_MATCH) const noexcept + { + return find_string(m_start, m_end, string, nth_match); + } + + /* helpers */ + + const auto get_start() const noexcept { return m_start; } + const auto get_end() const noexcept { return m_end; } + +private: + ms_uptr_t m_start{}; + ms_uptr_t m_end{}; +}; +} // namespace memscan +#endif \ No newline at end of file diff --git a/EGameTools/source/memscan/util/util.c b/EGameTools/source/memscan/util/util.c new file mode 100644 index 0000000..a1cd1b8 --- /dev/null +++ b/EGameTools/source/memscan/util/util.c @@ -0,0 +1,102 @@ +#include "util.h" +#include +#include + +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, ¤t, 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; +} \ No newline at end of file diff --git a/EGameTools/source/memscan/util/util.h b/EGameTools/source/memscan/util/util.h new file mode 100644 index 0000000..9d24139 --- /dev/null +++ b/EGameTools/source/memscan/util/util.h @@ -0,0 +1,88 @@ +#pragma once + +/* Includes */ + +#include +#include +#include + +/* 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) \ No newline at end of file diff --git a/EGameTools/source/menu/camera.cpp b/EGameTools/source/menu/camera.cpp index 53cc232..5667914 100644 --- a/EGameTools/source/menu/camera.cpp +++ b/EGameTools/source/menu/camera.cpp @@ -6,8 +6,8 @@ #include "..\game\GamePH\GameDI_PH.h" #include "..\game\GamePH\LevelDI.h" #include "..\game\GamePH\PlayerVariables.h" +#include "..\game\GamePH\PlayerObjProperties.h" #include "..\game\GamePH\gameph_misc.h" -#include "..\game\GamePH\gen_TPPModel.h" #include "..\offsets.h" #include "camera.h" #include "menu.h" @@ -149,8 +149,8 @@ namespace Menu { if (!iLevel || !iLevel->IsLoaded()) return; - GamePH::gen_TPPModel* pgen_TPPModel = GamePH::gen_TPPModel::Get(); - if (pgen_TPPModel) { + GamePH::PlayerObjProperties* playerObjProperties = GamePH::PlayerObjProperties::Get(); + if (playerObjProperties) { if (Menu::Camera::freeCam.GetValue() && !iLevel->IsTimerFrozen()) GamePH::ShowTPPModel(true); else if (Menu::Camera::freeCam.GetValue() && iLevel->IsTimerFrozen() && !photoMode.GetValue()) diff --git a/EGameTools/source/menu/debug.cpp b/EGameTools/source/menu/debug.cpp index c04a3c1..a82631a 100644 --- a/EGameTools/source/menu/debug.cpp +++ b/EGameTools/source/menu/debug.cpp @@ -16,7 +16,6 @@ #include "..\game\GamePH\SessionCooperativeDI.h" #include "..\game\GamePH\TPPCameraDI.h" #include "..\game\GamePH\TimeWeather\CSystem.h" -#include "..\game\GamePH\gen_TPPModel.h" #include "..\game\Engine\CBulletPhysicsCharacter.h" #include "..\game\Engine\CGSObject.h" @@ -37,7 +36,6 @@ namespace Menu { { "FreeCamera", reinterpret_cast(&GamePH::FreeCamera::Get) }, { "GameDI_PH", reinterpret_cast(&GamePH::GameDI_PH::Get) }, { "GameDI_PH2", reinterpret_cast(&GamePH::GameDI_PH2::Get) }, - { "gen_TPPModel", reinterpret_cast(&GamePH::gen_TPPModel::Get) }, { "LevelDI", reinterpret_cast(&GamePH::LevelDI::Get) }, { "LocalClientDI", reinterpret_cast(&GamePH::LocalClientDI::Get) }, { "LogicalPlayer", reinterpret_cast(&GamePH::LogicalPlayer::Get) }, diff --git a/EGameTools/source/menu/menu.cpp b/EGameTools/source/menu/menu.cpp index fe0f763..ed70058 100644 --- a/EGameTools/source/menu/menu.cpp +++ b/EGameTools/source/menu/menu.cpp @@ -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(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(); diff --git a/EGameTools/source/menu/menu.h b/EGameTools/source/menu/menu.h index d99d0c0..4806025 100644 --- a/EGameTools/source/menu/menu.h +++ b/EGameTools/source/menu/menu.h @@ -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(); } \ No newline at end of file diff --git a/EGameTools/source/menu/world.cpp b/EGameTools/source/menu/world.cpp index 0c0ec71..599cb16 100644 --- a/EGameTools/source/menu/world.cpp +++ b/EGameTools/source/menu/world.cpp @@ -89,7 +89,7 @@ namespace Menu { if (freezeTime.GetValue() && !Utils::Values::are_samef(time, timeBeforeFreeze, 0.0095f)) dayNightCycle->SetDaytime(timeBeforeFreeze); - if (!menuToggle.GetValue()) { + if (!menuToggle.GetValue() || Menu::currentTabIndex != World::Tab::tabIndex) { if (!slowMotion.GetValue() && !slowMotion.HasChanged() && !Utils::Values::are_samef(gameSpeed, 1.0f)) iLevel->TimerSetSpeedUp(gameSpeed); actualGameSpeed = iLevel->TimerGetSpeedUp(); diff --git a/EGameTools/source/offsets.h b/EGameTools/source/offsets.h index 932cb67..55fbf36 100644 --- a/EGameTools/source/offsets.h +++ b/EGameTools/source/offsets.h @@ -6,7 +6,7 @@ static retType Get_## name () {\ static retType name = NULL;\ static int i = 0;\ - if (Utils::Memory::IsValidPtr(name) || i >= 10) return name;\ + if (Utils::Memory::IsValidPtr(name) || !GetModuleHandleA(moduleName) || i >= 10) return name;\ i++;\ return name=reinterpret_cast(Utils::SigScan::PatternScanner::FindPattern(moduleName, {pattern, type}));\ } @@ -20,14 +20,14 @@ static DWORD64 Get_## name () {\ static DWORD64 Get_## name () {\ static DWORD64 name = 0;\ if (Utils::Memory::IsValidPtr(name)) return name;\ - return name=reinterpret_cast(GetModuleHandle(moduleName)) + static_cast(off);\ + return name=reinterpret_cast(GetModuleHandleA(moduleName)) + static_cast(off);\ } #define AddVTOffset(name, moduleName, rttiName, retType)\ static retType GetVT_## name () {\ static retType VT_## name = NULL;\ static int i = 0;\ - if (Utils::Memory::IsValidPtr(VT_## name)) return VT_## name;\ + if (Utils::Memory::IsValidPtr(VT_## name)|| !GetModuleHandleA(moduleName) || i >= 10) return VT_## name;\ i++;\ return VT_## name=reinterpret_cast(Utils::RTTI::GetVTablePtr(moduleName, rttiName));\ } @@ -42,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) diff --git a/EGameTools/source/utils/hook.h b/EGameTools/source/utils/hook.h index 8bfa94f..fce2e43 100644 --- a/EGameTools/source/utils/hook.h +++ b/EGameTools/source/utils/hook.h @@ -40,11 +40,11 @@ namespace Utils { bool HookLoop() override { if (hooked || (optionRef && !optionRef->GetValue())) return true; - timeSpentHooking = Utils::Time::Timer(160000); + timeSpentHooking = Utils::Time::Timer(120000); while (true) { if (timeSpentHooking.DidTimePass()) { - spdlog::error("Failed hooking \"{}\" after 160 seconds", name); + spdlog::error("Failed hooking \"{}\" after 120 seconds", name); return false; } if (!pGetOffsetFunc || !pGetOffsetFunc()) @@ -62,6 +62,8 @@ namespace Utils { VirtualProtect(pGetOffsetFunc(), bytesAmount, originalProtection, &oldProtection); hooked = true; return true; + + Sleep(10); } } @@ -111,7 +113,7 @@ namespace Utils { bool hooked = false; - Utils::Time::Timer timeSpentHooking{ 160000 }; + Utils::Time::Timer timeSpentHooking{ 120000 }; }; template class MHook : HookBase { @@ -121,11 +123,11 @@ namespace Utils { bool HookLoop() override { if (pOriginal) return true; - timeSpentHooking = Utils::Time::Timer(160000); + timeSpentHooking = Utils::Time::Timer(120000); while (true) { if (timeSpentHooking.DidTimePass()) { - spdlog::error("Failed hooking function \"{}\" after 160 seconds", name); + spdlog::error("Failed hooking function \"{}\" after 120 seconds", name); return false; } if (!pGetOffsetFunc) @@ -137,6 +139,8 @@ namespace Utils { MH_EnableHook(pTarget); return true; } + + Sleep(10); } } @@ -146,7 +150,7 @@ namespace Utils { GetTargetOffsetRetType(*pGetOffsetFunc)() = nullptr; OrigType pDetour = nullptr; - Utils::Time::Timer timeSpentHooking{ 160000 }; + Utils::Time::Timer timeSpentHooking{ 120000 }; }; template class VTHook : HookBase { @@ -156,11 +160,11 @@ namespace Utils { bool HookLoop() override { if (pOriginal) return true; - timeSpentHooking = Utils::Time::Timer(160000); + timeSpentHooking = Utils::Time::Timer(120000); while (true) { if (timeSpentHooking.DidTimePass()) { - spdlog::error("Failed hooking function \"{}\" after 160 seconds", name); + spdlog::error("Failed hooking function \"{}\" after 120 seconds", name); return false; } if (!pGetOffsetFunc) @@ -172,6 +176,8 @@ namespace Utils { HookVT(pInstance, pDetour, reinterpret_cast(&pOriginal), offset); return true; } + + Sleep(10); } } @@ -181,7 +187,7 @@ namespace Utils { LPVOID pInstance = nullptr; OrigType pDetour = nullptr; - Utils::Time::Timer timeSpentHooking{ 160000 }; + Utils::Time::Timer timeSpentHooking{ 120000 }; DWORD offset = 0x0; }; diff --git a/EGameTools/source/utils/memory.cpp b/EGameTools/source/utils/memory.cpp index 9599b47..f9125c8 100644 --- a/EGameTools/source/utils/memory.cpp +++ b/EGameTools/source/utils/memory.cpp @@ -37,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 << "??"; @@ -54,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(bytesCounted); + continue; + } + + if (pattern[i] == '?') { + patt += SigScanWildCardStr; + i += (pattern[static_cast(i) + 1] == '?') ? 2 : 1; + } else { + patt.push_back(pattern[i]); + i++; + } + } + + return patt; + } DWORD64 CalcTargetAddrOfRelInst(DWORD64 addrOfInst, size_t opSize) { int offset = *reinterpret_cast(addrOfInst + opSize); @@ -62,7 +90,7 @@ namespace Utils { std::vector GetXrefsTo(DWORD64 address, DWORD64 start, size_t size) { std::vector xrefs = {}; - const std::string idaPattern = bytesToIDAPattern(reinterpret_cast(&address), 8); + const std::string idaPattern = BytesToIDAPattern(reinterpret_cast(&address), 8); const DWORD64 end = start + size; while (start && start < end) { diff --git a/EGameTools/source/utils/memory.h b/EGameTools/source/utils/memory.h index 384c863..45c38b7 100644 --- a/EGameTools/source/utils/memory.h +++ b/EGameTools/source/utils/memory.h @@ -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 GetXrefsTo(DWORD64 address, DWORD64 start, size_t size); diff --git a/EGameTools/source/utils/rtti.cpp b/EGameTools/source/utils/rtti.cpp index f4b2a62..9b79493 100644 --- a/EGameTools/source/utils/rtti.cpp +++ b/EGameTools/source/utils/rtti.cpp @@ -1,45 +1,30 @@ -#include "pch.h" +#include namespace Utils { namespace RTTI { - static std::string bytesToIDAPattern(BYTE* bytes, size_t size) { - std::stringstream idaPattern; - idaPattern << std::hex << std::uppercase << std::setfill('0'); - - for (size_t i = 0; i < size; i++) { - const int currentByte = bytes[i]; - if (currentByte != 255) - idaPattern << std::setw(2) << currentByte; - else - idaPattern << "??"; - - if (i != size - 1) - idaPattern << " "; - } - - return idaPattern.str(); - } static std::vector getXrefsTo(DWORD64 moduleBaseAddr, DWORD64 address, DWORD64 start, size_t size) { std::vector xrefs = {}; const DWORD64 finalOffset = address - moduleBaseAddr; - const std::string idaPattern = bytesToIDAPattern(reinterpret_cast(const_cast(&finalOffset)), 4); + const std::string idaPattern = Memory::BytesToIDAPattern(reinterpret_cast(const_cast(&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(Utils::SigScan::PatternScanner::FindPattern(reinterpret_cast(start), size, { idaPattern.c_str(), Utils::SigScan::PatternType::Address})); + const auto scanner = memscan::mapped_region_t(start, end); + auto patternFind = scanner.find_pattern(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(const_cast(typeDescriptorName.data())), typeDescriptorName.size()); + std::string idaPattern = Memory::BytesToIDAPattern(reinterpret_cast(const_cast(typeDescriptorName.data())), typeDescriptorName.size()); DWORD64 rttiTypeDescriptor = reinterpret_cast(Utils::SigScan::PatternScanner::FindPattern(moduleName, { idaPattern.c_str(), Utils::SigScan::PatternType::Address })); if (!rttiTypeDescriptor) @@ -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(const_cast(&objectLocator)), 8); + idaPattern = Memory::BytesToIDAPattern(reinterpret_cast(const_cast(&objectLocator)), 8); const DWORD64 vtableAddr = reinterpret_cast(Utils::SigScan::PatternScanner::FindPattern(reinterpret_cast(rdataStart), rdataSize, { idaPattern.c_str(), Utils::SigScan::PatternType::Address })) + 0x8; diff --git a/EGameTools/source/utils/sigscan.cpp b/EGameTools/source/utils/sigscan.cpp index f4ba66f..35807ca 100644 --- a/EGameTools/source/utils/sigscan.cpp +++ b/EGameTools/source/utils/sigscan.cpp @@ -38,109 +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(malloc(byteCount + 1)); - if (!patt) - return nullptr; - - BYTE* mask = reinterpret_cast(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(startAddress), reinterpret_cast(startAddress) + searchSize); + auto patternFind = scanner.find_pattern(patt); LPVOID ret = nullptr; - const DWORD64 retAddress = reinterpret_cast(startAddress); - const DWORD64 endAddress = retAddress + searchSize; - size_t searchLen = bytesCounted; - BYTE* retAddressPtr = reinterpret_cast(retAddress); - BYTE* endAddressPtr = reinterpret_cast(endAddress); - - while (retAddressPtr < endAddressPtr) { - MEMORY_BASIC_INFORMATION mbi; - if (VirtualQuery(retAddressPtr, &mbi, sizeof(mbi))) { - // Check if the memory region is readable - if (mbi.Protect & (PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_READONLY | PAGE_READWRITE)) { - // Adjust the end address to the end of this memory region - BYTE* regionEnd = reinterpret_cast(mbi.BaseAddress) + mbi.RegionSize; - - // Only scan within this region - while (retAddressPtr < regionEnd && retAddressPtr < endAddressPtr) { - bool found = true; - - for (size_t j = 0; j < searchLen; j++) { - if (mask[j] == 'x' && retAddressPtr[j] != patt[j]) { - found = false; - break; - } - } - - if (found) { - ret = reinterpret_cast(retAddressPtr + offset); - break; - } - - retAddressPtr++; - } - - if (ret != nullptr) - break; - } else - // Skip the non-readable memory region - retAddressPtr = reinterpret_cast(mbi.BaseAddress) + mbi.RegionSize; - } else - retAddressPtr++; - } - - free(patt); - free(mask); + if (patternFind.has_value()) + ret = reinterpret_cast(patternFind.value() + offset); switch (pattern.type) { case PatternType::Pointer: