Рано или поздно в программистской практике возникает задача по управлению процессами, в частности их принудительного завершения. Для дочерних процессов, порожденных собственным приложением, обычно хватает вызова
TerminateProcess, но в некоторых случаях требуется принудительно завершить чужой процесс. Зная идентификатор процесса сделать это не составит большого труда. Открываем процесс функцией
OpenProcess с правами PROCESS_TERMINATE, а затем, в случае успеха, отдаем полученный хэндл функцииTerminateProcess. Вроде бы все просто, но не тут-то было. При попытке открыть некоторые процессы, как правило системные, возвращается ошибка ERROR_ACCESS_DENIED. Это связано с тем, что дескрипторы безопасности системных процессов блокируют такой доступ для всех пользователей, включая Администраторов. Чтобы обойти запрет, требуется поднять привилегии нашего процесса, а именно получить привилегию отладчика SE_DEBUG_NAME.
; Константы для работы привилегиями
TOKEN_ADJUST_PRIVILEGES = 20h
TOKEN_QUERY = 8h
SE_PRIVILEGE_ENABLED = 2h
; Структуры для работы привилегиями
struct LUID
lowPart dd ?
HighPart dd ?
ends
struct LUID_AND_ATTRIBUTES
pLuid LUID
Attributes dd ?
ends
struct _TOKEN_PRIVILEGES
PrivilegeCount dd ?
Privileges LUID_AND_ATTRIBUTES
ends
Когда данные подготовлены, можно поднять привилегии нашего процесса. Этот код вам уже должен быть знаком и трудностей не вызовет.
invoke GetCurrentProcess
invoke OpenProcessToken,eax,TOKEN_ADJUST_PRIVILEGES+TOKEN_QUERY,TTokenHd
; Получить привилегии процесса
invoke LookupPrivilegeValue,NULL,SE_DEBUG_NAME,udtLUID
; Повысить привилегии процесса
mov [tkp.PrivilegeCount],1
mov [tkp.Privileges.Attributes],SE_PRIVILEGE_ENABLED
mov eax,[udtLUID.lowPart]
mov [tkp.Privileges.pLuid.lowPart],eax
mov eax,[udtLUID.HighPart]
mov [tkp.Privileges.pLuid.HighPart],eax
invoke AdjustTokenPrivileges,[TTokenHd],0,tkp,0,0,0
Все, теперь у нас есть доступ ко всем процессам, включая сервисы и системные приложения. Конечно, есть особые программы типа антивирусов и фаерволов, которые на уровне ядра системы защищаются от манипуляций с их процессами, с ними такой трюк не сработает.
Усложним задачу. Предположим, что нам надо завершать не только произвольный процесс, но также и все процессы, порожденные им и его дочерними процессами. То есть нам надо обработать дерево процессов. Воспользуемся функцией
CreateToolhelp32Snapshot для получения списка процессов, а затем переберем их по очереди и завершим все процессы, идентификатор родительского процесса которых равняется идентификатору убиваемого процесса. Но это только первый уровень, значит придется вызывать функцию завершения рекурсивно, указывая в качестве параметра идентификаторы дочерних процессов. Вот что у меня получилось:
;------------------------------------------------------
; Рекурсивная функция завершения дерева процессов
; by ManHunter / PCL
; http://www.manhunter.ru
;------------------------------------------------------
; Параметры:
; pID - идентификатор процесса
; recursive - завершать дочерние процессы (TRUE/FALSE)
;------------------------------------------------------
proc KillProcess pID:DWORD, recursive:DWORD
; Сохранить регистры
pusha
; Требуется рекурсивный поиск?
cmp [recursive],TRUE
jne .kill_process
; Снимок процессов системы
invoke CreateToolhelp32Snapshot,TH32CS_SNAPPROCESS,0
mov ebx,eax
; Перебрать в цикле все процессы
mov [ProcEntry.dwSize],sizeof.PROCESSENTRY32
invoke Process32First,ebx,ProcEntry
.check_process:
; Ничего не найдено?
cmp eax,FALSE
je .stop_scan
; Это дочерний процесс от убиваемого?
mov eax,[ProcEntry.th32ParentProcessID]
cmp eax,[pID]
jne .next_process
; Рекурсивно убить дочерний процесс
stdcall KillProcess,[ProcEntry.th32ProcessID],[recursive]
.next_process:
; Следующий процесс
invoke Process32Next,ebx,ProcEntry
jmp .check_process
.stop_scan:
; Закрыть хэндл
invoke CloseHandle,ebx
.kill_process:
; Самоубийством не занимаемся
invoke GetCurrentProcessId
cmp eax,[pID]
je @f
; Убить заказанный процесс
invoke OpenProcess,PROCESS_TERMINATE,FALSE,[pID]
or eax,eax
jz @f
invoke TerminateProcess,eax,0
@@:
; Востановить регистры
popa
ret
endp
Функция универсальная, она позволяет завершать как отдельный процесс, так и дерево процессов. Все "неубиваемые" процессы пропускаются, свой собственный процесс также не завершается, независимо от того, на каком уровне иерархии дерева он находится.
В приложении несколько программ, запускающих друг друга для создания дерева процессов, а также программа, принудительно завершающая выбранный процесс вместе с его дочерними процессами.