Blog. Just Blog

Как определить состояние SUSPENDED процесса

Версия для печати Добавить в Избранное Отправить на E-Mail | Категория: Образ мышления: Assembler | Автор: ManHunter
Как определить состояние SUSPENDED процесса
Как определить состояние SUSPENDED процесса

Сегодня разберем интересный вопрос. Как программно узнать, заморожен процесс (SUSPENDED) или выполняется? Подобную информацию показывает, например, утилита Process Explorer. Строго говоря, у процесса не может быть состояния SUSPENDED, так что сама постановка вопроса и заголовок статьи не вполне корректные. Процесс может считаться замороженным, когда все без исключения его потоки находятся в состоянии SUSPENDED. Если хоть один поток активен, процесс также считается активным.

Таким образом, чтобы определить, заморожен процесс или нет, надо циклически перебрать все его потоки и проверить, заморожены они или нет. Если с перебором потоков проблем быть не должно, то с определением их состояния немного сложнее. В этих ваших интернетах я встречал чудовищные решения с использованием пары функций SuspendThread и ResumeThread. Способ основывается на том, что при вызове функции SuspendThread возвращается количество предыдущих заморозок потока, ведь поток можно замораживать неоднократно. Если предыдущих заморозок не было, то поток считается активным, иначе поток считается замороженным. После проверки поток снова запускается на выполнение вызовом ResumeThread, счетчик заморозок при этом возвращается в предыдущее значение. Соответственно, если поток ранее был заморожен, то ничего не произойдет, он так и останется замороженным. Никогда, слышите, НИКОГДА так не делайте! В пределах своего процесса подобный изврат еще допустим, но никто не сможет предугадать, как поведет себя какое-нибудь критическое системное или просто чужое многопоточное приложение после такого вмешательства. Не говоря уже о том, что в процессе перебора состояние уже проверенного потока может поменяться.

Правильное решение задачи - использование информации о процессах и потоках, полученной при помощи функции NtQuerySystemInformation. Раньше она относилась к разряду недокументированных, но какое-то время назад официальная документация все-таки появилась, хоть и с оговорками. Эта функция при вызове с параметром SystemProcessInformation делает мгновенный "снимок" состояния всех процессов и их потоков, имеющихся в системе на момент ее вызова. Затем нужно просто найти в массиве процессов интересующий нас процесс и проверить состояние его потоков. В качестве языка реализации, естественно, будет мой любимый Ассемблер.

