Как определить состояние SUSPENDED процесса
Как определить состояние SUSPENDED процесса
Сегодня разберем интересный вопрос. Как программно узнать, заморожен процесс (SUSPENDED) или выполняется? Подобную информацию показывает, например, утилита Process Explorer. Строго говоря, у процесса не может быть состояния SUSPENDED, так что сама постановка вопроса и заголовок статьи не вполне корректные. Процесс может считаться замороженным, когда все без исключения его потоки находятся в состоянии SUSPENDED. Если хоть один поток активен, процесс также считается активным.
Таким образом, чтобы определить, заморожен процесс или нет, надо циклически перебрать все его потоки и проверить, заморожены они или нет. Если с перебором потоков проблем быть не должно, то с определением их состояния немного сложнее. В этих ваших интернетах я встречал чудовищные решения с использованием пары функций SuspendThread и ResumeThread. Способ основывается на том, что при вызове функции SuspendThread возвращается количество предыдущих заморозок потока, ведь поток можно замораживать неоднократно. Если предыдущих заморозок не было, то поток считается активным, иначе поток считается замороженным. После проверки поток снова запускается на выполнение вызовом ResumeThread, счетчик заморозок при этом возвращается в предыдущее значение. Соответственно, если поток ранее был заморожен, то ничего не произойдет, он так и останется замороженным. Никогда, слышите, НИКОГДА так не делайте! В пределах своего процесса подобный изврат еще допустим, но никто не сможет предугадать, как поведет себя какое-нибудь критическое системное или просто чужое многопоточное приложение после такого вмешательства. Не говоря уже о том, что в процессе перебора состояние уже проверенного потока может поменяться.
Правильное решение задачи - использование информации о процессах и потоках, полученной при помощи функции NtQuerySystemInformation. Раньше она относилась к разряду недокументированных, но какое-то время назад официальная документация все-таки появилась, хоть и с оговорками. Эта функция при вызове с параметром SystemProcessInformation делает мгновенный "снимок" состояния всех процессов и их потоков, имеющихся в системе на момент ее вызова. Затем нужно просто найти в массиве процессов интересующий нас процесс и проверить состояние его потоков. В качестве языка реализации, естественно, будет мой любимый Ассемблер.
Про такие системные дебри FASM в базовой комплектации, естественно, не в курсе, поэтому сперва надо самостоятельно описать целую пачку необходимых структур. Более подробно про каждую можете посмотреть на MSDN, оригинальные названия я сохранил, они все неплохо документированы.
Code (Assembler) : Убрать нумерацию
- struct CLIENT_ID
- UniqueProcess dd ?
- UniqueThread dd ?
- ends
- ; Структура информации о потоке
- struct SYSTEM_THREAD
- ftKernelTime FILETIME
- ftUserTime FILETIME
- ftCreateTime FILETIME
- dWaitTime dd ?
- pStartAddress dd ?
- Cid CLIENT_ID
- dPriority dd ?
- dBasePriority dd ?
- dContextSwitches dd ?
- dThreadState dd ?
- WaitReason dd ?
- dReserved01 dd ?
- ends
- struct UNICODE_STRING
- Length dw ?
- MaximumLength dw ?
- Buffer dd ?
- ends
- struct VM_COUNTERS
- PeakVirtualSize dd ?
- VirtualSize dd ?
- PageFaultCount dd ?
- PeakWorkingSetSize dd ?
- WorkingSetSize dd ?
- QuotaPeakPagedPoolUsage dd ?
- QuotaPagedPoolUsage dd ?
- QuotaPeakNonPagedPoolUsage dd ?
- QuotaNonPagedPoolUsage dd ?
- PagefileUsage dd ?
- PeakPagefileUsage dd ?
- ends
- struct IO_COUNTERS
- ReadOperationCount dq ?
- WriteOperationCount dq ?
- OtherOperationCount dq ?
- ReadTransferCount dq ?
- WriteTransferCount dq ?
- OtherTransferCount dq ?
- ends
- ; Структура информации о процессе
- struct SYSTEM_PROCESS
- dNext dd ?
- dThreadCount dd ?
- dReserved01 dd ?
- dReserved02 dd ?
- dReserved03 dd ?
- dReserved04 dd ?
- dReserved05 dd ?
- dReserved06 dd ?
- ftCreateTime FILETIME
- ftUserTime FILETIME
- ftKernelTime FILETIME
- usName UNICODE_STRING
- BasePriority dd ?
- dUniqueProcessId dd ?
- dInheritedFromUniqueProcessId dd ?
- dHandleCount dd ?
- dReserved07 dd ?
- dReserved08 dd ?
- VmCounters VM_COUNTERS
- dCommitCharge dd ?
- IoCounters IO_COUNTERS
- aThreads SYSTEM_THREAD
- ends
Code (Assembler) : Убрать нумерацию
- STATUS_INFO_LENGTH_MISMATCH = 0xC0000004
- SystemProcessInformation = 5
Code (Assembler) : Убрать нумерацию
- ;-----------------------------------------------------------
- ; Функция проверки состояния SUSPENDED процесса
- ; Автор: ManHunter / PCL (www.manhunter.ru)
- ;-----------------------------------------------------------
- ; На входе:
- ; pID - идентификатор процесса
- ; На выходе:
- ; EAX = 1 - процесс заморожен
- ; EAX = 0 - как минимум один из потоков процесса активен
- ; EAX = -1 - информацию получить не удалось
- ;-----------------------------------------------------------
- proc IsSuspended pID:DWORD
- locals
- hMem dd ?
- dSize dd ?
- dThread dd ?
- result dd ?
- endl
- pusha
- ; По умолчанию результат неизвестен
- mov [result],-1
- ; Выделить память под процессы и прочитать информацию
- lea eax,[dSize]
- invoke NtQuerySystemInformation,SystemProcessInformation,\
- NULL,0,eax
- cmp eax,STATUS_INFO_LENGTH_MISMATCH
- jne .loc_ret
- cmp [dSize],0
- je .loc_ret
- ; Плюс небольшой запас памяти на всякий случай
- add [dSize],4096
- invoke LocalAlloc,LMEM_FIXED,[dSize]
- or eax,eax
- je .loc_ret
- mov [hMem],eax
- lea eax,[dSize]
- invoke NtQuerySystemInformation,SystemProcessInformation,\
- [hMem],[dSize],eax
- ; Перебрать все процессы по очереди
- mov ebx,[hMem]
- .loc_find_process:
- ; Нужный процесс найден
- mov eax,[pID]
- cmp [ebx+SYSTEM_PROCESS.dUniqueProcessId],eax
- jne .loc_next_process_entry
- ; Перебрать все потоки
- mov eax,[ebx+SYSTEM_PROCESS.dThreadCount]
- mov [dThread],eax
- ; Указатель на массив информации о потоках
- add ebx,SYSTEM_PROCESS.aThreads
- ; Все потоки процесса должны быть заморожены
- .loc_chk_threads:
- ; Если хоть один поток не заморожен, то и
- ; весь процесс не считается замороженным
- cmp [ebx+SYSTEM_THREAD.dThreadState],5
- jne .process_not_suspended
- cmp [ebx+SYSTEM_THREAD.WaitReason],5
- jne .process_not_suspended
- .thread_suspended:
- ; Поток заморожен, проверить следующий
- add ebx,sizeof.SYSTEM_THREAD
- dec [dThread]
- cmp [dThread],0
- jne .loc_chk_threads
- .process_suspended:
- ; Все потоки заморожены, процесс заморожен
- mov [result],1
- jmp .loc_done
- .process_not_suspended:
- ; Процесс не заморожен
- mov [result],0
- jmp .loc_done
- .loc_next_process_entry:
- ; Указатель на следующий блок
- mov eax,[ebx+SYSTEM_PROCESS.dNext]
- or eax,eax
- ; Процесс не найден
- jz .loc_done
- add ebx,eax
- jmp .loc_find_process
- .loc_done:
- ; Прибраться за собой
- invoke LocalFree,[hMem]
- .loc_ret:
- popa
- mov eax,[result]
- ret
- endp
В приложении пример программы с исходным текстом, использующей эту функцию. Идентификатор процесса захардкожен прямо в исходнике, никаких дополнительных удобств для выбора процесса не предусмотрено.
Просмотров: 2989 | Комментариев: 3
Внимание! Статья опубликована больше года назад, информация могла устареть!
Комментарии
Отзывы посетителей сайта о статье
uncleFedor
(30.07.2018 в 17:31):
мне понадобилось чтоб видеть статус (+усыплять+резюмать) богопротивного sppsvc, которого не прибить в 10. причем насовсем его усыплять тоже нельзя - слетает активация и офис без него не пашет. так вот и вкл-выкл его постоянно
ManHunter
(02.05.2017 в 14:49):
Я делал это для новой фичи в Quick Task Terminator.
Владислав
(02.05.2017 в 14:27):
Спасибо за такую работу, в любом случае в копилку - пригодится. Только вот сейчас, хоть убей, не пойму - где это можно использовать?))))
Добавить комментарий
Заполните форму для добавления комментария