- Added compatibility with v1.19 Tower Raid: Halloween Run update

- Improved CPU performance at game startup when using the mod
This commit is contained in:
EricPlayZ
2024-11-03 17:53:49 +02:00
parent 3280768125
commit 0ee417db70
32 changed files with 2340 additions and 246 deletions

View File

@ -35,7 +35,6 @@
<ClCompile Include="source\game\GamePH\FreeCamera.cpp" />
<ClCompile Include="source\game\GamePH\GameDI_PH.cpp" />
<ClCompile Include="source\game\GamePH\GameDI_PH2.cpp" />
<ClCompile Include="source\game\GamePH\gen_TPPModel.cpp" />
<ClCompile Include="source\game\GamePH\game_hooks.cpp" />
<ClCompile Include="source\game\GamePH\InventoryContainerDI.cpp" />
<ClCompile Include="source\game\GamePH\InventoryItem.cpp" />
@ -93,6 +92,14 @@
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="source\kiero.cpp" />
<ClCompile Include="source\memscan\memscan.c">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="source\memscan\util\util.c">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="source\menu\camera.cpp" />
<ClCompile Include="source\menu\debug.cpp" />
<ClCompile Include="source\menu\init.cpp" />
@ -140,7 +147,6 @@
<ClInclude Include="source\game\GamePH\FreeCamera.h" />
<ClInclude Include="source\game\GamePH\GameDI_PH.h" />
<ClInclude Include="source\game\GamePH\GameDI_PH2.h" />
<ClInclude Include="source\game\GamePH\gen_TPPModel.h" />
<ClInclude Include="source\game\GamePH\InventoryContainerDI.h" />
<ClInclude Include="source\game\GamePH\InventoryItem.h" />
<ClInclude Include="source\game\GamePH\InventoryMoney.h" />
@ -179,6 +185,8 @@
<ClInclude Include="source\ImGui\imstb_truetype.h" />
<ClInclude Include="source\ImGui\misc\freetype\imgui_freetype.h" />
<ClInclude Include="source\kiero.h" />
<ClInclude Include="source\memscan\memscan.h" />
<ClInclude Include="source\memscan\util\util.h" />
<ClInclude Include="source\menu\camera.h" />
<ClInclude Include="source\menu\debug.h" />
<ClInclude Include="source\menu\init.h" />
@ -271,7 +279,7 @@
</Optimization>
<IntrinsicFunctions>
</IntrinsicFunctions>
<AdditionalIncludeDirectories>pch;source\spdlog\include;source\ImGui\freetype\include;source\ImGui;source\MinHook;</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>pch;source\spdlog\include;source\ImGui\freetype\include;source\ImGui;source\MinHook;source\memscan;</AdditionalIncludeDirectories>
<PrecompiledHeaderOutputFile>$(IntDir)$(TargetName)_$(PlatformTarget).pch</PrecompiledHeaderOutputFile>
</ClCompile>
<Link>
@ -301,7 +309,7 @@
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<LanguageStandard>stdcpplatest</LanguageStandard>
<AdditionalIncludeDirectories>pch;source\spdlog\include;source\ImGui\freetype\include;source\ImGui;source\MinHook;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>pch;source\spdlog\include;source\ImGui\freetype\include;source\ImGui;source\MinHook;source\memscan;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<DebugInformationFormat>None</DebugInformationFormat>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
<PrecompiledHeaderOutputFile>$(IntDir)$(TargetName)_$(PlatformTarget).pch</PrecompiledHeaderOutputFile>

View File

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

View File

@ -66,6 +66,7 @@
#include <imstb_textedit.h>
#include <imstb_truetype.h>
#include <MinHook.h>
#include <memscan.h>
#include "..\source\config\ini.h"
#include "..\source\game\Engine\GameSpeedHandler.h"

View File

@ -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!)" }
};
}

View File

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

View File

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

View File

@ -3,6 +3,8 @@
namespace Core {
extern void DisableConsole();
extern std::counting_semaphore<4> maxHookThreads;
extern DWORD64 WINAPI MainThread(HMODULE hModule);
extern void Cleanup();
}
@ -22,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);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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<LPVOID, void(*)(DWORD64, bool)> 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;
}

View File

@ -1,7 +1,7 @@
#include <pch.h>
#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);

View File

@ -1,21 +0,0 @@
#include <pch.h>
#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;
}
}
}

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

