// dllmain.cpp — leakfix.dll entry point // // iter-5 (2026-05-20): patch application is deferred to a worker // thread that sleeps ~30 seconds before applying. This matches the // timing of the Python runtime patcher (tools/fleet_monitor.sh), // which lands its patches well after Decal init is complete. The // PE-import-load → DllMain → immediate apply_all_patches sequence // used in iter-1..iter-4 lost the race with Decal's own hook // installation and crashed some accounts (Unkle Leo most reliably). // See feedback_dll_load_order_conflict.md. // // The SEH crash handler is still installed immediately so any // crashes during the 30s window (including ones caused by Decal) // are captured. #include #include "logging.h" #include "patches.h" #include "instr.h" namespace { bool g_skip_patches = false; DWORD WINAPI deferred_patch_thread(LPVOID) { // Give Decal / UtilityBelt time to finish their own hook // installation before we lay our patches on top. 30s matches // the Python cascade's observed-good timing. Sleep(30000); leakfix::logf("deferred-patch thread: woke after 30s, applying patches"); if (g_skip_patches) { leakfix::logf("LEAKFIX_NO_PATCHES=1 — skipping patch application (diagnostic mode)"); } else { leakfix::apply_all_patches(); } leakfix::instr_start_periodic_scan(); return 0; } void on_attach() { char dll_path[MAX_PATH] = {0}; GetModuleFileNameA((HMODULE)GetModuleHandleA("leakfix.dll"), dll_path, MAX_PATH); // Log next to the DLL itself char log_path[MAX_PATH] = {0}; char* slash = nullptr; for (char* p = dll_path; *p; ++p) if (*p == '\\' || *p == '/') slash = p; if (slash) { size_t prefix = (size_t)(slash - dll_path) + 1; memcpy(log_path, dll_path, prefix); strcpy(log_path + prefix, "leakfix.log"); } else { strcpy(log_path, "leakfix.log"); } leakfix::log_init(log_path); leakfix::logf("attach: dll=%s (iter-5 deferred-patch)", dll_path); // Kill switch — set LEAKFIX_NO_PATCHES=1 in env to skip patch // application. Instrumentation still runs. Used to bisect crashes: // if the no-patches variant survives, the patches are the trigger. char no_patches[8] = {0}; GetEnvironmentVariableA("LEAKFIX_NO_PATCHES", no_patches, sizeof(no_patches)); g_skip_patches = (no_patches[0] == '1'); // Crash handler installed immediately so the 30s pre-patch window // is still observable if Decal/UB crashes the process. leakfix::instr_install_crash_handler(); HANDLE h = CreateThread(nullptr, 0, deferred_patch_thread, nullptr, 0, nullptr); if (h) { CloseHandle(h); leakfix::logf("deferred-patch thread spawned"); } else { // CreateThread failure is extraordinary — fall back to the // old in-DllMain apply so we at least get patches eventually. leakfix::logf("CreateThread failed (err=%lu) — falling back to in-DllMain apply", GetLastError()); if (!g_skip_patches) leakfix::apply_all_patches(); leakfix::instr_start_periodic_scan(); } } } // anon // Exported stub so PE-import-table patching of acclient.exe can name // a real symbol for the OS loader to resolve. Doing nothing is fine — // just being present in the DLL is what makes the import valid. extern "C" __declspec(dllexport) int __cdecl leakfix_init() { return 0; } extern "C" BOOL APIENTRY DllMain(HMODULE h, DWORD reason, LPVOID) { switch (reason) { case DLL_PROCESS_ATTACH: DisableThreadLibraryCalls(h); on_attach(); break; case DLL_PROCESS_DETACH: leakfix::instr_stop_periodic_scan(); leakfix::logf("detach"); leakfix::log_close(); break; } return TRUE; }