Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions Client/game_sa/HookSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,24 @@ bool HookInstall(DWORD dwInstallAddress, T dwHookHandler, int iJmpCodeSize = 5)
}
}

// For install addresses outside the ranges MemCpy knows how to unprotect; the page must have
// been made writable beforehand (see SetInitialVirtualProtect)
template <typename T>
bool HookInstallFast(DWORD dwInstallAddress, T dwHookHandler, int iJmpCodeSize = 5)
{
BYTE JumpBytes[MAX_JUMPCODE_SIZE];
MemSetFast(JumpBytes, 0x90, MAX_JUMPCODE_SIZE);
if (CreateJump(dwInstallAddress, (DWORD)FunctionPointerToVoidP(dwHookHandler), JumpBytes))
{
MemCpyFast((PVOID)dwInstallAddress, JumpBytes, iJmpCodeSize);
return true;
}
else
{
return false;
}
}

// Auto detect requirement of US/EU hook installation
#define EZHookInstall(type) HookInstall(HOOKPOS_##type, (DWORD)HOOK_##type, HOOKSIZE_##type);

Expand Down
39 changes: 39 additions & 0 deletions Client/multiplayer_sa/CMultiplayerSA_CrashFixHacks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4092,6 +4092,40 @@ static int _cdecl CFileLoader_LoadVehicleObject_sscanf(const char* s, const char
rearWheelSize, wheelUpgradeClass);
}

//////////////////////////////////////////////////////////////////////////////////////////
//
// CPathFind's per map area path node loader calls malloc() for m_pPathNodes[area] (offset
// 0x804) without checking the result, then loops over it using m_dwNumNodes[area] (offset
// 0xfa4) as the count. If the allocation fails, that loop dereferences a null pointer and
// crashes.
//
// Right where the node count gets loaded for the loop's bound check, this hook also checks
// whether m_pPathNodes[area] is null, and if so forces the count to 0 so the loop is skipped.
//
//////////////////////////////////////////////////////////////////////////////////////////
#define HOOKPOS_CPathFind_LoadPathNodeCount_Mid 0x0156F966
#define HOOKSIZE_CPathFind_LoadPathNodeCount_Mid 7
DWORD RETURN_CPathFind_LoadPathNodeCount_Mid = 0x0156F96D;
static void __declspec(naked) HOOK_CPathFind_LoadPathNodeCount_Mid()
{
MTA_VERIFY_HOOK_LOCAL_SIZE;

// clang-format off
__asm
{
// Replicate the overwritten instruction: EAX = m_dwNumNodes[area]
mov eax, dword ptr [esi + edi*4 + 0x0fa4]

// If m_pPathNodes[area] failed to allocate, force the node count to 0 for this loop
cmp dword ptr [esi + edi*4 + 0x804], 0
jne nodesOk
xor eax, eax
nodesOk:
jmp RETURN_CPathFind_LoadPathNodeCount_Mid
}
// clang-format on
}

//////////////////////////////////////////////////////////////////////////////////////////
//
// Setup hooks for CrashFixHacks
Expand Down Expand Up @@ -4191,4 +4225,9 @@ void CMultiplayerSA::InitHooks_CrashFixHacks()

// Fix uninitialized wheel scale in CFileLoader::LoadVehicleObject on Win11 24H2
HookInstallCall(CALL_CFileLoader_LoadVehicleObject_sscanf, (DWORD)CFileLoader_LoadVehicleObject_sscanf);

// This address sits further into the executable than EZHookInstall's normal range check
// allows, so it is installed with HookInstallFast instead; the page is unprotected up front
// in SetInitialVirtualProtect
HookInstallFast(HOOKPOS_CPathFind_LoadPathNodeCount_Mid, (DWORD)HOOK_CPathFind_LoadPathNodeCount_Mid, HOOKSIZE_CPathFind_LoadPathNodeCount_Mid);
}
3 changes: 3 additions & 0 deletions Shared/sdk/SharedUtil.MemAccess.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ namespace SharedUtil
VirtualProtect((LPVOID)0x6AE000, 0x1000, PAGE_EXECUTE_READWRITE, &oldProt);
VirtualProtect((LPVOID)0x729000, 0x1000, PAGE_EXECUTE_READWRITE, &oldProt);
VirtualProtect((LPVOID)0x742000, 0x1000, PAGE_EXECUTE_READWRITE, &oldProt);

// Used by the CPathFind crash fix in multiplayer_sa/CMultiplayerSA_CrashFixHacks.cpp, installed via the Fast path.
VirtualProtect((LPVOID)0x156F000, 0x1000, PAGE_EXECUTE_READWRITE, &oldProt);
}

// Returns true if pAddr is in slow mem area
Expand Down
Loading