@ -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<LPVOID(*)()>(&GamePH::FreeCamera::Get) },
{ "GameDI_PH", reinterpret_cast<LPVOID(*)()>(&GamePH::GameDI_PH::Get) },
{ "GameDI_PH2", reinterpret_cast<LPVOID(*)()>(&GamePH::GameDI_PH2::Get) },
{ "gen_TPPModel", reinterpret_cast<LPVOID(*)()>(&GamePH::gen_TPPModel::Get) },
{ "LevelDI", reinterpret_cast<LPVOID(*)()>(&GamePH::LevelDI::Get) },
{ "LocalClientDI", reinterpret_cast<LPVOID(*)()>(&GamePH::LocalClientDI::Get) },
{ "LogicalPlayer", reinterpret_cast<LPVOID(*)()>(&GamePH::LogicalPlayer::Get) },

View File

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

View File

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

View File

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

View File

@ -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<retType>(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<DWORD64>(GetModuleHandle(moduleName)) + static_cast<DWORD64>(off);\
return name=reinterpret_cast<DWORD64>(GetModuleHandleA(moduleName)) + static_cast<DWORD64>(off);\
}
#define AddVTOffset(name, moduleName, rttiName, retType)\
static retType GetVT_## name () {\
static retType VT_## name = NULL;\
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<retType>(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)

View File

@ -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 <typename GetTargetOffsetRetType, typename OrigType>
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 <typename GetTargetOffsetRetType, typename OrigType>
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<LPVOID*>(&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;
};

View File