Про такие системные дебри FASM в базовой комплектации, естественно, не в курсе, поэтому сперва надо самостоятельно описать целую пачку необходимых структур. Более подробно про каждую можете посмотреть на MSDN, оригинальные названия я сохранил, они все неплохо документированы.
  1. struct CLIENT_ID
  2.     UniqueProcess  dd ?
  3.     UniqueThread   dd ?
  4. ends
  5.  
  6. ; Структура информации о потоке
  7. struct SYSTEM_THREAD
  8.     ftKernelTime        FILETIME
  9.     ftUserTime          FILETIME
  10.     ftCreateTime        FILETIME
  11.     dWaitTime           dd ?
  12.     pStartAddress       dd ?
  13.     Cid                 CLIENT_ID
  14.     dPriority           dd ?
  15.     dBasePriority       dd ?
  16.     dContextSwitches    dd ?
  17.     dThreadState        dd ?
  18.     WaitReason          dd ?
  19.     dReserved01         dd ?
  20. ends
  21.  
  22. struct UNICODE_STRING
  23.     Length         dw ?
  24.     MaximumLength  dw ?
  25.     Buffer         dd ?
  26. ends
  27.  
  28. struct VM_COUNTERS
  29.     PeakVirtualSize     dd ?
  30.     VirtualSize         dd ?
  31.     PageFaultCount      dd ?
  32.     PeakWorkingSetSize  dd ?
  33.     WorkingSetSize      dd ?
  34.     QuotaPeakPagedPoolUsage dd ?
  35.     QuotaPagedPoolUsage dd ?
  36.     QuotaPeakNonPagedPoolUsage dd ?
  37.     QuotaNonPagedPoolUsage dd ?
  38.     PagefileUsage       dd ?
  39.     PeakPagefileUsage   dd ?
  40. ends
  41.  
  42. struct IO_COUNTERS
  43.     ReadOperationCount  dq ?
  44.     WriteOperationCount dq ?
  45.     OtherOperationCount dq ?
  46.     ReadTransferCount   dq ?
  47.     WriteTransferCount  dq ?
  48.     OtherTransferCount  dq ?
  49. ends
  50.  
  51. ; Структура информации о процессе
  52. struct SYSTEM_PROCESS
  53.     dNext               dd ?
  54.     dThreadCount        dd ?
  55.     dReserved01         dd ?
  56.     dReserved02         dd ?
  57.     dReserved03         dd ?
  58.     dReserved04         dd ?
  59.     dReserved05         dd ?
  60.     dReserved06         dd ?
  61.     ftCreateTime        FILETIME
  62.     ftUserTime          FILETIME
  63.     ftKernelTime        FILETIME
  64.     usName              UNICODE_STRING
  65.     BasePriority        dd ?
  66.     dUniqueProcessId    dd ?
  67.     dInheritedFromUniqueProcessId dd ?
  68.     dHandleCount        dd ?
  69.     dReserved07         dd ?
  70.     dReserved08         dd ?
  71.     VmCounters          VM_COUNTERS
  72.     dCommitCharge       dd ?
  73.     IoCounters          IO_COUNTERS
  74.     aThreads            SYSTEM_THREAD
  75. ends
Пару констант тоже не забываем.
  1. STATUS_INFO_LENGTH_MISMATCH = 0xC0000004
  2. SystemProcessInformation = 5
Теперь сама функция проверки состояния SUSPENDED процесса. Я постарался сделать ее максимально автономной, чтобы было удобно использовать в своих проектах.
  1. ;-----------------------------------------------------------
  2. ; Функция проверки состояния SUSPENDED процесса
  3. ; Автор: ManHunter / PCL (www.manhunter.ru)
  4. ;-----------------------------------------------------------
  5. ; На входе:
  6. ;    pID - идентификатор процесса
  7. ; На выходе:
  8. ;    EAX = 1 - процесс заморожен
  9. ;    EAX = 0 - как минимум один из потоков процесса активен
  10. ;    EAX = -1 - информацию получить не удалось
  11. ;-----------------------------------------------------------
  12. proc IsSuspended pID:DWORD
  13.         locals
  14.                 hMem    dd ?
  15.                 dSize   dd ?
  16.                 dThread dd ?
  17.                 result  dd ?
  18.         endl
  19.  
  20.         pusha
  21.  
  22.         ; По умолчанию результат неизвестен
  23.         mov     [result],-1
  24.  
  25.         ; Выделить память под процессы и прочитать информацию
  26.         lea     eax,[dSize]
  27.         invoke  NtQuerySystemInformation,SystemProcessInformation,\
  28.                 NULL,0,eax
  29.         cmp     eax,STATUS_INFO_LENGTH_MISMATCH
  30.         jne     .loc_ret
  31.         cmp     [dSize],0
  32.         je      .loc_ret
  33.  
  34.         ; Плюс небольшой запас памяти на всякий случай
  35.         add     [dSize],4096
  36.         invoke  LocalAlloc,LMEM_FIXED,[dSize]
  37.         or      eax,eax
  38.         je      .loc_ret
  39.  
  40.         mov     [hMem],eax
  41.  
  42.         lea     eax,[dSize]
  43.         invoke  NtQuerySystemInformation,SystemProcessInformation,\
  44.                 [hMem],[dSize],eax
  45.  
  46.         ; Перебрать все процессы по очереди
  47.         mov     ebx,[hMem]
  48. .loc_find_process:
  49.         ; Нужный процесс найден
  50.         mov     eax,[pID]
  51.         cmp     [ebx+SYSTEM_PROCESS.dUniqueProcessId],eax
  52.         jne     .loc_next_process_entry
  53.  
  54.         ; Перебрать все потоки
  55.         mov     eax,[ebx+SYSTEM_PROCESS.dThreadCount]
  56.         mov     [dThread],eax
  57.         ; Указатель на массив информации о потоках
  58.         add     ebx,SYSTEM_PROCESS.aThreads
  59.  
  60.         ; Все потоки процесса должны быть заморожены
  61. .loc_chk_threads:
  62.         ; Если хоть один поток не заморожен, то и
  63.         ; весь процесс не считается замороженным
  64.         cmp     [ebx+SYSTEM_THREAD.dThreadState],5
  65.         jne     .process_not_suspended
  66.         cmp     [ebx+SYSTEM_THREAD.WaitReason],5
  67.         jne     .process_not_suspended
  68.  
  69. .thread_suspended:
  70.         ; Поток заморожен, проверить следующий
  71.         add     ebx,sizeof.SYSTEM_THREAD
  72.         dec     [dThread]
  73.         cmp     [dThread],0
  74.         jne     .loc_chk_threads
  75. .process_suspended:
  76.         ; Все потоки заморожены, процесс заморожен
  77.         mov     [result],1
  78.         jmp     .loc_done
  79.  
  80. .process_not_suspended:
  81.         ; Процесс не заморожен
  82.         mov     [result],0
  83.         jmp     .loc_done
  84.  
  85. .loc_next_process_entry:
  86.         ; Указатель на следующий блок
  87.         mov     eax,[ebx+SYSTEM_PROCESS.dNext]
  88.         or      eax,eax
  89.         ; Процесс не найден
  90.         jz      .loc_done
  91.  
  92.         add     ebx,eax
  93.         jmp     .loc_find_process
  94. .loc_done:
  95.         ; Прибраться за собой
  96.         invoke  LocalFree,[hMem]
  97. .loc_ret:
  98.         popa
  99.         mov     eax,[result]
  100.         ret
  101. endp
