Resolve mappin system vtable during tracking probe

This commit is contained in:
2026-06-19 23:09:35 -05:00
parent da396f94c3
commit 306167c6a5
+145
View File
@@ -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<uint8_t*>(aVtable);
auto readSlot = [vtable](uintptr_t aOffset) -> void* {
if (!vtable)
{
return nullptr;
}
return *reinterpret_cast<void**>(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<MappinSystemResolverFunc>(
reinterpret_cast<uintptr_t>(GetModuleHandleW(nullptr)) + kMappinSystemResolverRva);
void* system = resolver ? resolver(aThis) : nullptr;
auto* vtable = system ? *reinterpret_cast<void***>(system) : nullptr;
if (gMappinSystemSlotLogCount < 16)
{
LogMappinSystemSlots(gMappinSystemSlotLogCount, system, vtable);
++gMappinSystemSlotLogCount;
}
if (gMappinSlotHookAttachAttempted || !vtable)
{
return;
}
gMappinSlotHookAttachAttempted = true;
gMappinSetTrackedTarget =
*reinterpret_cast<void**>(reinterpret_cast<uintptr_t>(vtable) + kMappinSetTrackedSlotOffset);
gMappinClearTrackedTarget =
*reinterpret_cast<void**>(reinterpret_cast<uintptr_t>(vtable) + kMappinClearTrackedSlotOffset);
AttachAbsoluteHook("MappinSystem::SetTracked slot220", gMappinSetTrackedTarget,
reinterpret_cast<void*>(&DetourMappinSetTracked),
reinterpret_cast<void**>(&gOriginalMappinSetTracked));
AttachAbsoluteHook("MappinSystem::ClearTracked slot228", gMappinClearTrackedTarget,
reinterpret_cast<void*>(&DetourMappinClearTracked),
reinterpret_cast<void**>(&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;