#ifndef EXIT_PROCESS_H #define EXIT_PROCESS_H /* * This file contains functions to terminate a Win32 process, as gently as * possible. * * At first, we will attempt to inject a thread that calls ExitProcess(). If * that fails, we will fall back to terminating the entire process tree. * * For simplicity, these functions are marked as file-local. */ #include /* * Terminates the process corresponding to the process ID and all of its * directly and indirectly spawned subprocesses. * * This way of terminating the processes is not gentle: the processes get * no chance of cleaning up after themselves (closing file handles, removing * .lock files, terminating spawned processes (if any), etc). */ static int terminate_process_tree(HANDLE main_process, int exit_status) { HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); PROCESSENTRY32 entry; DWORD pids[16384]; int max_len = sizeof(pids) / sizeof(*pids), i, len, ret = 0; pid_t pid = GetProcessId(main_process); pids[0] = (DWORD)pid; len = 1; /* * Even if Process32First()/Process32Next() seem to traverse the * processes in topological order (i.e. parent processes before * child processes), there is nothing in the Win32 API documentation * suggesting that this is guaranteed. * * Therefore, run through them at least twice and stop when no more * process IDs were added to the list. */ for (;;) { int orig_len = len; memset(&entry, 0, sizeof(entry)); entry.dwSize = sizeof(entry); if (!Process32First(snapshot, &entry)) break; do { for (i = len - 1; i >= 0; i--) { if (pids[i] == entry.th32ProcessID) break; if (pids[i] == entry.th32ParentProcessID) pids[len++] = entry.th32ProcessID; } } while (len < max_len && Process32Next(snapshot, &entry)); if (orig_len == len || len >= max_len) break; } for (i = len - 1; i > 0; i--) { HANDLE process = OpenProcess(PROCESS_TERMINATE, FALSE, pids[i]); if (process) { if (!TerminateProcess(process, exit_status)) ret = -1; CloseHandle(process); } } if (!TerminateProcess(main_process, exit_status)) ret = -1; CloseHandle(main_process); return ret; } /** * Determine whether a process runs in the same architecture as the current * one. That test is required before we assume that GetProcAddress() returns * a valid address *for the target process*. */ static inline int process_architecture_matches_current(HANDLE process) { static BOOL current_is_wow = -1; BOOL is_wow; if (current_is_wow == -1 && !IsWow64Process (GetCurrentProcess(), ¤t_is_wow)) current_is_wow = -2; if (current_is_wow == -2) return 0; /* could not determine current process' WoW-ness */ if (!IsWow64Process (process, &is_wow)) return 0; /* cannot determine */ return is_wow == current_is_wow; } /** * Inject a thread into the given process that runs ExitProcess(). * * Note: as kernel32.dll is loaded before any process, the other process and * this process will have ExitProcess() at the same address. * * This function expects the process handle to have the access rights for * CreateRemoteThread(): PROCESS_CREATE_THREAD, PROCESS_QUERY_INFORMATION, * PROCESS_VM_OPERATION, PROCESS_VM_WRITE, and PROCESS_VM_READ. * * The idea comes from the Dr Dobb's article "A Safer Alternative to * TerminateProcess()" by Andrew Tucker (July 1, 1999), * http://www.drdobbs.com/a-safer-alternative-to-terminateprocess/184416547 * * If this method fails, we fall back to running terminate_process_tree(). */ static int exit_process(HANDLE process, int exit_code) { DWORD code; if (GetExitCodeProcess(process, &code) && code == STILL_ACTIVE) { static int initialized; static LPTHREAD_START_ROUTINE exit_process_address; PVOID arg = (PVOID)(intptr_t)exit_code; DWORD thread_id; HANDLE thread = NULL; if (!initialized) { HINSTANCE kernel32 = GetModuleHandleA("kernel32"); if (!kernel32) die("BUG: cannot find kernel32"); exit_process_address = (LPTHREAD_START_ROUTINE)(void (*)(void)) GetProcAddress(kernel32, "ExitProcess"); initialized = 1; } if (!exit_process_address || !process_architecture_matches_current(process)) return terminate_process_tree(process, exit_code); thread = CreateRemoteThread(process, NULL, 0, exit_process_address, arg, 0, &thread_id); if (thread) { CloseHandle(thread); /* * If the process survives for 10 seconds (a completely * arbitrary value picked from thin air), fall back to * killing the process tree via TerminateProcess(). */ if (WaitForSingleObject(process, 10000) == WAIT_OBJECT_0) { CloseHandle(process); return 0; } } return terminate_process_tree(process, exit_code); } return 0; } #endif