From 02bcf33beadc5af0432ab5e8c09b802563b758b0 Mon Sep 17 00:00:00 2001 From: ASpoonPlaysGames <66967891+ASpoonPlaysGames@users.noreply.github.com> Date: Mon, 26 Aug 2024 13:33:59 +0100 Subject: [PATCH 1/7] Run callbacks for nested modules, and prevent running callbacks multiple times for the same module. --- primedev/thirdparty/silver-bun/module.cpp | 15 +++++++++++++++ primedev/thirdparty/silver-bun/module.h | 2 ++ primedev/windows/libsys.cpp | 16 ++++++++++++++++ 3 files changed, 33 insertions(+) diff --git a/primedev/thirdparty/silver-bun/module.cpp b/primedev/thirdparty/silver-bun/module.cpp index 84f4da9e2..dceb602a4 100644 --- a/primedev/thirdparty/silver-bun/module.cpp +++ b/primedev/thirdparty/silver-bun/module.cpp @@ -66,6 +66,21 @@ void CModule::Init() m_ModuleSections.push_back(ModuleSections_t(reinterpret_cast(hCurrentSection.Name), static_cast(m_pModuleBase + hCurrentSection.VirtualAddress), hCurrentSection.SizeOfRawData)); // Push back a struct with the section data. } + + // Get the location of IMAGE_IMPORT_DESCRIPTOR for this module by adding the IMAGE_DIRECTORY_ENTRY_IMPORT relative virtual address onto our + // module base address. + IMAGE_IMPORT_DESCRIPTOR* pImageImportDescriptors = reinterpret_cast( + m_pModuleBase + m_pNTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress); + if (!pImageImportDescriptors) + return; + + for (IMAGE_IMPORT_DESCRIPTOR* pIID = pImageImportDescriptors; pIID->Name != 0; pIID++) + { + // Get virtual relative Address of the imported module name. Then add module base Address to get the actual location. + const char* szImportedModuleName = reinterpret_cast(reinterpret_cast(m_pModuleBase + pIID->Name)); + + m_vImportedModules.push_back(szImportedModuleName); + } } //----------------------------------------------------------------------------- diff --git a/primedev/thirdparty/silver-bun/module.h b/primedev/thirdparty/silver-bun/module.h index 5683ee146..cc5130866 100644 --- a/primedev/thirdparty/silver-bun/module.h +++ b/primedev/thirdparty/silver-bun/module.h @@ -52,6 +52,7 @@ class CModule ModuleSections_t GetSectionByName(const char* szSectionName) const; inline const std::vector& GetSections() const { return m_ModuleSections; } + inline const std::vector& GetImportedModules() const { return m_vImportedModules; } inline uintptr_t GetModuleBase(void) const { return m_pModuleBase; } inline DWORD GetModuleSize(void) const { return m_nModuleSize; } inline const std::string& GetModuleName(void) const { return m_ModuleName; } @@ -73,4 +74,5 @@ class CModule uintptr_t m_pModuleBase; DWORD m_nModuleSize; std::vector m_ModuleSections; + std::vector m_vImportedModules; }; diff --git a/primedev/windows/libsys.cpp b/primedev/windows/libsys.cpp index 501eae687..0aff820b7 100644 --- a/primedev/windows/libsys.cpp +++ b/primedev/windows/libsys.cpp @@ -18,15 +18,31 @@ ILoadLibraryExW o_LoadLibraryExW = nullptr; //----------------------------------------------------------------------------- void LibSys_RunModuleCallbacks(HMODULE hModule) { + // Modules that we have already ran callbacks for. + // Note: If we ever hook unloading modules, then this will need updating to handle removal etc. + static std::vector vCalledModules; + if (!hModule) { return; } + // If we have already ran callbacks for this module, don't run them again. + if (std::find(vCalledModules.begin(), vCalledModules.end(), hModule) != vCalledModules.end()) + { + return; + } + vCalledModules.push_back(hModule); + // Get module base name in ASCII as noone wants to deal with unicode CHAR szModuleName[MAX_PATH]; GetModuleBaseNameA(GetCurrentProcess(), hModule, szModuleName, MAX_PATH); + // Run calllbacks for all imported modules + CModule cModule(hModule); + for (const std::string& svImport : cModule.GetImportedModules()) + LibSys_RunModuleCallbacks(GetModuleHandleA(svImport.c_str())); + // DevMsg(eLog::NONE, "%s\n", szModuleName); // Call callbacks From 5de4d8121eaffcba9bf3997b5e76a1029460f443 Mon Sep 17 00:00:00 2001 From: ASpoonPlaysGames <66967891+ASpoonPlaysGames@users.noreply.github.com> Date: Sun, 25 Aug 2024 23:51:52 +0100 Subject: [PATCH 2/7] Manually hook LoadSampleMetadata --- primedev/client/audio.cpp | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/primedev/client/audio.cpp b/primedev/client/audio.cpp index 635014141..f29efd89a 100644 --- a/primedev/client/audio.cpp +++ b/primedev/client/audio.cpp @@ -389,14 +389,12 @@ bool ShouldPlayAudioEvent(const char* eventName, const std::shared_ptrsecond; if (!ShouldPlayAudioEvent(eventName, overrideData)) - return LoadSampleMetadata(sample, audioBuffer, audioBufferLength, audioType); + return o_pLoadSampleMetadata(sample, audioBuffer, audioBufferLength, audioType); void* data = 0; unsigned int dataLength = 0; @@ -479,7 +477,7 @@ bool, __fastcall, (void* sample, void* audioBuffer, unsigned int audioBufferLeng if (!data) { spdlog::warn("Could not fetch override sample data for event {}! Using original data instead.", eventName); - return LoadSampleMetadata(sample, audioBuffer, audioBufferLength, audioType); + return o_pLoadSampleMetadata(sample, audioBuffer, audioBufferLength, audioType); } audioBuffer = data; @@ -490,7 +488,7 @@ bool, __fastcall, (void* sample, void* audioBuffer, unsigned int audioBufferLeng *(unsigned int*)((uintptr_t)sample + 0xF0) = audioBufferLength; // 64 - Auto-detect sample type - bool res = LoadSampleMetadata(sample, audioBuffer, audioBufferLength, 64); + bool res = o_pLoadSampleMetadata(sample, audioBuffer, audioBufferLength, 64); if (!res) spdlog::error("LoadSampleMetadata failed! The game will crash :("); @@ -517,6 +515,12 @@ void, __fastcall, (int level, const char* string)) spdlog::info("[MSS] {} - {}", level, string); } +ON_DLL_LOAD("mileswin64.dll", MilesWin64_Audio, (CModule module)) +{ + o_pLoadSampleMetadata = module.Offset(0xF110).RCast(); + HookAttach(&(PVOID&)o_pLoadSampleMetadata, (PVOID)h_LoadSampleMetadata); +} + ON_DLL_LOAD_RELIESON("engine.dll", MilesLogFuncHooks, ConVar, (CModule module)) { Cvar_mileslog_enable = new ConVar("mileslog_enable", "0", FCVAR_NONE, "Enables/disables whether the mileslog func should be logged"); From 80ade15db43bb5840f3fabf59baaa952f9cac488 Mon Sep 17 00:00:00 2001 From: ASpoonPlaysGames <66967891+ASpoonPlaysGames@users.noreply.github.com> Date: Sun, 25 Aug 2024 23:54:57 +0100 Subject: [PATCH 3/7] Manually hook sub_1800294C0 --- primedev/client/audio.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/primedev/client/audio.cpp b/primedev/client/audio.cpp index f29efd89a..c250be957 100644 --- a/primedev/client/audio.cpp +++ b/primedev/client/audio.cpp @@ -495,13 +495,11 @@ static bool __fastcall h_LoadSampleMetadata(void* sample, void* audioBuffer, uns return res; } -// clang-format off -AUTOHOOK(sub_1800294C0, mileswin64.dll + 0x294C0, -void*, __fastcall, (void* a1, void* a2)) -// clang-format on +static void* (__fastcall* o_pSub_1800294C0)(void* a1, void* a2) = nullptr; +static void* __fastcall h_Sub_1800294C0(void* a1, void* a2) { pszAudioEventName = reinterpret_cast((*((__int64*)a2 + 6))); - return sub_1800294C0(a1, a2); + return o_pSub_1800294C0(a1, a2); } // clang-format off @@ -519,6 +517,9 @@ ON_DLL_LOAD("mileswin64.dll", MilesWin64_Audio, (CModule module)) { o_pLoadSampleMetadata = module.Offset(0xF110).RCast(); HookAttach(&(PVOID&)o_pLoadSampleMetadata, (PVOID)h_LoadSampleMetadata); + + o_pSub_1800294C0 = module.Offset(0x294C0).RCast(); + HookAttach(&(PVOID&)o_pSub_1800294C0, (PVOID)h_Sub_1800294C0); } ON_DLL_LOAD_RELIESON("engine.dll", MilesLogFuncHooks, ConVar, (CModule module)) From 744569319fc303381967b4a0b5bca2fcf41dc741 Mon Sep 17 00:00:00 2001 From: ASpoonPlaysGames <66967891+ASpoonPlaysGames@users.noreply.github.com> Date: Sun, 25 Aug 2024 23:57:21 +0100 Subject: [PATCH 4/7] Manually hook MilesLog --- primedev/client/audio.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/primedev/client/audio.cpp b/primedev/client/audio.cpp index c250be957..8862a9dbc 100644 --- a/primedev/client/audio.cpp +++ b/primedev/client/audio.cpp @@ -502,10 +502,8 @@ static void* __fastcall h_Sub_1800294C0(void* a1, void* a2) return o_pSub_1800294C0(a1, a2); } -// clang-format off -AUTOHOOK(MilesLog, client.dll + 0x57DAD0, -void, __fastcall, (int level, const char* string)) -// clang-format on +static void (__fastcall* o_pMilesLog)(int level, const char* string) = nullptr; +static void __fastcall h_MilesLog(int level, const char* string) { if (!Cvar_mileslog_enable->GetBool()) return; @@ -531,6 +529,9 @@ ON_DLL_LOAD_CLIENT_RELIESON("client.dll", AudioHooks, ConVar, (CModule module)) { AUTOHOOK_DISPATCH() + o_pMilesLog = module.Offset(0x57DAD0).RCast(); + HookAttach(&(PVOID&)o_pMilesLog, (PVOID)h_MilesLog); + Cvar_ns_print_played_sounds = new ConVar("ns_print_played_sounds", "0", FCVAR_NONE, ""); MilesStopAll = module.Offset(0x580850).RCast(); } From 4ac2e70cf448bc6e922c7e03c85f420bd0e0cb3c Mon Sep 17 00:00:00 2001 From: ASpoonPlaysGames <66967891+ASpoonPlaysGames@users.noreply.github.com> Date: Sun, 25 Aug 2024 23:59:35 +0100 Subject: [PATCH 5/7] Remove AUTOHOOK_INIT and AUTOHOOK_DISPATCH --- primedev/client/audio.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/primedev/client/audio.cpp b/primedev/client/audio.cpp index 8862a9dbc..d32f1b60f 100644 --- a/primedev/client/audio.cpp +++ b/primedev/client/audio.cpp @@ -11,8 +11,6 @@ namespace fs = std::filesystem; -AUTOHOOK_INIT() - static const char* pszAudioEventName; ConVar* Cvar_mileslog_enable; @@ -527,8 +525,6 @@ ON_DLL_LOAD_RELIESON("engine.dll", MilesLogFuncHooks, ConVar, (CModule module)) ON_DLL_LOAD_CLIENT_RELIESON("client.dll", AudioHooks, ConVar, (CModule module)) { - AUTOHOOK_DISPATCH() - o_pMilesLog = module.Offset(0x57DAD0).RCast(); HookAttach(&(PVOID&)o_pMilesLog, (PVOID)h_MilesLog); From 44a050bf9f115da03ff6862680e315143166cfb9 Mon Sep 17 00:00:00 2001 From: ASpoonPlaysGames <66967891+ASpoonPlaysGames@users.noreply.github.com> Date: Mon, 26 Aug 2024 00:02:54 +0100 Subject: [PATCH 6/7] Formatting --- primedev/client/audio.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/primedev/client/audio.cpp b/primedev/client/audio.cpp index d32f1b60f..15f5c2aec 100644 --- a/primedev/client/audio.cpp +++ b/primedev/client/audio.cpp @@ -387,7 +387,7 @@ bool ShouldPlayAudioEvent(const char* eventName, const std::shared_ptr((*((__int64*)a2 + 6))); return o_pSub_1800294C0(a1, a2); } -static void (__fastcall* o_pMilesLog)(int level, const char* string) = nullptr; +static void(__fastcall* o_pMilesLog)(int level, const char* string) = nullptr; static void __fastcall h_MilesLog(int level, const char* string) { if (!Cvar_mileslog_enable->GetBool()) From 7c41e710dcd1db51fba25aac3251fddd3447e4a4 Mon Sep 17 00:00:00 2001 From: Jack <66967891+ASpoonPlaysGames@users.noreply.github.com> Date: Sun, 25 Aug 2024 21:49:45 +0100 Subject: [PATCH 7/7] Fix manual hooks storing their name badly (#774) Fix manual hooks storing their name wrong and move to just using std::string since it's much more convenient and this isn't C --- primedev/core/hooks.cpp | 22 ++++++---------------- primedev/core/hooks.h | 2 +- 2 files changed, 7 insertions(+), 17 deletions(-) diff --git a/primedev/core/hooks.cpp b/primedev/core/hooks.cpp index 5026f837b..d8fad4094 100644 --- a/primedev/core/hooks.cpp +++ b/primedev/core/hooks.cpp @@ -85,19 +85,9 @@ void __fileAutohook::DispatchForModule(const char* pModuleName) hook->Dispatch(); } -ManualHook::ManualHook(const char* funcName, LPVOID func) : pHookFunc(func), ppOrigFunc(nullptr) -{ - const size_t iFuncNameStrlen = strlen(funcName); - pFuncName = new char[iFuncNameStrlen]; - memcpy(pFuncName, funcName, iFuncNameStrlen); -} +ManualHook::ManualHook(const char* funcName, LPVOID func) : svFuncName(funcName), pHookFunc(func), ppOrigFunc(nullptr) {} -ManualHook::ManualHook(const char* funcName, LPVOID* orig, LPVOID func) : pHookFunc(func), ppOrigFunc(orig) -{ - const size_t iFuncNameStrlen = strlen(funcName); - pFuncName = new char[iFuncNameStrlen]; - memcpy(pFuncName, funcName, iFuncNameStrlen); -} +ManualHook::ManualHook(const char* funcName, LPVOID* orig, LPVOID func) : svFuncName(funcName), pHookFunc(func), ppOrigFunc(orig) {} bool ManualHook::Dispatch(LPVOID addr, LPVOID* orig) { @@ -105,19 +95,19 @@ bool ManualHook::Dispatch(LPVOID addr, LPVOID* orig) ppOrigFunc = orig; if (!addr) - spdlog::error("Address for hook {} is invalid", pFuncName); + spdlog::error("Address for hook {} is invalid", svFuncName); else if (MH_CreateHook(addr, pHookFunc, ppOrigFunc) == MH_OK) { if (MH_EnableHook(addr) == MH_OK) { - spdlog::info("Enabling hook {}", pFuncName); + spdlog::info("Enabling hook {}", svFuncName); return true; } else - spdlog::error("MH_EnableHook failed for function {}", pFuncName); + spdlog::error("MH_EnableHook failed for function {}", svFuncName); } else - spdlog::error("MH_CreateHook failed for function {}", pFuncName); + spdlog::error("MH_CreateHook failed for function {}", svFuncName); return false; } diff --git a/primedev/core/hooks.h b/primedev/core/hooks.h index 2a2180dab..023b7411e 100644 --- a/primedev/core/hooks.h +++ b/primedev/core/hooks.h @@ -278,7 +278,7 @@ class __autohook class ManualHook { public: - char* pFuncName; + std::string svFuncName; LPVOID pHookFunc; LPVOID* ppOrigFunc;