Единственный параметр вызова - идентификатор процесса. Результат возвращается в регистре EAX: 0 - процесс активен (как минимум один поток процесса не заморожен), 1 - процесс заморожен (все без исключения потоки находятся в состоянии SUSPENDED) и -1 если что-то пошло не так или на момент проверки процесс вообще не найден.

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

Пример программы с исходным текстом (FASM)Пример программы с исходным текстом (FASM)

Is.Process.Suspended.Demo.zip (2,926 bytes)


Поделиться ссылкой ВКонтакте
Просмотров: 2876 | Комментариев: 3

Внимание! Статья опубликована больше года назад, информация могла устареть!

Комментарии

Отзывы посетителей сайта о статье
uncleFedor (30.07.2018 в 17:31):
мне понадобилось чтоб видеть статус (+усыплять+резюмать) богопротивного sppsvc, которого не прибить в 10. причем насовсем его усыплять тоже нельзя - слетает активация и офис без него не пашет. так вот и вкл-выкл его постоянно
ManHunter (02.05.2017 в 14:49):
Я делал это для новой фичи в Quick Task Terminator.
Владислав (02.05.2017 в 14:27):
Спасибо за такую работу, в любом случае в копилку - пригодится. Только вот сейчас, хоть убей, не пойму - где это можно использовать?))))

Добавить комментарий

Заполните форму для добавления комментария
Имя*:
Текст комментария (не более 2000 символов)*:

*Все поля обязательны для заполнения.
Комментарии, содержащие рекламу, ненормативную лексику, оскорбления и т.п., а также флуд и сообщения не по теме, будут удаляться. Нарушителям может быть заблокирован доступ к сайту.
Наверх
Powered by PCL's Speckled Band Engine 0.2 RC3
© ManHunter / PCL, 2008-2024
При использовании материалов ссылка на сайт обязательна
Время генерации: 0.1 сек. / MySQL: 2 (0.0061 сек.) / Память: 4.5 Mb
Наверх