Вернуться на сайт

Пример 8 – Процессы и службы

В данном примере мы рассмотрим что такое процессы, чем они отличаются от программ, и отдельный класс процессов, именуемый системными службами. Для этого нам понадобится только среда программирования (в моем случае это Borland C++ Builder).
Процессом как правило называют экземпляр выполняемой программы. Хотя на первый взгляд кажется, что программа и процесс понятия практически одинаковые, они на самом деле друг от друга отличаются. Программа представляет собой статический набор команд, процесс же - это набор ресурсов и данных, использующихся при выполнении программы.

Процесс в ОС Windows состоит из следующих компонентов:

Создание Win32 процесса осуществляется вызовом одной из таких функций, как CreateProcess, CreateProcessAsUser (для Windows NT/2000) и CreateProcessWithLogonW (начиная с Windows 2000) и происходит следующим образом:

  1. Открывается файл образа (EXE), который будет выполняться в процессе. Если исполняемый файл не является Win32 приложением, то ищется образ поддержки (support image) для запуска этой программы. Например, если исполняется файл с расширением .bat, запускается cmd.exe и т.п.
  2. Создается объект Win32 "процесс".
  3. Создается первичный поток (стек, контекст и объект "поток").
  4. Подсистема Win32 уведомляется о создании нового процесса и потока.
  5. Начинается выполнение первичного потока.
  6. В контексте нового процесса и потока инициализируется адресное пространство (например, загружаются требуемые DLL) и начинается выполнение программы.

Процесс завершается если:

Когда процесс завершается, все User- и GDI-объекты, созданные процессом, уничтожаются, объекты ядра закрываются (если их не использует другой процесс), адресное пространство процесса уничтожается. Рассмотрим, как это реализуется на практике.

Напишем маленькую функцию, которая завершает процесс с использованием функции TerminateProcess(). В качестве параметра ей передается хэндл завершаемого процесса. В свою очередь хэндл процесса можно получить с помощью функции OpenProcess(). Одним из параметров для этой функции передается PID process identificator (идентификатор процесса). Идентификатор процесса можно лицезреть в стандартном Диспетчере Задач.

Для получения идентификатора в нашей программе напишем небольшую функцию, которая будет определять PID по имени файла. Получим снэпшот (snapshot) системных процессов с помощью функции CreateToolhelp32Snapshot(), а затем функциями Process32First() и Process32Next() переберем все процессы для поиска нужного нам (который задается переменной ExeName). Здесь существенную роль играет структура PROCESSENTRY32, которая как раз содержит информацию как об имени файла (элемент szExeFile), так и об идентификаторе процесса (элемент th32ProcessID). В результате этих нехитрых манипуляций в переменную Pid возвращается текущий идентификатор процесса. Соответственно, не забываем после нахождения идентификатора освобождать хэндл с помощью функции CloseHandle(). Более подробное описание использованных функций можно найти в хелпе по WinAPI, кстати, для их корректной работы необходимо #include <tlhelp32.hpp>

Для использования вынесем на форму кнопку и в ее обработчик вставим вызовы описанных ранее функций. Здесь для примера я открывал браузер Опера и потом программно завершал его процесс. После этого функцией Application->Terminate() завершалось выполнение самой программы. Необходимо отметить, что таким способом можно завершить не каждый процесс, и тут мы вплотную подходим к теме служб…

Исполняемый файл службы использует во время работы специальный протокол общения с особой подсистемой Windows NT - Service Control Manager (в дальнейшем SCM). API-функции для работы с SCM экспортирует стандартная библиотека Windows advapi32.dll. Подсистема служб загружается в самом начале загрузки операционной системы из исполняемого файла services.exe, одноимённый процесс вы можете наблюдать в списке активных процессов во всё время работы компьютера. Библиотека advapi32.dll общается с services.exe посредством RPC (Remote Procedure Call – удаленный вызов процедур), поэтому всё, что вы можете делать со службами на локальной машине, вы можете делать и на удалённой машине по сети, что очень удобно.