@ -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<int>(bytesCounted);
continue;
}
if (pattern[i] == '?') {
patt += SigScanWildCardStr;
i += (pattern[static_cast<DWORD64>(i) + 1] == '?') ? 2 : 1;
} else {
patt.push_back(pattern[i]);
i++;
}
}
return patt;
}
DWORD64 CalcTargetAddrOfRelInst(DWORD64 addrOfInst, size_t opSize) {
int offset = *reinterpret_cast<int*>(addrOfInst + opSize);
@ -62,7 +90,7 @@ namespace Utils {
std::vector<DWORD64> GetXrefsTo(DWORD64 address, DWORD64 start, size_t size) {
std::vector<DWORD64> xrefs = {};
const std::string idaPattern = bytesToIDAPattern(reinterpret_cast<BYTE*>(&address), 8);
const std::string idaPattern = BytesToIDAPattern(reinterpret_cast<BYTE*>(&address), 8);
const DWORD64 end = start + size;
while (start && start < end) {

View File

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

View File

@ -1,45 +1,30 @@
#include "pch.h"
#include <pch.h>
namespace Utils {
namespace RTTI {
static std::string bytesToIDAPattern(BYTE* bytes, size_t size) {
std::stringstream idaPattern;
idaPattern << std::hex << std::uppercase << std::setfill('0');
for (size_t i = 0; i < size; i++) {
const int currentByte = bytes[i];
if (currentByte != 255)
idaPattern << std::setw(2) << currentByte;
else
idaPattern << "??";
if (i != size - 1)
idaPattern << " ";
}
return idaPattern.str();
}
static std::vector<DWORD64> getXrefsTo(DWORD64 moduleBaseAddr, DWORD64 address, DWORD64 start, size_t size) {
std::vector<DWORD64> xrefs = {};
const DWORD64 finalOffset = address - moduleBaseAddr;
const std::string idaPattern = bytesToIDAPattern(reinterpret_cast<BYTE*>(const_cast<DWORD64*>(&finalOffset)), 4);
const std::string idaPattern = Memory::BytesToIDAPattern(reinterpret_cast<BYTE*>(const_cast<DWORD64*>(&finalOffset)), 4);
std::string patt = Memory::ConvertSigToScannerSig(idaPattern.c_str());
// Get the end of the section (in our case the end of the .rdata section)
const DWORD64 end = start + size;
while (start && start < end) {
DWORD64 xref = reinterpret_cast<DWORD64>(Utils::SigScan::PatternScanner::FindPattern(reinterpret_cast<LPVOID>(start), size, { idaPattern.c_str(), Utils::SigScan::PatternType::Address}));
const auto scanner = memscan::mapped_region_t(start, end);
auto patternFind = scanner.find_pattern<ms_uptr_t>(patt);
// If the xref is 0 it means that there either were no xrefs, or there are no remaining xrefs.
// So we should break out of our loop, otherwise it will keep on trying to look for xrefs.
if (!xref)
if (!patternFind.has_value())
break;
// We've found an xref, save it in the vector, and add 8 to start, so it wil now search for xrefs
// from the previously found xref untill we're at the end of the section, or there aren't any xrefs left.
xrefs.push_back(xref);
start = xref + 8;
xrefs.push_back(patternFind.value());
start = patternFind.value() + 8;
}
return xrefs;
@ -83,7 +68,7 @@ namespace Utils {
const std::string typeDescriptorName = ".?AV" + std::string(tableName) + "@@";
// Convert the string to an IDA pattern so that we can pattern scan it
std::string idaPattern = bytesToIDAPattern(reinterpret_cast<BYTE*>(const_cast<char*>(typeDescriptorName.data())), typeDescriptorName.size());
std::string idaPattern = Memory::BytesToIDAPattern(reinterpret_cast<BYTE*>(const_cast<char*>(typeDescriptorName.data())), typeDescriptorName.size());
DWORD64 rttiTypeDescriptor = reinterpret_cast<DWORD64>(Utils::SigScan::PatternScanner::FindPattern(moduleName, { idaPattern.c_str(), Utils::SigScan::PatternType::Address }));
if (!rttiTypeDescriptor)
@ -113,7 +98,7 @@ namespace Utils {
// Now we need to get an xref to the object locator, as that's where the vtable is located
{
// Convert the object locator address to an IDA pattern
idaPattern = bytesToIDAPattern(reinterpret_cast<BYTE*>(const_cast<DWORD64*>(&objectLocator)), 8);
idaPattern = Memory::BytesToIDAPattern(reinterpret_cast<BYTE*>(const_cast<DWORD64*>(&objectLocator)), 8);
const DWORD64 vtableAddr = reinterpret_cast<DWORD64>(Utils::SigScan::PatternScanner::FindPattern(reinterpret_cast<LPVOID>(rdataStart), rdataSize, { idaPattern.c_str(), Utils::SigScan::PatternType::Address })) + 0x8;

View File

@ -38,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<BYTE*>(malloc(byteCount + 1));
if (!patt)
return nullptr;
BYTE* mask = reinterpret_cast<BYTE*>(malloc(byteCount + 1));
if (!mask) {
free(patt);
return nullptr;
}
int offset = 0;
int bytesCounted = 0;
i = 0;
while (i < len - 1) {
if (pattern.pattern[i] == '[') {
i++;
offset = bytesCounted;
}
std::string patt = Memory::ConvertSigToScannerSig(pattern.pattern, &offset);
if (pattern.pattern[i] == '\0')
break;
if (pattern.pattern[i] == '?' && pattern.pattern[i + 1] == '?') {
mask[bytesCounted] = '?';
patt[bytesCounted] = '\0';
} else {
BYTE hn = pattern.pattern[i] > '9' ? pattern.pattern[i] - 'A' + 10 : pattern.pattern[i] - '0';
BYTE ln = pattern.pattern[i + 1] > '9' ? pattern.pattern[i + 1] - 'A' + 10 : pattern.pattern[i + 1] - '0';
BYTE n = (hn << 4) | ln;
mask[bytesCounted] = 'x';
patt[bytesCounted] = n;
}
bytesCounted++;
i += 2;
while (i < len && (pattern.pattern[i] == ' ' || pattern.pattern[i] == '\t' || pattern.pattern[i] == '\r' || pattern.pattern[i] == '\n'))
i++;
}
if (bytesCounted <= byteCount)
mask[bytesCounted] = '\0';
const auto scanner = memscan::mapped_region_t(reinterpret_cast<DWORD64>(startAddress), reinterpret_cast<DWORD64>(startAddress) + searchSize);
auto patternFind = scanner.find_pattern<ms_uptr_t>(patt);
LPVOID ret = nullptr;
const DWORD64 retAddress = reinterpret_cast<DWORD64>(startAddress);
const DWORD64 endAddress = retAddress + searchSize;
size_t searchLen = bytesCounted;
BYTE* retAddressPtr = reinterpret_cast<BYTE*>(retAddress);
BYTE* endAddressPtr = reinterpret_cast<BYTE*>(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<BYTE*>(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<LPVOID>(retAddressPtr + offset);
break;
}
retAddressPtr++;
}
if (ret != nullptr)
break;
} else
// Skip the non-readable memory region
retAddressPtr = reinterpret_cast<BYTE*>(mbi.BaseAddress) + mbi.RegionSize;
} else
retAddressPtr++;
}
free(patt);
free(mask);
if (patternFind.has_value())
ret = reinterpret_cast<LPVOID>(patternFind.value() + offset);
switch (pattern.type) {
case PatternType::Pointer: