// Included inside 90CN.cpp's namespace after its existing patch helpers. // Only the evidenced UI update iterator call-site is patched, never the CRT. uintptr_t g_uiIteratorNativeAdvance = 0; uintptr_t g_uiIteratorAdvanceResume = 0; uintptr_t g_uiIteratorNextBucket = 0; volatile LONG g_uiIteratorInvalidations = 0; int __stdcall CanAdvanceUiUpdateIterator(unsigned int bucket, const uintptr_t* owner, const uintptr_t* iterator) { dnf90::UiIteratorSnapshot s = {}; __try { s.owner = reinterpret_cast(owner); if (owner && iterator) { s.proxy = owner[0]; s.iteratorProxy = iterator[0]; s.begin = owner[1]; s.end = owner[2]; s.current = iterator[2]; if (s.proxy && s.iteratorProxy == s.proxy) s.proxyOwner = *reinterpret_cast(s.proxy); } } __except (EXCEPTION_EXECUTE_HANDLER) { s.proxyOwner = 0; } if (dnf90::CanAdvanceUiIterator(s)) return 1; LONG n = InterlockedIncrement(&g_uiIteratorInvalidations); if (n <= 16) LogLine("ui-update iterator invalidated bucket=%u owner=%08X " "proxy=%08X iter_proxy=%08X begin=%08X end=%08X current=%08X " "action=defer_remaining_bucket_until_next_frame hit=%ld", bucket, s.owner, s.proxy, s.iteratorProxy, s.begin, s.end, s.current, n); return 0; } __declspec(naked) void ProxyUiUpdateIteratorAdvance() { __asm { // Entry is a JMP replacing CALL; result pointer and dummy are on stack. push eax push ecx push edx push ecx push dword ptr [ebp - 14h] push dword ptr [ebp - 1Ch] call CanAdvanceUiUpdateIterator test eax, eax pop edx pop ecx pop eax jz invalidated call dword ptr [g_uiIteratorNativeAdvance] jmp dword ptr [g_uiIteratorAdvanceResume] invalidated: // Native ++ would have consumed two arguments (ret 8). Do not compare // an invalid iterator again; the next bucket constructs a fresh one. add esp, 8 jmp dword ptr [g_uiIteratorNextBucket] } } bool InstallUiIteratorAdvanceCompatibility() { const uintptr_t site = g_dnfBase + 0x02BF2732; static const unsigned char kLoopContract[] = { 0x8B,0x0F,0x8B,0x11,0x8B,0x42,0x10,0xFF,0xD0,0x6A,0x00, 0x8D,0x4D,0xB4,0x51,0x8D,0x4D,0xD4,0xE8,0x19,0x02,0x7F,0xFD, 0xE9,0x77,0xFF,0xFF,0xFF,0x8B,0x45,0xE4,0x83,0x45,0xEC,0x14, 0x40,0x89,0x45,0xE4,0x3D,0x87,0x04,0x00,0x00,0x0F,0x8C,0x55,0xFF,0xFF,0xFF }; static const unsigned char kBeginContract[] = { 0x55,0x8B,0xEC,0x8B,0x45,0x08,0x8B,0x51,0x04,0x8B,0x09, 0xC7,0x40,0x04,0,0,0,0,0x89,0x50,0x08,0x89,0x08,0x5D,0xC2,0x04,0 }; static const unsigned char kAdvanceContract[] = { 0x55,0x8B,0xEC,0x8B,0x45,0x08,0x8B,0x11,0x56,0xC7,0x00,0,0,0,0 }; if (!BytesMatch(reinterpret_cast(g_dnfBase+0x02BF2720), kLoopContract,sizeof(kLoopContract)) || !BytesMatch(reinterpret_cast(g_dnfBase+0x00A44ED0), kBeginContract,sizeof(kBeginContract)) || !BytesMatch(reinterpret_cast(g_dnfBase+0x003E2950), kAdvanceContract,sizeof(kAdvanceContract))) { LogLine("ui-update iterator compatibility disabled: native signature mismatch"); return false; } g_uiIteratorNativeAdvance = g_dnfBase + 0x003E2950; g_uiIteratorAdvanceResume = g_dnfBase + 0x02BF2737; g_uiIteratorNextBucket = g_dnfBase + 0x02BF273C; DWORD oldProtection = 0; if (!VirtualProtect(reinterpret_cast(site),5,PAGE_EXECUTE_READWRITE,&oldProtection)) { LogLine("ui-update iterator compatibility protection failed error=%lu",GetLastError()); return false; } unsigned char jump[5] = {0xE9}; *reinterpret_cast(jump+1) = static_cast( reinterpret_cast(&ProxyUiUpdateIteratorAdvance)-static_cast(site+5)); memcpy(reinterpret_cast(site),jump,sizeof(jump)); DWORD ignored = 0; VirtualProtect(reinterpret_cast(site),5,oldProtection,&ignored); FlushInstructionCache(GetCurrentProcess(),reinterpret_cast(site),5); LogLine("ui-update mutation-safe iterator installed site=%08X next_bucket=%08X",site,g_uiIteratorNextBucket); return true; }