Ещё одно отличие службы от "обычной программы" в том, что служба, будучи запущенной, работает и после выхода пользователя из системы (даже когда вы видите приглашение "Для входа в систему нажмите Ctrl+Alt+Delete"). Поэтому службами часто делают программы, которые должны работать даже тогда, когда нет вошедших в систему пользователей. Из встроенных служб это, к примеру, Spooler (служба печати), Alerter (служба оповещения, часто используемая системными администраторами) и т.п.
Кстати, SCM запускает также и драйверы, поэтому захват власти над ним является лакомой добычей хакеров и вирусов, поскольку именно оттуда они могут запустить драйвер ядра, который может взять полный контроль над системой))
В данном примере мы будем останавливать службу драйверов Nvidia (которую можно остановить вручную). Для этого рассмотрим некоторые важные функции API для работы со службами.
Всё начинается с функции OpenSCManager(), которая возвращает дескриптор на SCМ, что впоследствии позволяет устанавливать, удалять или контролировать службы в системе (локальной или удалённой).
Следующая функция - OpenService() позволяет получить дескриптор для уже установленной службы. Ей передаются три параметра: дескриптор на SCM, имя службы, желаемый уровень доступа. Этот уровень доступа к службам может быть суммой (логическая операция OR) некоторых постоянных выражений, к примеру SERVICE_START, SERVICE_STOP, SERVICE_ALL_ACCESS (для полного доступа)

Для нашего примера понадобится уровень доступа SERVICE_STOP, который позволяет остановить службу с помощи функции ControlService();
Функция ControlService() позволяет посылать коды управления запущенной службе. Этой функции передаются следующие параметры:
hService - ранее открытый дескриптор на службу;dwControl - определяет код управления, который будет послан службе. Это может быть SERVICE_CONTROL_STOP, SERVICE_CONTROL_PAUSE, SERVICE_CONTROL_CONTINUE (понятно из названий) pServiceStatus - указатель на структуру SERVICE_STATUS, в которую отобразится состояние службы после полученного ею кода управления. Естественно, в нашем случае будет использоваться код управления SERVICE_CONTROL_STOP для остановки службы. Далее с помощью функции QueryServiceStatus проверяется в цикле флаг остановки службы. Как только флаг получен не забывает с помощью функции CloseServiceHandle() освободить открытый дескриптор на объект службы или на SCM. Желательно не забывать вызывать эту функцию, когда дескриптор становится не нужным. Проверить остановку службы можно в той же панели "Службы" из Администрирования.

Итак, мы рассмотрели функции, нужные для работы со службами и их конфигурирования, на примере остановки службы (кстати, для работы функций необходимо #include <winsvc.h>). В качестве дополнительной информации можно сказать, что SCM запускает службы исходя из того, какие параметры имеются в базе данных служб. Эта базу данных находится в реестре по ключу HKLM\SYSTEM\CurrentControlSet\Services, где каждый раздел - имя установленной службы. Исходя из этого можно сделать вывод, что существует возможность добавлять службы через реестр. Правда придется перезагружать машину после такой "установки" службы, поскольку база грузится в SCM при старте системы. Кроме того, этот способ подходит только для текущих версий ОС, так как Microsoft не гарантирует, что ключи реестра в следующих версиях Windows будут на том же месте. Более того, никто не гарантирует, что в следующих версиях эта база вообще будет находиться в реестре. У вирусописателей особое мнение на этот счёт - анализ подозрительной программы, в которой импортируется вызов CreateService(), вызывает подозрения, а так будет просто "безобидная" работа с реестром (но у этих "товарищей" вообще на многие вещи особое мнение)

При написании примера использовалась информация с сайта http://bugtraq.ru/

(с) FM, 2008

Вернуться на сайт

Отзывы как обычно принимаются на mimicria@mail.ru