/* * Process Hacker Extra Plugins - * Terminator Plugin * * Copyright (C) 2010-2011 wj32 * Copyright (C) 2016 dmex * * This file is part of Process Hacker. * * Process Hacker is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Process Hacker is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Process Hacker. If not, see . */ #include "main.h" #include "phapi.h" #include "kph2user.h" static PVOID GetExitProcessFunction( VOID ) { if (WindowsVersion >= WINDOWS_VISTA) return PhGetModuleProcAddress(L"ntdll.dll", "RtlExitUserProcess"); return PhGetModuleProcAddress(L"kernel32.dll", "ExitProcess"); } NTSTATUS NTAPI TerminatorTP1( _In_ HANDLE ProcessId ) { NTSTATUS status; HANDLE processHandle; if (NT_SUCCESS(status = Ph2OpenProcess( &processHandle, PROCESS_TERMINATE, ProcessId ))) { // Don't use KPH. status = NtTerminateProcess(processHandle, STATUS_SUCCESS); NtClose(processHandle); } return status; } NTSTATUS NTAPI TerminatorTP2( _In_ HANDLE ProcessId ) { NTSTATUS status; HANDLE processHandle; if (NT_SUCCESS(status = Ph2OpenProcess( &processHandle, PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | PROCESS_VM_WRITE, ProcessId ))) { status = RtlCreateUserThread( processHandle, NULL, FALSE, 0, 0, 0, (PUSER_THREAD_START_ROUTINE)GetExitProcessFunction(), NULL, NULL, NULL ); NtClose(processHandle); } return status; } NTSTATUS NTAPI TerminatorTTGeneric( _In_ HANDLE ProcessId, _In_ BOOLEAN UseKph ) { NTSTATUS status; PVOID processes; PSYSTEM_PROCESS_INFORMATION process; if (UseKph && !Kph2IsConnected()) return STATUS_NOT_SUPPORTED; if (!NT_SUCCESS(status = PhEnumProcesses(&processes))) return status; process = PhFindProcessInformation(processes, ProcessId); if (!process) { PhFree(processes); return STATUS_INVALID_CID; } for (ULONG i = 0; i < process->NumberOfThreads; i++) { HANDLE threadHandle; if (NT_SUCCESS(Ph2OpenThread( &threadHandle, THREAD_TERMINATE, process->Threads[i].ClientId.UniqueThread ))) { if (UseKph) Kph2TerminateThread(threadHandle, STATUS_SUCCESS); else NtTerminateThread(threadHandle, STATUS_SUCCESS); NtClose(threadHandle); } } PhFree(processes); return STATUS_SUCCESS; } NTSTATUS NTAPI TerminatorTT1( _In_ HANDLE ProcessId ) { return TerminatorTTGeneric(ProcessId, FALSE); } NTSTATUS NTAPI TerminatorTT2( _In_ HANDLE ProcessId ) { NTSTATUS status; PVOID processes; PSYSTEM_PROCESS_INFORMATION process; CONTEXT context; PVOID exitProcess; exitProcess = GetExitProcessFunction(); if (!NT_SUCCESS(status = PhEnumProcesses(&processes))) return status; process = PhFindProcessInformation(processes, ProcessId); if (!process) { PhFree(processes); return STATUS_INVALID_CID; } for (ULONG i = 0; i < process->NumberOfThreads; i++) { HANDLE threadHandle; if (NT_SUCCESS(Ph2OpenThread( &threadHandle, THREAD_GET_CONTEXT | THREAD_SET_CONTEXT, process->Threads[i].ClientId.UniqueThread ))) { #ifdef _M_IX86 context.ContextFlags = CONTEXT_CONTROL; Ph2GetThreadContext(threadHandle, &context); context.Eip = (ULONG)exitProcess; Ph2SetThreadContext(threadHandle, &context); #else context.ContextFlags = CONTEXT_CONTROL; Ph2GetThreadContext(threadHandle, &context); context.Rip = (ULONG64)exitProcess; Ph2SetThreadContext(threadHandle, &context); #endif NtClose(threadHandle); } } PhFree(processes); return STATUS_SUCCESS; } NTSTATUS NTAPI TerminatorTP1a( _In_ HANDLE ProcessId ) { NTSTATUS status; HANDLE processHandle = NtCurrentProcess(); if (!NT_SUCCESS(status = NtGetNextProcess( NtCurrentProcess(), ProcessQueryAccess | PROCESS_TERMINATE, 0, 0, &processHandle ))) { return status; } for (ULONG i = 0; i < 1000; i++) // make sure we don't go into an infinite loop or something { HANDLE newProcessHandle; PROCESS_BASIC_INFORMATION basicInfo; if (NT_SUCCESS(PhGetProcessBasicInformation(processHandle, &basicInfo))) { if (basicInfo.UniqueProcessId == ProcessId) { Ph2TerminateProcess(processHandle, STATUS_SUCCESS); break; } } if (NT_SUCCESS(status = NtGetNextProcess( processHandle, ProcessQueryAccess | PROCESS_TERMINATE, 0, 0, &newProcessHandle ))) { NtClose(processHandle); processHandle = newProcessHandle; } else { NtClose(processHandle); break; } } return status; } NTSTATUS NTAPI TerminatorTT1a( _In_ HANDLE ProcessId ) { NTSTATUS status; HANDLE processHandle; HANDLE threadHandle; if (NT_SUCCESS(status = Ph2OpenProcess( &processHandle, PROCESS_QUERY_INFORMATION, // NtGetNextThread actually requires this access for some reason ProcessId ))) { if (!NT_SUCCESS(status = NtGetNextThread( processHandle, NULL, THREAD_TERMINATE, 0, 0, &threadHandle ))) { NtClose(processHandle); return status; } for (ULONG i = 0; i < 1000; i++) { HANDLE newThreadHandle; Ph2TerminateThread(threadHandle, STATUS_SUCCESS); if (NT_SUCCESS(NtGetNextThread( processHandle, threadHandle, THREAD_TERMINATE, 0, 0, &newThreadHandle ))) { NtClose(threadHandle); threadHandle = newThreadHandle; } else { NtClose(threadHandle); break; } } NtClose(processHandle); } return status; } NTSTATUS NTAPI TerminatorCH1( _In_ HANDLE ProcessId ) { NTSTATUS status; HANDLE processHandle; if (NT_SUCCESS(status = Ph2OpenProcess( &processHandle, PROCESS_DUP_HANDLE, ProcessId ))) { for (ULONG i = 0; i < 0x1000; i += 4) { Ph2DuplicateObject( processHandle, UlongToHandle(i), NULL, NULL, 0, 0, DUPLICATE_CLOSE_SOURCE ); } NtClose(processHandle); } return status; } static BOOL CALLBACK DestroyProcessWindowsProc( _In_ HWND hwnd, _In_ LPARAM lParam ) { ULONG processId; GetWindowThreadProcessId(hwnd, &processId); if (processId == (ULONG)lParam) { PostMessage(hwnd, WM_DESTROY, 0, 0); } return TRUE; } NTSTATUS NTAPI TerminatorW1( _In_ HANDLE ProcessId ) { EnumWindows(DestroyProcessWindowsProc, (LPARAM)ProcessId); return STATUS_SUCCESS; } static BOOL CALLBACK QuitProcessWindowsProc( _In_ HWND hwnd, _In_ LPARAM lParam ) { ULONG processId; GetWindowThreadProcessId(hwnd, &processId); if (processId == (ULONG)lParam) { PostMessage(hwnd, WM_QUIT, 0, 0); } return TRUE; } NTSTATUS NTAPI TerminatorW2( _In_ HANDLE ProcessId ) { EnumWindows(QuitProcessWindowsProc, (LPARAM)ProcessId); return STATUS_SUCCESS; } static BOOL CALLBACK CloseProcessWindowsProc( _In_ HWND hwnd, _In_ LPARAM lParam ) { ULONG processId; GetWindowThreadProcessId(hwnd, &processId); if (processId == (ULONG)lParam) { PostMessage(hwnd, WM_CLOSE, 0, 0); } return TRUE; } NTSTATUS NTAPI TerminatorW3( _In_ HANDLE ProcessId ) { EnumWindows(CloseProcessWindowsProc, (LPARAM)ProcessId); return STATUS_SUCCESS; } NTSTATUS NTAPI TerminatorTJ1( _In_ HANDLE ProcessId ) { NTSTATUS status; HANDLE processHandle; // TODO: Check if the process is already in a job. if (NT_SUCCESS(status = Ph2OpenProcess( &processHandle, PROCESS_SET_QUOTA | PROCESS_TERMINATE, ProcessId ))) { HANDLE jobHandle; status = NtCreateJobObject(&jobHandle, JOB_OBJECT_ALL_ACCESS, NULL); if (NT_SUCCESS(status)) { status = NtAssignProcessToJobObject(jobHandle, processHandle); if (NT_SUCCESS(status)) status = NtTerminateJobObject(jobHandle, STATUS_SUCCESS); NtClose(jobHandle); } NtClose(processHandle); } return status; } NTSTATUS NTAPI TerminatorTD1( _In_ HANDLE ProcessId ) { NTSTATUS status; HANDLE processHandle; if (NT_SUCCESS(status = Ph2OpenProcess( &processHandle, PROCESS_SUSPEND_RESUME, ProcessId ))) { HANDLE debugObjectHandle; OBJECT_ATTRIBUTES objectAttributes; InitializeObjectAttributes( &objectAttributes, NULL, 0, NULL, NULL ); if (NT_SUCCESS(NtCreateDebugObject( &debugObjectHandle, DEBUG_PROCESS_ASSIGN, &objectAttributes, DEBUG_KILL_ON_CLOSE ))) { NtDebugActiveProcess(processHandle, debugObjectHandle); NtClose(debugObjectHandle); } NtClose(processHandle); } return status; } NTSTATUS NTAPI TerminatorTP3( _In_ HANDLE ProcessId ) { NTSTATUS status; HANDLE processHandle; if (!Kph2IsConnected()) return STATUS_NOT_SUPPORTED; if (NT_SUCCESS(status = Ph2OpenProcess( &processHandle, SYNCHRONIZE, // KPH doesn't require any access for this operation ProcessId ))) { status = Kph2TerminateProcess(processHandle, STATUS_SUCCESS); NtClose(processHandle); } return status; } NTSTATUS NTAPI TerminatorTT3( _In_ HANDLE ProcessId ) { return TerminatorTTGeneric(ProcessId, TRUE); } NTSTATUS NTAPI TerminatorM1( _In_ HANDLE ProcessId ) { NTSTATUS status; HANDLE processHandle; if (NT_SUCCESS(status = Ph2OpenProcess( &processHandle, PROCESS_QUERY_INFORMATION | PROCESS_VM_WRITE, ProcessId ))) { PVOID pageOfGarbage; SIZE_T pageSize; PVOID baseAddress; MEMORY_BASIC_INFORMATION basicInfo; pageOfGarbage = NULL; pageSize = PAGE_SIZE; if (!NT_SUCCESS(NtAllocateVirtualMemory( NtCurrentProcess(), &pageOfGarbage, 0, &pageSize, MEM_COMMIT, PAGE_READONLY ))) { NtClose(processHandle); return STATUS_NO_MEMORY; } baseAddress = (PVOID)0; while (NT_SUCCESS(NtQueryVirtualMemory( processHandle, baseAddress, MemoryBasicInformation, &basicInfo, sizeof(MEMORY_BASIC_INFORMATION), NULL ))) { ULONG i; // Make sure we don't write to views of mapped files. That // could possibly corrupt files! if (basicInfo.Type == MEM_PRIVATE) { for (i = 0; i < basicInfo.RegionSize; i += PAGE_SIZE) { Ph2WriteVirtualMemory( processHandle, PTR_ADD_OFFSET(baseAddress, i), pageOfGarbage, PAGE_SIZE, NULL ); } } baseAddress = PTR_ADD_OFFSET(baseAddress, basicInfo.RegionSize); } // Size needs to be zero if we're freeing. pageSize = 0; NtFreeVirtualMemory( NtCurrentProcess(), &pageOfGarbage, &pageSize, MEM_RELEASE ); NtClose(processHandle); } return status; } NTSTATUS NTAPI TerminatorM2( _In_ HANDLE ProcessId ) { NTSTATUS status; HANDLE processHandle; if (NT_SUCCESS(status = Ph2OpenProcess( &processHandle, PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION, ProcessId ))) { PVOID baseAddress; MEMORY_BASIC_INFORMATION basicInfo; ULONG oldProtect; baseAddress = (PVOID)0; while (NT_SUCCESS(NtQueryVirtualMemory( processHandle, baseAddress, MemoryBasicInformation, &basicInfo, sizeof(MEMORY_BASIC_INFORMATION), NULL ))) { SIZE_T regionSize; regionSize = basicInfo.RegionSize; NtProtectVirtualMemory( processHandle, &basicInfo.BaseAddress, ®ionSize, PAGE_NOACCESS, &oldProtect ); baseAddress = PTR_ADD_OFFSET(baseAddress, basicInfo.RegionSize); } NtClose(processHandle); } return status; } TEST_ITEM PhTerminatorTests[16] = { { L"TP1", L"Terminates the process using NtTerminateProcess", TerminatorTP1 }, { L"TP2", L"Creates a remote thread in the process which terminates the process", TerminatorTP2 }, { L"TT1", L"Terminates the process' threads", TerminatorTT1 }, { L"TT2", L"Modifies the process' threads with contexts which terminate the process", TerminatorTT2 }, { L"TP1a", L"Terminates the process using NtTerminateProcess (alternative method)", TerminatorTP1a }, { L"TT1a", L"Terminates the process' threads (alternative method)", TerminatorTT1a }, { L"CH1", L"Closes the process' handles", TerminatorCH1 }, { L"W1", L"Sends the WM_DESTROY message to the process' windows", TerminatorW1 }, { L"W2", L"Sends the WM_QUIT message to the process' windows", TerminatorW2 }, { L"W3", L"Sends the WM_CLOSE message to the process' windows", TerminatorW3 }, { L"TJ1", L"Assigns the process to a job object and terminates the job", TerminatorTJ1 }, { L"TD1", L"Debugs the process and closes the debug object", TerminatorTD1 }, { L"TP3", L"Terminates the process in kernel-mode", TerminatorTP3 }, { L"TT3", L"Terminates the process' threads in kernel-mode", TerminatorTT3 }, { L"M1", L"Writes garbage to the process' memory regions", TerminatorM1 }, { L"M2", L"Sets the page protection of the process' memory regions to PAGE_NOACCESS", TerminatorM2 } }; BOOLEAN PhpRunTerminatorTest( _In_ HWND WindowHandle, _In_ PPH_PROCESS_ITEM ProcessItem, _In_ INT Index ) { NTSTATUS status; PTEST_ITEM testItem; HWND lvHandle; PVOID processes; BOOLEAN success = FALSE; LARGE_INTEGER interval; lvHandle = GetDlgItem(WindowHandle, IDC_TERMINATOR_LIST); if (!PhGetListViewItemParam( lvHandle, Index, &testItem )) return FALSE; status = testItem->TestProc(ProcessItem->ProcessId); interval.QuadPart = -1000 * PH_TIMEOUT_MS; NtDelayExecution(FALSE, &interval); if (status == STATUS_NOT_SUPPORTED) { PPH_STRING concat; concat = PhConcatStrings2(L"(Not available) ", testItem->Description); PhSetListViewSubItem(lvHandle, Index, 1, concat->Buffer); PhDereferenceObject(concat); } if (!NT_SUCCESS(PhEnumProcesses(&processes))) return FALSE; // Check if the process exists. if (!PhFindProcessInformation(processes, ProcessItem->ProcessId)) { PhSetListViewItemImageIndex(lvHandle, Index, 1); SetDlgItemText(WindowHandle, IDC_TERMINATOR_TEXT, L"The process was terminated."); success = TRUE; } else { PhSetListViewItemImageIndex(lvHandle, Index, 0); } PhFree(processes); UpdateWindow(WindowHandle); return success; }