// dllmain.cpp — leakfix.dll entry point #include #include "logging.h" #include "patches.h" #include "instr.h" namespace { 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", 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)); bool skip_patches = (no_patches[0] == '1'); leakfix::instr_install_crash_handler(); if (skip_patches) { leakfix::logf("LEAKFIX_NO_PATCHES=1 — skipping patch application (diagnostic mode)"); } else { 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; }