From 306167c6a55b778a8ba7c8f013a96ba0f8ca9102 Mon Sep 17 00:00:00 2001 From: Jacob Babor Date: Fri, 19 Jun 2026 23:09:35 -0500 Subject: [PATCH] Resolve mappin system vtable during tracking probe --- red4ext/EdgeWeightGPS/src/Main.cpp | 145 +++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) diff --git a/red4ext/EdgeWeightGPS/src/Main.cpp b/red4ext/EdgeWeightGPS/src/Main.cpp index 99df339..539956f 100644 --- a/red4ext/EdgeWeightGPS/src/Main.cpp +++ b/red4ext/EdgeWeightGPS/src/Main.cpp @@ -91,9 +91,12 @@ namespace { constexpr uint32_t kGameStateRunning = 2; constexpr uintptr_t kGpsSystemTickRva = 0x0E6B62C; +constexpr uintptr_t kMappinSystemResolverRva = 0x02BB840; constexpr uintptr_t kTrackMappinWrapperRva = 0x27C4AB4; constexpr uintptr_t kTrackMappinCoreRva = 0x27C11D0; constexpr uintptr_t kSetMappinTrackingAlternativeWrapperRva = 0x1246794; +constexpr uintptr_t kMappinSetTrackedSlotOffset = 0x220; +constexpr uintptr_t kMappinClearTrackedSlotOffset = 0x228; HMODULE gModule = nullptr; std::mutex gLogMutex; @@ -105,16 +108,27 @@ uint32_t gGpsTickLogCount = 0; uint32_t gTrackMappinWrapperLogCount = 0; uint32_t gTrackMappinCoreLogCount = 0; uint32_t gSetMappinTrackingAlternativeLogCount = 0; +uint32_t gMappinSystemSlotLogCount = 0; +uint32_t gMappinSetTrackedLogCount = 0; +uint32_t gMappinClearTrackedLogCount = 0; uint64_t gLogStartTick = 0; uint64_t gLogFrequency = 0; +void* gMappinSetTrackedTarget = nullptr; +void* gMappinClearTrackedTarget = nullptr; +bool gMappinSlotHookAttachAttempted = false; using ProbeFunc = void (*)(void* aThis, void* a2, void* a3, void* a4); using ScriptNativeVoidFunc = void (*)(void* aThis, void* aStack, void* aResult); +using MappinSystemResolverFunc = void* (*)(void* aThis); using TrackMappinCoreFunc = void (*)(void* aThis, void* aMappinHandle); +using MappinSetTrackedFunc = void (*)(void* aSystem, void* aMappinId); +using MappinClearTrackedFunc = void (*)(void* aSystem); ProbeFunc gOriginalGpsSystemTick = nullptr; ScriptNativeVoidFunc gOriginalTrackMappinWrapper = nullptr; TrackMappinCoreFunc gOriginalTrackMappinCore = nullptr; ScriptNativeVoidFunc gOriginalSetMappinTrackingAlternativeWrapper = nullptr; +MappinSetTrackedFunc gOriginalMappinSetTracked = nullptr; +MappinClearTrackedFunc gOriginalMappinClearTracked = nullptr; std::filesystem::path GetLogPath() { @@ -195,6 +209,28 @@ void LogTrackMappinCoreCall(uint32_t aCount, void* aThis, void* aMappinHandle) LogRed4ext(line.str()); } +void LogMappinSystemSlots(uint32_t aCount, void* aSystem, void* aVtable) +{ + auto* vtable = reinterpret_cast(aVtable); + auto readSlot = [vtable](uintptr_t aOffset) -> void* { + if (!vtable) + { + return nullptr; + } + + return *reinterpret_cast(vtable + aOffset); + }; + + std::ostringstream line; + line << "mappin-system slots call=" << aCount << " system=" << aSystem << " vtable=" << aVtable + << " slot220=" << readSlot(0x220) << " slot228=" << readSlot(0x228) + << " slot250=" << readSlot(0x250) << " slot2e0=" << readSlot(0x2E0) + << " slot368=" << readSlot(0x368) << " slot3a8=" << readSlot(0x3A8); + LogRed4ext(line.str()); +} + +void InspectAndHookMappinSystem(void* aThis); + const char* ReasonToString(RED4ext::v1::EMainReason aReason) { switch (aReason) @@ -389,6 +425,8 @@ void DetourTrackMappinCore(void* aThis, void* aMappinHandle) ++gTrackMappinCoreLogCount; } + InspectAndHookMappinSystem(aThis); + if (gOriginalTrackMappinCore) { gOriginalTrackMappinCore(aThis, aMappinHandle); @@ -410,6 +448,40 @@ void DetourSetMappinTrackingAlternativeWrapper(void* aThis, void* aStack, void* } } +void DetourMappinSetTracked(void* aSystem, void* aMappinId) +{ + if (gMappinSetTrackedLogCount < 64) + { + std::ostringstream line; + line << "hook MappinSystem::SetTracked slot220 call=" << gMappinSetTrackedLogCount + << " system=" << aSystem << " mappinId=" << aMappinId; + LogRed4ext(line.str()); + ++gMappinSetTrackedLogCount; + } + + if (gOriginalMappinSetTracked) + { + gOriginalMappinSetTracked(aSystem, aMappinId); + } +} + +void DetourMappinClearTracked(void* aSystem) +{ + if (gMappinClearTrackedLogCount < 64) + { + std::ostringstream line; + line << "hook MappinSystem::ClearTracked slot228 call=" << gMappinClearTrackedLogCount + << " system=" << aSystem; + LogRed4ext(line.str()); + ++gMappinClearTrackedLogCount; + } + + if (gOriginalMappinClearTracked) + { + gOriginalMappinClearTracked(aSystem); + } +} + void AttachProbeHook(const char* aName, uintptr_t aRva, void* aDetour, void** aOriginal) { if (!gSdk || !gSdk->hooking || !gSdk->hooking->Attach) @@ -427,6 +499,30 @@ void AttachProbeHook(const char* aName, uintptr_t aRva, void* aDetour, void** aO LogRed4ext(line.str()); } +void AttachAbsoluteHook(const char* aName, void* aTarget, void* aDetour, void** aOriginal) +{ + if (!gSdk || !gSdk->hooking || !gSdk->hooking->Attach) + { + LogRed4ext("absolute hook attach skipped: RED4ext hooking API unavailable"); + return; + } + + if (!aTarget) + { + std::ostringstream line; + line << "absolute hook attach skipped " << aName << ": target=null"; + LogRed4ext(line.str()); + return; + } + + const auto attached = gSdk->hooking->Attach(gHandle, aTarget, aDetour, aOriginal); + + std::ostringstream line; + line << "absolute hook attach " << aName << " target=" << aTarget + << " result=" << (attached ? "ok" : "failed") << " original=" << (aOriginal ? *aOriginal : nullptr); + LogRed4ext(line.str()); +} + void DetachProbeHook(const char* aName, uintptr_t aRva) { if (!gSdk || !gSdk->hooking || !gSdk->hooking->Detach) @@ -442,6 +538,53 @@ void DetachProbeHook(const char* aName, uintptr_t aRva) LogRed4ext(line.str()); } +void DetachAbsoluteHook(const char* aName, void* aTarget) +{ + if (!gSdk || !gSdk->hooking || !gSdk->hooking->Detach || !aTarget) + { + return; + } + + const auto detached = gSdk->hooking->Detach(gHandle, aTarget); + + std::ostringstream line; + line << "absolute hook detach " << aName << " target=" << aTarget + << " result=" << (detached ? "ok" : "failed"); + LogRed4ext(line.str()); +} + +void InspectAndHookMappinSystem(void* aThis) +{ + auto* resolver = reinterpret_cast( + reinterpret_cast(GetModuleHandleW(nullptr)) + kMappinSystemResolverRva); + void* system = resolver ? resolver(aThis) : nullptr; + auto* vtable = system ? *reinterpret_cast(system) : nullptr; + + if (gMappinSystemSlotLogCount < 16) + { + LogMappinSystemSlots(gMappinSystemSlotLogCount, system, vtable); + ++gMappinSystemSlotLogCount; + } + + if (gMappinSlotHookAttachAttempted || !vtable) + { + return; + } + + gMappinSlotHookAttachAttempted = true; + gMappinSetTrackedTarget = + *reinterpret_cast(reinterpret_cast(vtable) + kMappinSetTrackedSlotOffset); + gMappinClearTrackedTarget = + *reinterpret_cast(reinterpret_cast(vtable) + kMappinClearTrackedSlotOffset); + + AttachAbsoluteHook("MappinSystem::SetTracked slot220", gMappinSetTrackedTarget, + reinterpret_cast(&DetourMappinSetTracked), + reinterpret_cast(&gOriginalMappinSetTracked)); + AttachAbsoluteHook("MappinSystem::ClearTracked slot228", gMappinClearTrackedTarget, + reinterpret_cast(&DetourMappinClearTracked), + reinterpret_cast(&gOriginalMappinClearTracked)); +} + bool OnRunningEnter(void* aApp) { std::ostringstream line; @@ -531,6 +674,8 @@ RED4EXT_C_EXPORT bool RED4EXT_CALL Main(RED4ext::v1::PluginHandle aHandle, RED4e DetachProbeHook("TrackMappinWrapper", kTrackMappinWrapperRva); DetachProbeHook("TrackMappinCore", kTrackMappinCoreRva); DetachProbeHook("SetMappinTrackingAlternativeWrapper", kSetMappinTrackingAlternativeWrapperRva); + DetachAbsoluteHook("MappinSystem::SetTracked slot220", gMappinSetTrackedTarget); + DetachAbsoluteHook("MappinSystem::ClearTracked slot228", gMappinClearTrackedTarget); } return true;