From 8e43db9a20d481704c4f5b6b0e71cbb45706e762 Mon Sep 17 00:00:00 2001 From: Surasia <74399067+Surasia@users.noreply.github.com> Date: Tue, 13 Aug 2024 00:03:19 +0200 Subject: [PATCH] 0.2.0: Hooking HavokScript --- InfExt/InfExt.vcxproj | 3 +- InfExt/InfExt.vcxproj.filters | 9 +- InfExt/src/Client.cpp | 193 ++++++++------- InfExt/src/Client.hpp | 9 +- InfExt/src/HavokScript/HavokScript.cpp | 71 +++++- InfExt/src/HavokScript/HavokScript.hpp | 42 +++- .../src/HavokScript/HksCompilerSettings.hpp | 21 ++ InfExt/src/Lua/LuaHook.cpp | 99 ++------ InfExt/src/Lua/LuaHook.hpp | 18 +- InfExt/src/Memory.cpp | 104 ++++---- InfExt/src/Memory.hpp | 40 +-- InfExt/src/Misc/ChromaSDK.cpp | 20 +- InfExt/src/Misc/ChromaSDK.hpp | 24 +- InfExt/src/Variant/Variant.cpp | 233 ------------------ InfExt/src/Variant/Variant.hpp | 50 ---- README.md | 45 +--- licenses/LuaLicense.txt | 8 - licenses/Sol2License.txt | 20 -- vcpkg.json | 4 +- 19 files changed, 354 insertions(+), 659 deletions(-) create mode 100644 InfExt/src/HavokScript/HksCompilerSettings.hpp delete mode 100644 InfExt/src/Variant/Variant.cpp delete mode 100644 InfExt/src/Variant/Variant.hpp delete mode 100644 licenses/LuaLicense.txt delete mode 100644 licenses/Sol2License.txt diff --git a/InfExt/InfExt.vcxproj b/InfExt/InfExt.vcxproj index ce8d1da..d1980c8 100644 --- a/InfExt/InfExt.vcxproj +++ b/InfExt/InfExt.vcxproj @@ -79,11 +79,11 @@ + - @@ -91,7 +91,6 @@ - diff --git a/InfExt/InfExt.vcxproj.filters b/InfExt/InfExt.vcxproj.filters index 8708a46..30e2f78 100644 --- a/InfExt/InfExt.vcxproj.filters +++ b/InfExt/InfExt.vcxproj.filters @@ -26,15 +26,15 @@ Header Files - - Header Files - Header Files Header Files + + Header Files + @@ -49,9 +49,6 @@ Source Files - - Source Files - Source Files diff --git a/InfExt/src/Client.cpp b/InfExt/src/Client.cpp index 50eb8b8..9284b1e 100644 --- a/InfExt/src/Client.cpp +++ b/InfExt/src/Client.cpp @@ -1,117 +1,120 @@ #include "Client.hpp" -#include "Exports.hpp" #include "./HavokScript/HavokScript.hpp" #include "./Lua/LuaHook.hpp" #include "./Misc/ChromaSDK.hpp" -#include "./Variant/Variant.hpp" - -std::mutex consoleMutex; -std::condition_variable cv; -std::atomic stopThread(false); -std::thread mainThread; - -DWORD WINAPI CreateConsole() -{ - FILE *dummy = nullptr; - AllocConsole(); - AttachConsole(GetCurrentProcessId()); - { - std::lock_guard lock(consoleMutex); - if (freopen_s(&dummy, "CONIN$", "r", stdin) != 0 || freopen_s(&dummy, "CONOUT$", "w", stdout) != 0 || - freopen_s(&dummy, "CONOUT$", "w", stderr) != 0) { - std::cerr << "Failed to redirect console streams !" << std::endl; - return 1; - } - } - return 0; +#include "Exports.hpp" + +DWORD WINAPI CreateConsole() { + FILE* dummy = nullptr; + AllocConsole(); + AttachConsole(GetCurrentProcessId()); + { + std::lock_guard lock(consoleMutex); + if (freopen_s(&dummy, "CONIN$", "r", stdin) != 0 || + freopen_s(&dummy, "CONOUT$", "w", stdout) != 0 || + freopen_s(&dummy, "CONOUT$", "w", stderr) != 0) { + std::cerr << "Failed to redirect console streams !" << std::endl; + return 1; + } + } + return 0; } -DWORD WINAPI DestroyConsole() -{ - if (FreeConsole() == 0) { - std::cerr << "Console failed to close!" << std::endl; - } - - { - std::lock_guard lock(consoleMutex); - freopen_s(reinterpret_cast(stdin), "NUL:", "r", stdin); - freopen_s(reinterpret_cast(stdout), "NUL:", "r", stdout); - freopen_s(reinterpret_cast(stderr), "NUL:", "r", stderr); - } - - std::cin.clear(); - std::cout.clear(); - std::cerr.clear(); - return 0; +DWORD WINAPI DestroyConsole() { + if (FreeConsole() == 0) { + std::cerr << "Console failed to close!" << std::endl; + } + + { + std::lock_guard lock(consoleMutex); + freopen_s(reinterpret_cast(stdin), "NUL:", "r", stdin); + freopen_s(reinterpret_cast(stdout), "NUL:", "r", stdout); + freopen_s(reinterpret_cast(stderr), "NUL:", "r", stderr); + } + + std::cin.clear(); + std::cout.clear(); + std::cerr.clear(); + return 0; } -DWORD WINAPI DestroyHook() -{ - stopThread = true; - cv.notify_all(); +DWORD WINAPI DestroyHook() { + stopThread = true; + cv.notify_all(); - if (mainThread.joinable()) { - mainThread.join(); - } + if (mainThread.joinable()) { + mainThread.join(); + } - MH_DisableHook(MH_ALL_HOOKS); - MH_Uninitialize(); - return 0; -} + MH_DisableHook(MH_ALL_HOOKS); + MH_Uninitialize(); -void ProcessCommands() -{ - while (!stopThread) { - LuaVM::ProcessCommand(); - } + if (hMutex) { + ReleaseMutex(hMutex); + CloseHandle(hMutex); + hMutex = NULL; + } + + return 0; } +void ProcessCommands() { + { + std::unique_lock lock(consoleMutex); + if (cv.wait_for(lock, std::chrono::seconds(10), + [] { return stopThread.load(); })) { + return; // Wait after Steam prints debug info. + } + } + while (!stopThread) { + LuaVM::ProcessCommand(); + } +} -static DWORD SetupHook() -{ - uintptr_t ModuleBase = 0; +static DWORD SetupHook() { + uintptr_t ModuleBase = 0; - LPCSTR moduleName = "HaloInfinite.exe"; - ModuleBase = reinterpret_cast(GetModuleHandleA(moduleName)); - if (ModuleBase == 0) { - std::cerr << "Failed to get module handle for " << moduleName << std::endl; - return 1; - } + LPCSTR moduleName = "HaloInfinite.exe"; + ModuleBase = reinterpret_cast(GetModuleHandleA(moduleName)); + if (ModuleBase == 0) { + std::cerr << "Failed to get module handle for " << moduleName << std::endl; + return 1; + } - ChromaSDK::HookChroma(ModuleBase); - Hks::HookHavokScript(ModuleBase); - Variant::HookVariant(ModuleBase); + ChromaSDK::HookChroma(ModuleBase); + Hks::HookHavokScript(ModuleBase); - LuaVM::InitializeLua(); - LuaVM::HookVariantFunctions(); - mainThread = std::thread(ProcessCommands); - return 0; + mainThread = std::thread(ProcessCommands); + return 0; } - -DWORD WINAPI MainThread(LPVOID lpParameter) -{ - SetupHook(); - return 0; +DWORD WINAPI MainThread(LPVOID lpParameter) { + SetupHook(); + return 0; } - -BOOL APIENTRY DllMain(HMODULE hModule, DWORD dwReason, LPVOID lpReserved) -{ - switch (dwReason) { - case DLL_PROCESS_ATTACH: - CreateConsole(); - DisableThreadLibraryCalls(hModule); - CreateThread(nullptr, 0, MainThread, nullptr, 0, nullptr); - break; - case DLL_PROCESS_DETACH: - DestroyHook(); - LuaVM::CleanLua(); - DestroyConsole(); - FreeLibraryAndExitThread(hModule, TRUE); - break; - default: - break; - } - return TRUE; +BOOL APIENTRY DllMain(HMODULE hModule, DWORD dwReason, LPVOID lpReserved) { + switch (dwReason) { + case DLL_PROCESS_ATTACH: + hMutex = CreateMutexA(NULL, TRUE, "UniqueDLLInstanceMutex"); + if (hMutex == NULL || GetLastError() == ERROR_ALREADY_EXISTS) { + if (hMutex) { + CloseHandle(hMutex); + hMutex = NULL; + } + return FALSE; // DLL is already loaded + } + CreateConsole(); + DisableThreadLibraryCalls(hModule); + CreateThread(nullptr, 0, MainThread, nullptr, 0, nullptr); + break; + case DLL_PROCESS_DETACH: + DestroyHook(); + DestroyConsole(); + FreeLibraryAndExitThread(hModule, TRUE); + break; + default: + break; + } + return TRUE; } diff --git a/InfExt/src/Client.hpp b/InfExt/src/Client.hpp index 3305b78..c8f4651 100644 --- a/InfExt/src/Client.hpp +++ b/InfExt/src/Client.hpp @@ -1,15 +1,13 @@ #ifndef CLIENT_HPP #define CLIENT_HPP - -#include "../include/MinHook.h" // IWYU pragma: keep +#include #include // IWYU pragma: keep #include #include // IWYU pragma: keep #include // IWYU pragma: keep #include // IWYU pragma: keep #include // IWYU pragma: keep -#include #include // IWYU pragma: keep #include #include // IWYU pragma: keep @@ -20,5 +18,10 @@ #include #include +std::mutex consoleMutex; +std::condition_variable cv; +std::atomic stopThread(false); +std::thread mainThread; +HANDLE hMutex = NULL; #endif diff --git a/InfExt/src/HavokScript/HavokScript.cpp b/InfExt/src/HavokScript/HavokScript.cpp index c9d7a6b..859a3e6 100644 --- a/InfExt/src/HavokScript/HavokScript.cpp +++ b/InfExt/src/HavokScript/HavokScript.cpp @@ -1,18 +1,69 @@ #include "HavokScript.hpp" #include "../Memory.hpp" +#include "HksCompilerSettings.hpp" + +int Hks::GetGlobalHook(uintptr_t state, char* string) { + LuaState = state; + return GetGlobalA(state, string); +} + +uint64_t Hks::PCallHook(uintptr_t state, int function, uint32_t i, int u) { + __int64 result = PCallA(state, function, i, u); + return result; +} -/* Declare private variables. */ -Hks::GetGlobal Hks::GetGlobalH = nullptr; -Hks::GetGlobal Hks::GetGlobalA = nullptr; -int Hks::GetGlobalHook(lua_State *state, char *string) -{ - return GetGlobalA(state, string); +uint64_t Hks::LoadBufferHook(uintptr_t state, + const struct HksCompilerSettings* compilerSettings, + const char* buffer, __int64 length, + const char* unknown1) { + __int64 result = + LoadBufferA(state, compilerSettings, buffer, length, unknown1); + return result; } -void Hks::HookHavokScript(uintptr_t ModuleBase) -{ - const uintptr_t GetGlobalOffset = 0x80CB4C; - GetGlobalH = hook_function(ModuleBase, GetGlobalOffset, &GetGlobalHook, &GetGlobalA); +uint64_t Hks::DoString(const char* string) { + + size_t stringLength = std::strlen(string); + if (Hks::LoadBufferHook( + LuaState, + (const struct HksCompilerSettings*)(*((uintptr_t*)LuaState + 2) + + 1368), + string, stringLength, string)) { + return 1; + } + + uint64_t call_result = PCallHook(LuaState, 0, 0xFFFFFFFF, 0); + if (call_result) { + return 1; + } + return call_result; +} + + +uint64_t Hks::DoFile(const char* filename) { + std::ifstream file(filename); + if (!file.is_open()) { + std::cerr << "Failed to open file: " << filename << std::endl; + return 1; + } + + std::stringstream buffer; + buffer << file.rdbuf(); + std::string fileContents = buffer.str(); + file.close(); + + return DoString(fileContents.c_str()); +} + +void Hks::HookHavokScript(uintptr_t ModuleBase) { + const uintptr_t GetGlobalOffset = 0x80CB4C; + GetGlobalH = hook_function(ModuleBase, GetGlobalOffset, + &GetGlobalHook, &GetGlobalA); + const uintptr_t PCallOffset = 0x764304; + PCallH = hook_function(ModuleBase, PCallOffset, &PCallHook, &PCallA); + const uintptr_t LoadBufferOffset = 0xA484C0; + LoadBufferH = hook_function(ModuleBase, LoadBufferOffset, + &LoadBufferHook, &LoadBufferA); } diff --git a/InfExt/src/HavokScript/HavokScript.hpp b/InfExt/src/HavokScript/HavokScript.hpp index c4547f8..3da8063 100644 --- a/InfExt/src/HavokScript/HavokScript.hpp +++ b/InfExt/src/HavokScript/HavokScript.hpp @@ -1,20 +1,46 @@ #ifndef HAVOKSCRIPT_HPP #define HAVOKSCRIPT_HPP #include -#include +#include +#include +#include +#include struct Hks { public: + /* Operators */ + Hks() = default; + Hks(const Hks &) = default; + Hks(Hks &&) = delete; + Hks &operator=(const Hks &) = default; + Hks &operator=(Hks &&) = delete; + ~Hks() = default; /* Hooks */ - static void HookHavokScript(uintptr_t ModuleBase); + static void HookHavokScript(uintptr_t ModuleBase); + static uint64_t DoFile(const char* filename); + static uint64_t DoString(const char* string); private: - /* Function Type Definitions */ - using GetGlobal = int(__fastcall *)(lua_State *state, char *string); - static GetGlobal GetGlobalH; - static GetGlobal GetGlobalA; - /* Private Functions */ - static int GetGlobalHook(lua_State *state, char *string); + /* Function Type Definitions */ + using GetGlobal = int(__fastcall*)(uintptr_t state, char* string); + static inline GetGlobal GetGlobalH = nullptr; + static inline GetGlobal GetGlobalA = nullptr; + + using PCall = uint64_t(__fastcall*)(uintptr_t state, int function, uint32_t i, int u); + static inline PCall PCallH = nullptr; + static inline PCall PCallA = nullptr; + + using LoadBuffer = uint64_t(__fastcall*)(uintptr_t state, const struct HksCompilerSettings* compilerSettings, const char* buffer, __int64 length, const char* unknown1); + static inline LoadBuffer LoadBufferH = nullptr; + static inline LoadBuffer LoadBufferA = nullptr; + + /* Private Functions */ + static int GetGlobalHook(uintptr_t state, char* string); + static inline uint64_t PCallHook(uintptr_t state, int function, uint32_t i, int u); + static inline uint64_t LoadBufferHook(uintptr_t state, const struct HksCompilerSettings* compilerSettings, const char* buffer, __int64 length, const char* unknown1); + + /* Private Variables */ + static inline uintptr_t LuaState = 0; }; #endif diff --git a/InfExt/src/HavokScript/HksCompilerSettings.hpp b/InfExt/src/HavokScript/HksCompilerSettings.hpp new file mode 100644 index 0000000..53aeeef --- /dev/null +++ b/InfExt/src/HavokScript/HksCompilerSettings.hpp @@ -0,0 +1,21 @@ +#ifndef HKSCOMPILERSETTINGS_HPP +#define HKSCOMPILERSETTINGS_HPP +#include + +struct HksCompilerSettings { +public: + /* Operators */ + HksCompilerSettings() = default; + ~HksCompilerSettings() = default; + +private: + uint32_t ignore_debug; + uint32_t emitStruct; + uint32_t enableIntLiteral; + uint32_t emitMemo; + uint32_t skipMemo; + uint32_t strip; + const char* stripNames; +}; + +#endif diff --git a/InfExt/src/Lua/LuaHook.cpp b/InfExt/src/Lua/LuaHook.cpp index e20800a..1ec39cd 100644 --- a/InfExt/src/Lua/LuaHook.cpp +++ b/InfExt/src/Lua/LuaHook.cpp @@ -1,76 +1,27 @@ #include "LuaHook.hpp" -#include "../Variant/Variant.hpp" - - -sol::state LuaVM::lua; - -void LuaVM::HookVariantFunctions() -{ - lua["Variant_GetVariantHook"] = &Variant::GetVariantHook; - lua["Variant_GetEngineName"] = &Variant::GetEngineName; - lua["Variant_GetVariantName"] = &Variant::GetVariantName; - lua["Variant_GetTeamsEnabled"] = &Variant::GetTeamsEnabled; - lua["Variant_GetTeamScoringEnabled"] = &Variant::GetTeamScoringEnabled; - lua["Variant_GetRequisitionEnabled"] = &Variant::GetRequisitionEnabled; - lua["Variant_GetReqVehiclesEnabled"] = &Variant::GetReqVehiclesEnabled; - lua["Variant_GetReqConsumableWeaponsEnabled"] = &Variant::GetReqConsumableWeaponsEnabled; - lua["Variant_GetReqLoadoutWeaponsEnabled"] = &Variant::GetReqLoadoutWeaponsEnabled; - lua["Variant_GetReqArmorModsEnabled"] = &Variant::GetReqArmorModsEnabled; - lua["Variant_GetActiveSpartanTrackingEnabled"] = &Variant::GetActiveSpartanTrackingEnabled; - lua["Variant_GetPassiveSpartanTrackingEnabled"] = &Variant::GetPassiveSpartanTrackingEnabled; - lua["Variant_GetREQVehicleCapPerMapEnabled"] = &Variant::GetREQVehicleCapPerMapEnabled; - lua["Variant_GetREQVehicleCapPerMap"] = &Variant::GetREQVehicleCapPerMap; - lua["Variant_GetREQVehicleCapPerTeamEnabled"] = &Variant::GetREQVehicleCapPerTeamEnabled; - lua["Variant_GetREQVehicleCapPerTeam"] = &Variant::GetREQVehicleCapPerTeam; - lua["Variant_GetLivesPerRound"] = &Variant::GetLivesPerRound; - lua["Variant_GetUseInfluencerSpawnPointSpawning"] = &Variant::GetUseInfluencerSpawnPointSpawning; - lua["Variant_GetUseCoopSpawning"] = &Variant::GetUseCoopSpawning; - lua["Variant_GetUseSwarmSpawning"] = &Variant::GetUseSwarmSpawning; - lua["Variant_GetSpawnProtectionEnabled"] = &Variant::GetSpawnProtectionEnabled; - lua["Variant_GetSpawnProtectionDuration"] = &Variant::GetSpawnProtectionDuration; - lua["Variant_GetRegulationRoundLimit"] = &Variant::GetRegulationRoundLimit; - lua["Variant_GetRoundLimit"] = &Variant::GetRoundLimit; - lua["Variant_GetRoundsTiedLimit"] = &Variant::GetRoundsTiedLimit; - lua["Variant_GetEarlyVictoryRoundCount"] = &Variant::GetEarlyVictoryRoundCount; - lua["Variant_IsEarlyRespawnEnabled"] = &Variant::IsEarlyRespawnEnabled; - lua["Variant_GetMaximumRespawnTime"] = &Variant::GetMaximumRespawnTime; - lua["Variant_GetMinimumRespawnTime"] = &Variant::GetMinimumRespawnTime; - lua["Variant_GetBetrayalPenaltyTime"] = &Variant::GetBetrayalPenaltyTime; - lua["Variant_GetSuicidePenaltyTime"] = &Variant::GetSuicidePenaltyTime; -} - - -void LuaVM::InitializeLua() -{ - lua.open_libraries(sol::lib::base, sol::lib::os, sol::lib::io, sol::lib::string); - lua["exit"] = [](int code) { - std::cout << "Exiting with code: " << code << std::endl; - std::exit(code); - }; -} - -void LuaVM::CleanLua() -{ - lua.collect_garbage(); -} - -void LuaVM::ProcessCommand() -{ - std::string command = ""; - - std::cout << "> "; - std::getline(std::cin, command); - sol::protected_function_result result = lua.script(command, [](lua_State *, sol::protected_function_result pfr) { return pfr; }); - - if (result.valid()) { - sol::optional output = result; - if (output) { - std::cout << std::format("{} \n", *output); - } else { - std::cout << "\n"; - } - } else { - sol::error err = result; - std::cout << err.what() << std::endl; - } +#include "../HavokScript/HavokScript.hpp" + +void LuaVM::ProcessCommand() { + std::string command = ""; + + std::cout << "> "; + std::getline(std::cin, command); + + if (command.rfind("/dofile ", 0) == 0) { + std::string filepath = command.substr(8); + if (!filepath.empty()) { + if (Hks::DoFile(filepath.c_str())) { + std::cerr << "Error: Failed to load file: " << filepath << std::endl; + } + } + else { + std::cerr << "Error: No filepath specified for /dofile command." + << std::endl; + } + } + else { + if (Hks::DoString(command.c_str())) { + std::cerr << "Error: Failed to load string: " << command << std::endl; + } + } } diff --git a/InfExt/src/Lua/LuaHook.hpp b/InfExt/src/Lua/LuaHook.hpp index c6edc1f..ed978fa 100644 --- a/InfExt/src/Lua/LuaHook.hpp +++ b/InfExt/src/Lua/LuaHook.hpp @@ -1,17 +1,19 @@ #ifndef LUAHOOK_HPP #define LUAHOOK_HPP -#include +#include #include struct LuaVM { public: - static void HookVariantFunctions(); - static void InitializeLua(); - static void CleanLua(); - static void ProcessCommand(); - -private: - static sol::state lua; + /* Operators */ + LuaVM() = default; + LuaVM(const LuaVM &) = default; + LuaVM(LuaVM &&) = delete; + LuaVM &operator=(const LuaVM &) = default; + LuaVM &operator=(LuaVM &&) = delete; + ~LuaVM() = default; + /* Public Functions */ + static void ProcessCommand(); }; #endif diff --git a/InfExt/src/Memory.cpp b/InfExt/src/Memory.cpp index bc2e0d0..91850bb 100644 --- a/InfExt/src/Memory.cpp +++ b/InfExt/src/Memory.cpp @@ -1,80 +1,70 @@ #include "Memory.hpp" -Memory::Memory() -{ -} - -Memory::~Memory() -{ -} - -/* Declare private variables. */ -bool Memory::isMHInit = false; - UINT64 Memory::CheckCodeAccess(UINT64 startaddress) { - MEMORY_BASIC_INFORMATION mbi{}; + MEMORY_BASIC_INFORMATION mbi{}; - const auto *safeAdress = reinterpret_cast(startaddress); - if (VirtualQuery(safeAdress, &mbi, sizeof(mbi)) != 0) { - if (mbi.Protect != PAGE_NOACCESS && mbi.Protect == PAGE_EXECUTE_READ) { - return 1; - } - } + const auto* safeAdress = reinterpret_cast(startaddress); + if (VirtualQuery(safeAdress, &mbi, sizeof(mbi)) != 0) { + if (mbi.Protect != PAGE_NOACCESS && mbi.Protect == PAGE_EXECUTE_READ) { + return 1; + } + } - return 0; + return 0; } DWORD Memory::HookFunction(LPVOID pTarget, LPVOID pDetour, LPVOID pTrampoline) { - if (!isMHInit) { - if (MH_Initialize() != MH_OK) { - std::cout << "Error: MHInit failed." << "\n"; - return 1; - } + if (!isMHInit) { + if (MH_Initialize() != MH_OK) { + std::cout << "Error: MHInit failed." << "\n"; + return 1; + } - isMHInit = true; - } + isMHInit = true; + } - uint32_t hook = MH_CreateHook(pTarget, pDetour, static_cast(pTrampoline)); - if (hook != MH_OK) { - std::cout << "Error: MHCreateHook failed." << "\n"; - return 1; - } + uint32_t hook = MH_CreateHook(pTarget, pDetour, static_cast(pTrampoline)); + if (hook != MH_OK) { + printf("Error: MHCreateHook failed with code: %d \n", hook); + return 1; + } - if (MH_EnableHook(pTarget) != MH_OK) { - std::cout << "Error: MHEnableHook failed." << "\n"; - return 1; - } + if (MH_EnableHook(pTarget) != MH_OK) { + std::cout << "Error: MHEnableHook failed." << "\n"; + return 1; + } - return 0; + return 0; } DWORD Memory::OnAccessHookFunction(PVOID pTarget, LPVOID pDetour, LPVOID pTrampoline) { - bool isWaiting = true; - uintptr_t accessPointer = 0; - - while (isWaiting) { - accessPointer = Memory::CheckCodeAccess(reinterpret_cast(pTarget)); - - if (accessPointer != 0) { - Memory::HookFunction(pTarget, pDetour, pTrampoline); - } else { - std::cout << "Error: Failed to hook code." << "\n"; - } - - isWaiting = false; - } - return 0; + bool isWaiting = true; + uintptr_t accessPointer = 0; + + while (isWaiting) { + accessPointer = Memory::CheckCodeAccess(reinterpret_cast(pTarget)); + + if (accessPointer != 0) { + Memory::HookFunction(pTarget, pDetour, pTrampoline); + } + else { + std::cout << "Error: Failed to hook code." << "\n"; + } + + isWaiting = false; + } + return 0; } -void Memory::WriteBytes(uintptr_t pAddress, BYTE *pBytes, UINT szSize) +void Memory::WriteBytes(uintptr_t pAddress, BYTE* pBytes, UINT szSize) { - DWORD oldProtect = 0; - if (VirtualProtect(reinterpret_cast(pAddress), szSize, PAGE_EXECUTE_READWRITE, &oldProtect) != 0) { - memcpy(reinterpret_cast(pAddress), pBytes, szSize); - VirtualProtect(reinterpret_cast(pAddress), szSize, oldProtect, &oldProtect); - } + DWORD oldProtect = 0; + if (VirtualProtect(reinterpret_cast(pAddress), szSize, PAGE_EXECUTE_READWRITE, &oldProtect) != 0) { + memcpy(reinterpret_cast(pAddress), pBytes, szSize); + VirtualProtect(reinterpret_cast(pAddress), szSize, oldProtect, &oldProtect); + } } diff --git a/InfExt/src/Memory.hpp b/InfExt/src/Memory.hpp index 139195e..bb9d61c 100644 --- a/InfExt/src/Memory.hpp +++ b/InfExt/src/Memory.hpp @@ -9,32 +9,32 @@ class Memory { public: - /* Operators */ - Memory(); - Memory(const Memory &) = default; - Memory(Memory &&) = delete; - Memory &operator=(const Memory &) = default; - Memory &operator=(Memory &&) = delete; - ~Memory(); - /* Public Functions */ - static DWORD HookFunction(LPVOID pTarget, LPVOID pDetour, LPVOID pTrampoline); - static DWORD OnAccessHookFunction(PVOID pTarget, LPVOID pDetour, LPVOID pTrampoline); - static void WriteBytes(uintptr_t pAddress, BYTE *pBytes, UINT szSize); + /* Operators */ + Memory() = default; + Memory(const Memory&) = default; + Memory(Memory&&) = delete; + Memory& operator=(const Memory&) = default; + Memory& operator=(Memory&&) = delete; + ~Memory() = default;; + /* Public Functions */ + static DWORD HookFunction(LPVOID pTarget, LPVOID pDetour, LPVOID pTrampoline); + static DWORD OnAccessHookFunction(PVOID pTarget, LPVOID pDetour, LPVOID pTrampoline); + static void WriteBytes(uintptr_t pAddress, BYTE* pBytes, UINT szSize); private: - /* Private Declarations */ - static bool isMHInit; - static DWORD dwOldProt; - /* Private Functions */ - static UINT64 CheckCodeAccess(UINT64 startaddress); + /* Private Declarations */ + static inline bool isMHInit = false; + static DWORD dwOldProt; + /* Private Functions */ + static UINT64 CheckCodeAccess(UINT64 startaddress); }; template -T hook_function(uintptr_t ModuleBase, uintptr_t offset, void *hook, T *original) +T hook_function(uintptr_t ModuleBase, uintptr_t offset, void* hook, T* original) { - T functionPointer = reinterpret_cast(ModuleBase + offset); - Memory::HookFunction(functionPointer, hook, original); - return functionPointer; + T functionPointer = reinterpret_cast(ModuleBase + offset); + Memory::HookFunction(functionPointer, hook, original); + return functionPointer; } #endif diff --git a/InfExt/src/Misc/ChromaSDK.cpp b/InfExt/src/Misc/ChromaSDK.cpp index 8d1d9dc..13ec136 100644 --- a/InfExt/src/Misc/ChromaSDK.cpp +++ b/InfExt/src/Misc/ChromaSDK.cpp @@ -1,18 +1,12 @@ #include "ChromaSDK.hpp" #include "../Memory.hpp" -/* Declare private variables. */ -ChromaSDK::NullChromaSDK ChromaSDK::NullChromaSDKH = nullptr; -ChromaSDK::NullChromaSDK ChromaSDK::NullChromaSDKA = nullptr; +/* Nulls out the print message for "ChromaSDK failed to load!" which can cause + * issues with stdin. */ +__int64 ChromaSDK::NullChromaSDKHook(char* Format) { return 0; } -/* Nulls out the print message for "ChromaSDK failed to load!" which can cause issues with stdin. */ -__int64 ChromaSDK::NullChromaSDKHook(char *Format) -{ - return 0; -} - -void ChromaSDK::HookChroma(uintptr_t ModuleBase) -{ - uintptr_t address = 0x8CF168; - NullChromaSDKH = hook_function(ModuleBase, address, &NullChromaSDKHook, &NullChromaSDKA); +void ChromaSDK::HookChroma(uintptr_t ModuleBase) { + uintptr_t address = 0x8CF168; + NullChromaSDKH = hook_function( + ModuleBase, address, &NullChromaSDKHook, &NullChromaSDKA); } diff --git a/InfExt/src/Misc/ChromaSDK.hpp b/InfExt/src/Misc/ChromaSDK.hpp index a1e4d8c..9827ac8 100644 --- a/InfExt/src/Misc/ChromaSDK.hpp +++ b/InfExt/src/Misc/ChromaSDK.hpp @@ -2,20 +2,26 @@ #define CHROMASDK_HPP #include -struct ChromaSDK -{ +struct ChromaSDK { public: + /* Operators */ + ChromaSDK() = default; + ChromaSDK(const ChromaSDK &) = default; + ChromaSDK(ChromaSDK &&) = delete; + ChromaSDK &operator=(const ChromaSDK &) = default; + ChromaSDK &operator=(ChromaSDK &&) = delete; + ~ChromaSDK() = default; /* Hook */ - static void HookChroma(uintptr_t ModuleBase); + static void HookChroma(uintptr_t ModuleBase); private: - /* Function Type Definitions */ - using NullChromaSDK = __int64(__fastcall *)(char *Format); - static NullChromaSDK NullChromaSDKH; - static NullChromaSDK NullChromaSDKA; + /* Function Type Definitions */ + using NullChromaSDK = __int64(__fastcall*)(char* Format); + static inline NullChromaSDK NullChromaSDKH = nullptr; + static inline NullChromaSDK NullChromaSDKA = nullptr; - /* Private Functions */ - static __int64 NullChromaSDKHook(char *Format); + /* Private Functions */ + static __int64 NullChromaSDKHook(char* Format); }; #endif diff --git a/InfExt/src/Variant/Variant.cpp b/InfExt/src/Variant/Variant.cpp deleted file mode 100644 index 8230883..0000000 --- a/InfExt/src/Variant/Variant.cpp +++ /dev/null @@ -1,233 +0,0 @@ -#include "Variant.hpp" -#include "../Memory.hpp" - - -/* Declare private variables. */ -Variant::GetVariant Variant::GetVariantH = nullptr; -Variant::GetVariant Variant::GetVariantA = nullptr; - -uintptr_t Variant::GetVariantHook() -{ - uintptr_t result = GetVariantA(); - return result; -} - -const char *Variant::GetEngineName() -{ - return reinterpret_cast(GetVariantHook() + 28); -} - -const char *Variant::GetVariantName() -{ - return reinterpret_cast(GetVariantHook() + 156); -} - -bool Variant::GetTeamsEnabled() -{ - uintptr_t address = GetVariantHook() + 4252; - return *reinterpret_cast(address); -} - -bool Variant::GetTeamScoringEnabled() -{ - auto *pVariant = (uint8_t *)GetVariantHook(); - bool result = static_cast(pVariant[4252]); - if (!result) { - return false; - } - if (pVariant[4254] != 0U) { - return static_cast(pVariant[4253]); - } - return result; -} - -bool Variant::GetRequisitionEnabled() -{ - uintptr_t address = GetVariantHook() + 540; - return *reinterpret_cast(address); -} - -bool Variant::GetReqVehiclesEnabled() -{ - uintptr_t address = GetVariantHook() + 545; - return *reinterpret_cast(address); -} - -bool Variant::GetReqConsumableWeaponsEnabled() -{ - uintptr_t address = GetVariantHook() + 546; - return *reinterpret_cast(address); -} - -bool Variant::GetReqLoadoutWeaponsEnabled() -{ - uintptr_t address = GetVariantHook() + 547; - return *reinterpret_cast(address); -} - -bool Variant::GetReqArmorModsEnabled() -{ - uintptr_t address = GetVariantHook() + 547; - return *reinterpret_cast(address); -} - -bool Variant::GetActiveSpartanTrackingEnabled() -{ - uintptr_t address = GetVariantHook() + 929435; - return *reinterpret_cast(address); -} - -bool Variant::GetPassiveSpartanTrackingEnabled() -{ - uintptr_t address = GetVariantHook() + 929436; - return *reinterpret_cast(address); -} - -bool Variant::GetREQVehicleCapPerMapEnabled() -{ - uintptr_t address = GetVariantHook() + 541; - return *reinterpret_cast(address); -} - -int8_t Variant::GetREQVehicleCapPerMap() -{ - uintptr_t address = GetVariantHook() + 542; - return *reinterpret_cast(address); -} - -bool Variant::GetREQVehicleCapPerTeamEnabled() -{ - uintptr_t address = GetVariantHook() + 543; - return *reinterpret_cast(address); -} - -int8_t Variant::GetREQVehicleCapPerTeam() -{ - uintptr_t address = GetVariantHook() + 544; - return *reinterpret_cast(address); -} - -int8_t Variant::GetLivesPerRound() -{ - uintptr_t address = GetVariantHook() + 654; - return *reinterpret_cast(address); -} - -int8_t Variant::GetUseInfluencerSpawnPointSpawning() -{ - uintptr_t address = GetVariantHook() + 661; - return *reinterpret_cast(address); -} - -bool Variant::GetUseCoopSpawning() -{ - uintptr_t address = GetVariantHook() + 662; - return *reinterpret_cast(address); -} - -bool Variant::GetUseSwarmSpawning() -{ - uintptr_t address = GetVariantHook() + 663; - return *reinterpret_cast(address); -} - -bool Variant::CheckIfExists(uintptr_t address, uint32_t value) -{ - char temp = 0; - __int64 eval = 0; - - if (value > 0xD || (temp = value, eval = 1i64, ((unsigned __int16)(1 << temp) & address) == 0)) { - eval = 0; - } - return eval != 0; -} - -bool Variant::GetSpawnProtectionEnabled() -{ - uintptr_t address = GetVariantHook() + 652; - return CheckIfExists(address, 10); -} - -uint32_t Variant::GetSpawnProtectionDuration() -{ - double result = 0.0; - uintptr_t address = GetVariantHook(); - if(!CheckIfExists(address + 652, 0xAu)) { - return 0; - } - return *reinterpret_cast(address + 1288); -} - -uint8_t Variant::GetRegulationRoundLimit() -{ - uintptr_t address = GetVariantHook() + 4288; - return *reinterpret_cast(address); -} - -uint8_t Variant::GetRoundLimit() -{ - uintptr_t address = GetVariantHook() + 4248; - int8_t temp_value = *reinterpret_cast(address + 40); - int8_t current_value = 0; - - if (temp_value > 0 && *reinterpret_cast(address + 16) == 2) { - ++temp_value; - } - - if (temp_value >= 0) { - current_value = temp_value; - } - - if (current_value < 32) { - return static_cast(current_value); - } - return 32; -} - -uint8_t Variant::GetRoundsTiedLimit() -{ - uintptr_t address = GetVariantHook() + 4261; - return *reinterpret_cast(address); -} - -uint8_t Variant::GetEarlyVictoryRoundCount() -{ - uintptr_t address = GetVariantHook() + 4260; - return *reinterpret_cast(address); -} - -bool Variant::IsEarlyRespawnEnabled() -{ - uintptr_t address = GetVariantHook() + 652; - return CheckIfExists(address, 4); -} - -uint8_t Variant::GetMaximumRespawnTime() -{ - uintptr_t address = GetVariantHook() + 657; - return *reinterpret_cast(address); -} - -uint8_t Variant::GetMinimumRespawnTime() -{ - uintptr_t address = GetVariantHook() + 656; - return *reinterpret_cast(address); -} - -uint8_t Variant::GetBetrayalPenaltyTime() -{ - uintptr_t address = GetVariantHook() + 659; - return *reinterpret_cast(address); -} - -uint8_t Variant::GetSuicidePenaltyTime() -{ - uintptr_t address = GetVariantHook() + 658; - return *reinterpret_cast(address); -} - -void Variant::HookVariant(uintptr_t ModuleBase) -{ - uintptr_t address = 0xB0F0D0; - GetVariantH = hook_function(ModuleBase, address, &GetVariantHook, &GetVariantA); -} diff --git a/InfExt/src/Variant/Variant.hpp b/InfExt/src/Variant/Variant.hpp deleted file mode 100644 index 77a2924..0000000 --- a/InfExt/src/Variant/Variant.hpp +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef VARIANT_HPP -#define VARIANT_HPP -#include - -struct Variant { -public: - /* Hook */ - static void HookVariant(uintptr_t ModuleBase); - static uintptr_t GetVariantHook(); - static const char *GetEngineName(); - static const char *GetVariantName(); - static bool GetTeamsEnabled(); - static bool GetTeamScoringEnabled(); - static bool GetRequisitionEnabled(); - static bool GetReqVehiclesEnabled(); - static bool GetReqConsumableWeaponsEnabled(); - static bool GetReqLoadoutWeaponsEnabled(); - static bool GetReqArmorModsEnabled(); - static bool GetActiveSpartanTrackingEnabled(); - static bool GetPassiveSpartanTrackingEnabled(); - static bool GetREQVehicleCapPerMapEnabled(); - static int8_t GetREQVehicleCapPerMap(); - static bool GetREQVehicleCapPerTeamEnabled(); - static int8_t GetREQVehicleCapPerTeam(); - static int8_t GetLivesPerRound(); - static int8_t GetUseInfluencerSpawnPointSpawning(); - static bool GetUseCoopSpawning(); - static bool GetUseSwarmSpawning(); - static bool GetSpawnProtectionEnabled(); - static uint32_t GetSpawnProtectionDuration(); - static uint8_t GetRegulationRoundLimit(); - static uint8_t GetRoundLimit(); - static uint8_t GetRoundsTiedLimit(); - static uint8_t GetEarlyVictoryRoundCount(); - static bool IsEarlyRespawnEnabled(); - static uint8_t GetMaximumRespawnTime(); - static uint8_t GetMinimumRespawnTime(); - static uint8_t GetBetrayalPenaltyTime(); - static uint8_t GetSuicidePenaltyTime(); - - -private: - /* Function Type Definitions */ - using GetVariant = uintptr_t(__fastcall *)(); - static GetVariant GetVariantH; - static GetVariant GetVariantA; - static bool CheckIfExists(uintptr_t address, uint32_t value); -}; - -#endif diff --git a/README.md b/README.md index 54c5663..56e9af3 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,10 @@ # InfiniteExt -**InfiniteExt** is a customizable DLL Hook for Halo Infinite featuring a **Lua API** for interacting with game objects and functions. This project is built on top of [Blamify's Gatekeeper](https://github.com/Blamify/Gatekeeper), with functionality updated for the latest build of Halo Infinite. +**InfiniteExt** is a customizable DLL Hook for Halo Infinite for interacting with the built-in HavokScript VM. This project is built on top of [Blamify's Gatekeeper](https://github.com/Blamify/Gatekeeper), with functionality updated for the latest build of Halo Infinite. ## Features -- [x] Built-in Lua VM +- [x] Hooking HavokScript - [x] Hooks to Game Functions +- [x] Lua File Loading - [ ] Tag Viewing - [ ] Built-in Memory Editor - [ ] ImGui Integration @@ -30,44 +31,8 @@ This project can be built using Visual Studio 2022 (vc143) with `vcpkg` installe > The current project is not cross-platform compatible due to a multitude of Microsoft-specific extensions of C++ being used. Usage on Linux using Wine has not been tested. ## Usage -InfiniteExt implements a simple command-line with a Lua 5.4 VM interpreter, powered by [Sol2](https://github.com/ThePhD/sol2/). The `base, os, io, string` modules are loaded by default, and can be used to perform generic lua functionality. Alongside the base functionality, Lua hooks to C++ functions are also used: - -- "Variant_GetVariantHook -- "Variant_GetEngineName -- "Variant_GetVariantName -- "Variant_GetTeamsEnabled -- "Variant_GetTeamScoringEnabled -- "Variant_GetRequisitionEnabled -- "Variant_GetReqVehiclesEnabled -- "Variant_GetReqConsumableWeaponsEnabled -- "Variant_GetReqLoadoutWeaponsEnabled -- "Variant_GetReqArmorModsEnabled -- "Variant_GetActiveSpartanTrackingEnabled -- "Variant_GetPassiveSpartanTrackingEnabled -- "Variant_GetREQVehicleCapPerMapEnabled -- "Variant_GetREQVehicleCapPerMap -- "Variant_GetREQVehicleCapPerTeamEnabled -- "Variant_GetREQVehicleCapPerTeam -- "Variant_GetLivesPerRound" -- "Variant_GetUseInfluencerSpawnPointSpawning -- "Variant_GetUseCoopSpawning -- "Variant_GetUseSwarmSpawning -- "Variant_GetSpawnProtectionEnabled -- "Variant_GetSpawnProtectionDuration -- "Variant_GetRegulationRoundLimit -- "Variant_GetRoundLimit -- "Variant_GetRoundsTiedLimit -- "Variant_GetEarlyVictoryRoundCount -- "Variant_IsEarlyRespawnEnabled -- "Variant_GetMaximumRespawnTime -- "Variant_GetMinimumRespawnTime -- "Variant_GetBetrayalPenaltyTime -- "Variant_GetSuicidePenaltyTime - -As the project matures, these functions will include their own documentation and more will be added. +InfiniteExt's command line is directly integrated into the built-in HavokScript VM of Halo Infinite. Here, you can run regular lua functions in addition to the custom C bindings which you can find a list for [here.](https://surasia.github.io/assets/env_alphabetical.json) ## Licenses -- [Minhook:]("licenses/MinhookLicense.txt") The Minimalistic x86/x64 API Hooking Library for Windows. -- [Sol2:]("licenses/Sol2License.txt") Lua API wrapper with advanced features and top notch performance. -- [Lua:]("licenses/LuaLicense.txt") Lua is a powerful, efficient, lightweight, embeddable scripting language. It supports procedural programming, object-oriented programming, functional programming, data-driven programming, and data description. \ No newline at end of file +- [Minhook:](licenses/MinhookLicense.txt) The Minimalistic x86/x64 API Hooking Library for Windows. \ No newline at end of file diff --git a/licenses/LuaLicense.txt b/licenses/LuaLicense.txt deleted file mode 100644 index dcead05..0000000 --- a/licenses/LuaLicense.txt +++ /dev/null @@ -1,8 +0,0 @@ - -Copyright © 1994–2024 Lua.org, PUC-Rio. - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/licenses/Sol2License.txt b/licenses/Sol2License.txt deleted file mode 100644 index 3d58545..0000000 --- a/licenses/Sol2License.txt +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2013-2022 Rapptz, ThePhD, and contributors - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/vcpkg.json b/vcpkg.json index 8f6167a..53b61fa 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -1,7 +1,5 @@ { "dependencies": [ - "lua", - "minhook", - "sol2" + "minhook" ] }