@@ -47,7 +47,7 @@ static void __declspec(naked) CrashAverted()
4747 // clang-format on
4848}
4949
50- static bool HasReadAccess (DWORD dwProtect)
50+ static bool HasReadAccess (DWORD dwProtect) noexcept
5151{
5252 if (dwProtect & PAGE_GUARD)
5353 return false ;
@@ -61,7 +61,7 @@ static bool HasReadAccess(DWORD dwProtect)
6161 dwProtect == PAGE_EXECUTE_READ || dwProtect == PAGE_EXECUTE_READWRITE || dwProtect == PAGE_EXECUTE_WRITECOPY;
6262}
6363
64- static bool HasWriteAccess (DWORD dwProtect)
64+ static bool HasWriteAccess (DWORD dwProtect) noexcept
6565{
6666 if (dwProtect & PAGE_GUARD)
6767 return false ;
@@ -71,116 +71,124 @@ static bool HasWriteAccess(DWORD dwProtect)
7171 return dwProtect == PAGE_READWRITE || dwProtect == PAGE_WRITECOPY || dwProtect == PAGE_EXECUTE_READWRITE || dwProtect == PAGE_EXECUTE_WRITECOPY;
7272}
7373
74- static bool QueryRegionCached (uintptr_t uiAddress, MEMORY_BASIC_INFORMATION& outMbi, uintptr_t & outStart, uintptr_t & outEnd)
74+ static constexpr std::size_t REGION_CACHE_SIZE = 8 ;
75+ static constexpr std::uintptr_t NUM_LOWMEM_THRESHOLD = 0x10000 ;
76+
77+ #define NUM_LOWMEM_THRESHOLD_ASM 0x10000
78+
79+ struct CachedRegion
7580{
76- static thread_local MEMORY_BASIC_INFORMATION s_mbi;
77- static thread_local uintptr_t s_start = 0 ;
78- static thread_local uintptr_t s_end = 0 ;
81+ std::uintptr_t start{};
82+ std::uintptr_t end{};
83+ DWORD state{};
84+ DWORD protect{};
85+ };
7986
80- if (s_start != 0 && uiAddress >= s_start && uiAddress <= s_end)
87+ static bool QueryRegionCached (std::uintptr_t address, DWORD& outState, DWORD& outProtect, std::uintptr_t & outEnd) noexcept
88+ {
89+ static thread_local CachedRegion s_cache[REGION_CACHE_SIZE]{};
90+ static thread_local std::size_t s_nextSlot{};
91+
92+ for (const auto & entry : s_cache)
8193 {
82- outMbi = s_mbi;
83- outStart = s_start;
84- outEnd = s_end;
85- return true ;
94+ if (entry.start != 0 && address >= entry.start && address <= entry.end )
95+ {
96+ outState = entry.state ;
97+ outProtect = entry.protect ;
98+ outEnd = entry.end ;
99+ return true ;
100+ }
86101 }
87102
88- MEMORY_BASIC_INFORMATION mbi;
89- SIZE_T mbiResult = VirtualQuery (reinterpret_cast <LPCVOID>(uiAddress), &mbi, sizeof (mbi));
90- if (mbiResult != sizeof (mbi))
103+ MEMORY_BASIC_INFORMATION mbi{};
104+ if (VirtualQuery (reinterpret_cast <LPCVOID>(address), &mbi, sizeof (mbi)) != sizeof (mbi))
91105 return false ;
92106
93- s_mbi = mbi;
94- s_start = reinterpret_cast <uintptr_t >(mbi.BaseAddress );
107+ const auto regionSize = static_cast <std::uintptr_t >(mbi.RegionSize );
108+ if (regionSize == 0 )
109+ return false ;
95110
96- const uintptr_t uiSize = static_cast <uintptr_t >(mbi.RegionSize );
97- if (uiSize == 0 )
98- s_end = s_start;
99- else
100- {
101- const uintptr_t uiEndPlusOne = s_start + uiSize;
102- s_end = (uiEndPlusOne <= s_start) ? static_cast <uintptr_t >(-1 ) : (uiEndPlusOne - 1 );
103- }
111+ auto & slot = s_cache[s_nextSlot];
112+ s_nextSlot = (s_nextSlot + 1 ) % REGION_CACHE_SIZE;
113+
114+ slot.start = reinterpret_cast <std::uintptr_t >(mbi.BaseAddress );
115+ slot.state = mbi.State ;
116+ slot.protect = mbi.Protect ;
117+
118+ const auto endPlusOne = slot.start + regionSize;
119+ slot.end = (endPlusOne <= slot.start ) ? static_cast <std::uintptr_t >(-1 ) : (endPlusOne - 1 );
104120
105- outMbi = s_mbi ;
106- outStart = s_start ;
107- outEnd = s_end ;
121+ outState = slot. state ;
122+ outProtect = slot. protect ;
123+ outEnd = slot. end ;
108124 return true ;
109125}
110126
111- static bool IsReadablePtr (const void * pPtr, size_t uiSize)
127+ static bool IsReadablePtr (const void * ptr, std:: size_t size) noexcept
112128{
113- if (pPtr == nullptr || uiSize == 0 )
129+ if (ptr == nullptr || size == 0 )
114130 return false ;
115131
116- const uintptr_t uiStart = reinterpret_cast <uintptr_t >(pPtr );
117- if (uiStart < 0x10000 )
132+ const auto start = reinterpret_cast <std:: uintptr_t >(ptr );
133+ if (start < NUM_LOWMEM_THRESHOLD )
118134 return false ;
119135
120- const uintptr_t uiEnd = uiStart + uiSize - 1 ;
121- if (uiEnd < uiStart )
136+ const auto end = start + size - 1 ;
137+ if (end < start )
122138 return false ;
123139
124- uintptr_t uiCur = uiStart ;
125- while ( true )
140+ auto cur = start ;
141+ for (;; )
126142 {
127- MEMORY_BASIC_INFORMATION mbi;
128- uintptr_t uiRegionStart = 0 ;
129- uintptr_t uiRegionEnd = 0 ;
130- if (!QueryRegionCached (uiCur, mbi, uiRegionStart, uiRegionEnd))
131- return false ;
132-
133- if (mbi.State != MEM_COMMIT)
143+ DWORD state{}, protect{};
144+ std::uintptr_t regionEnd{};
145+ if (!QueryRegionCached (cur, state, protect, regionEnd))
134146 return false ;
135147
136- if (! HasReadAccess (mbi. Protect ))
148+ if (state != MEM_COMMIT || ! HasReadAccess (protect ))
137149 return false ;
138150
139- if (uiRegionEnd >= uiEnd )
151+ if (regionEnd >= end )
140152 return true ;
141153
142- if (uiRegionEnd < uiCur || uiRegionEnd == static_cast <uintptr_t >(-1 ))
154+ if (regionEnd < cur || regionEnd == static_cast <std:: uintptr_t >(-1 ))
143155 return false ;
144156
145- uiCur = uiRegionEnd + 1 ;
157+ cur = regionEnd + 1 ;
146158 }
147159}
148160
149- static bool IsWritablePtr (void * pPtr, size_t uiSize)
161+ static bool IsWritablePtr (void * ptr, std:: size_t size) noexcept
150162{
151- if (pPtr == nullptr || uiSize == 0 )
163+ if (ptr == nullptr || size == 0 )
152164 return false ;
153165
154- const uintptr_t uiStart = reinterpret_cast <uintptr_t >(pPtr );
155- if (uiStart < 0x10000 )
166+ const auto start = reinterpret_cast <std:: uintptr_t >(ptr );
167+ if (start < NUM_LOWMEM_THRESHOLD )
156168 return false ;
157169
158- const uintptr_t uiEnd = uiStart + uiSize - 1 ;
159- if (uiEnd < uiStart )
170+ const auto end = start + size - 1 ;
171+ if (end < start )
160172 return false ;
161173
162- uintptr_t uiCur = uiStart ;
163- while ( true )
174+ auto cur = start ;
175+ for (;; )
164176 {
165- MEMORY_BASIC_INFORMATION mbi;
166- uintptr_t uiRegionStart = 0 ;
167- uintptr_t uiRegionEnd = 0 ;
168- if (!QueryRegionCached (uiCur, mbi, uiRegionStart, uiRegionEnd))
177+ DWORD state{}, protect{};
178+ std::uintptr_t regionEnd{};
179+ if (!QueryRegionCached (cur, state, protect, regionEnd))
169180 return false ;
170181
171- if (mbi. State != MEM_COMMIT)
182+ if (state != MEM_COMMIT || ! HasWriteAccess (protect) )
172183 return false ;
173184
174- if (!HasWriteAccess (mbi.Protect ))
175- return false ;
176-
177- if (uiRegionEnd >= uiEnd)
185+ if (regionEnd >= end)
178186 return true ;
179187
180- if (uiRegionEnd < uiCur || uiRegionEnd == static_cast <uintptr_t >(-1 ))
188+ if (regionEnd < cur || regionEnd == static_cast <std:: uintptr_t >(-1 ))
181189 return false ;
182190
183- uiCur = uiRegionEnd + 1 ;
191+ cur = regionEnd + 1 ;
184192 }
185193}
186194
@@ -1508,35 +1516,44 @@ static void __declspec(naked) HOOK_CrashFix_Misc34()
15081516//
15091517// Invalid list entry pointer (crash at 0x7F374A: mov esi, [eax])
15101518// (https://pastebin.com/hFduf1JB)
1519+ // WARNING: No pushad/popad with C++ calls (corrupts /GS cookie > 0xC0000409)
15111520// //////////////////////////////////////////////////////////////////////
15121521#define HOOKPOS_CrashFix_Misc35 0x7F374A
15131522#define HOOKSIZE_CrashFix_Misc35 5
15141523#define HOOKCHECK_CrashFix_Misc35 0x8B
15151524DWORD RETURN_CrashFix_Misc35 = 0x7F374F ;
15161525DWORD RETURN_CrashFix_Misc35_Abort = 0x7F3760 ;
1517- static void _declspec (naked) HOOK_CrashFix_Misc35()
1526+
1527+ static void __declspec (naked) HOOK_CrashFix_Misc35()
15181528{
15191529 MTA_VERIFY_HOOK_LOCAL_SIZE;
15201530 // clang-format off
15211531 __asm
15221532 {
1523- pushad
1533+ cmp eax, NUM_LOWMEM_THRESHOLD_ASM
1534+ jb abort
1535+
1536+ push eax
1537+ push ecx
1538+ push edx
15241539 push 4
15251540 push eax
15261541 call IsReadablePtr
15271542 add esp, 8
1528- test eax, eax
1529- popad
1530- jnz ok
1531-
1532- push 35
1533- call CrashAverted
1534- jmp RETURN_CrashFix_Misc35_Abort
1543+ test al, al
1544+ pop edx
1545+ pop ecx
1546+ pop eax
1547+ jz abort
15351548
1536- ok:
15371549 mov esi, [eax]
15381550 add eax, 0FFFFFFF8h
15391551 jmp RETURN_CrashFix_Misc35
1552+
1553+ abort:
1554+ push 35
1555+ call CrashAverted
1556+ jmp RETURN_CrashFix_Misc35_Abort
15401557 }
15411558 // clang-format on
15421559}
@@ -1555,46 +1572,31 @@ static void _declspec(naked) HOOK_CrashFix_Misc35()
15551572DWORD RETURN_CrashFix_Misc36 = 0x7F3A0F ;
15561573DWORD RETURN_CrashFix_Misc36_Abort = 0x7F3A5C ;
15571574
1558- static bool CheckTextureName (DWORD ecxValue)
1559- {
1560- return IsReadablePtr ((const void *)ecxValue, 1 );
1561- }
1562-
1563- static void _declspec (naked) HOOK_CrashFix_Misc36()
1575+ static void __declspec (naked) HOOK_CrashFix_Misc36()
15641576{
15651577 MTA_VERIFY_HOOK_LOCAL_SIZE;
15661578
15671579 // clang-format off
15681580 __asm
15691581 {
1570- cmp ebx, 0x10000
1582+ cmp ebx, NUM_LOWMEM_THRESHOLD_ASM
15711583 jb abort
1572- cmp ebx, 0x80000000
1573- jae abort
15741584
15751585 // Replicate original code
15761586 lea eax, [ebx-8 ]
15771587 lea ecx, [eax+10h]
15781588
1579- // Quick check: ecx (texture name ptr) must make sense
1580- cmp ecx, 0x10000
1589+ cmp ecx, NUM_LOWMEM_THRESHOLD_ASM
15811590 jb abort
1582- cmp ecx, 0x80000000
1583- jae abort
15841591
1585- // ecx must point readable memory
1586- // save all state before call
15871592 push eax
15881593 push ecx
15891594 push edx
1590- pushfd
1591-
1595+ push 1
15921596 push ecx
1593- call CheckTextureName
1594- add esp, 4
1597+ call IsReadablePtr
1598+ add esp, 8
15951599 test al, al
1596-
1597- popfd
15981600 pop edx
15991601 pop ecx
16001602 pop eax
@@ -1616,24 +1618,33 @@ static void _declspec(naked) HOOK_CrashFix_Misc36()
16161618//
16171619// Invalid list head pointer (crash at 0x7F39B3: mov [esi+4], edx)
16181620// (https://pastebin.com/pkWwsSih)
1621+ // WARNING: No pushad/popad with C++ calls (corrupts /GS cookie > 0xC0000409)
16191622// //////////////////////////////////////////////////////////////////////
16201623#define HOOKPOS_CrashFix_Misc37 0x7F39B3
16211624#define HOOKSIZE_CrashFix_Misc37 5
16221625#define HOOKCHECK_CrashFix_Misc37 0x89
16231626DWORD RETURN_CrashFix_Misc37 = 0x7F39B8 ;
1624- static void _declspec (naked) HOOK_CrashFix_Misc37()
1627+
1628+ static void __declspec (naked) HOOK_CrashFix_Misc37()
16251629{
16261630 MTA_VERIFY_HOOK_LOCAL_SIZE;
16271631 // clang-format off
16281632 __asm
16291633 {
1630- pushad
1634+ cmp esi, NUM_LOWMEM_THRESHOLD_ASM
1635+ jb bad
1636+
1637+ push eax
1638+ push ecx
1639+ push edx
16311640 push 8
16321641 push esi
16331642 call IsWritablePtr
16341643 add esp, 8
1635- test eax, eax
1636- popad
1644+ test al, al
1645+ pop edx
1646+ pop ecx
1647+ pop eax
16371648 jz bad
16381649
16391650 cmp esi, ecx
@@ -1683,7 +1694,8 @@ static void OnVideoMemoryExhausted()
16831694 OnRequestDeferredStreamingMemoryRelief ();
16841695}
16851696
1686- static void _declspec (naked) HOOK_CrashFix_Misc38()
1697+ // No pushad/popad with C++ calls (corrupts /GS cookie > 0xC0000409)
1698+ static void __declspec (naked) HOOK_CrashFix_Misc38()
16871699{
16881700 MTA_VERIFY_HOOK_LOCAL_SIZE;
16891701
@@ -1694,9 +1706,15 @@ static void _declspec(naked) HOOK_CrashFix_Misc38()
16941706 test esi, esi
16951707 jnz ok
16961708
1697- pushad
1709+ // Save only volatile registers before call
1710+ push eax
1711+ push ecx
1712+ push edx
16981713 call OnVideoMemoryExhausted
1699- popad
1714+ pop edx
1715+ pop ecx
1716+ pop eax
1717+
17001718 push 38
17011719 call CrashAverted
17021720 jmp RETURN_CrashFix_Misc38_Skip
0 commit comments