| 6638 | } |
| 6639 | |
| 6640 | static void InstallExitProcessHook() { |
| 6641 | HMODULE hKernel = GetModuleHandleA("kernel32.dll"); |
| 6642 | if (!hKernel) { |
| 6643 | LOG("InstallExitProcessHook: kernel32.dll not loaded (impossible) -- skipping"); |
| 6644 | return; |
| 6645 | } |
| 6646 | auto origAddr = reinterpret_cast<void*>(GetProcAddress(hKernel, "ExitProcess")); |
| 6647 | if (!origAddr) { |
| 6648 | LOG("InstallExitProcessHook: GetProcAddress(ExitProcess) returned NULL -- skipping"); |
| 6649 | return; |
| 6650 | } |
| 6651 | g_originalExitProcess = reinterpret_cast<ExitProcessFn>(origAddr); |
| 6652 | void* hookAddr = reinterpret_cast<void*>(&ExitProcessHook); |
| 6653 | |
| 6654 | // Walk every loaded module and patch its ExitProcess IAT slot. |
| 6655 | HMODULE mods[1024]; |
| 6656 | DWORD cbNeeded = 0; |
| 6657 | HANDLE hProc = GetCurrentProcess(); |
| 6658 | if (!EnumProcessModules(hProc, mods, sizeof(mods), &cbNeeded)) { |
| 6659 | LOG("InstallExitProcessHook: EnumProcessModules failed (%u)", GetLastError()); |
| 6660 | return; |
| 6661 | } |
| 6662 | DWORD modCount = cbNeeded / sizeof(HMODULE); |
| 6663 | if (modCount > 1024) modCount = 1024; |
| 6664 | int patchedModules = 0; |
| 6665 | for (DWORD i = 0; i < modCount; ++i) { |
| 6666 | // Skip kernel32 itself (its IAT entry, if any, would create a self-loop). |
| 6667 | if (mods[i] == hKernel) continue; |
| 6668 | if (PatchModuleExitProcessIat(mods[i], origAddr, hookAddr)) { |
| 6669 | ++patchedModules; |
| 6670 | } |
| 6671 | } |
| 6672 | LOG("InstallExitProcessHook: patched %d module IAT slot(s) for ExitProcess (orig=%p hook=%p)", |
| 6673 | patchedModules, origAddr, hookAddr); |
| 6674 | } |
| 6675 | |
| 6676 | void DrainPlaytimeUpdates() { |
| 6677 | DrainPlaytimeUpdateQueueOnNetThread(); |
no test coverage detected