Принудительное завершение процесса

Рано или поздно в программистской практике возникает задача по управлению процессами, в частности их принудительного завершения. Для дочерних процессов, порожденных собственным приложением, обычно хватает вызова 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

Функция универсальная, она позволяет завершать как отдельный процесс, так и дерево процессов. Все "неубиваемые" процессы пропускаются, свой собственный процесс также не завершается, независимо от того, на каком уровне иерархии дерева он находится.

В приложении несколько программ, запускающих друг друга для создания дерева процессов, а также программа, принудительно завершающая выбранный процесс вместе с его дочерними процессами.
Теги:
процесс
Добавлено: 10 Апреля 2018 20:25:15 Добавил: Андрей Ковальчук Нравится 0
Добавить
Комментарии:
Нету комментариев для вывода...