Create-Service,реализованная в Advapi32.dll ( \Windows\System32\Advapi32.dll). Эта DLL, название которой расшифровывается как «Advanced API», реализует всю клиентскую часть SCM API.
   Регистрируя сервис через CreateService,программа установки посылает SCM сообщение о том, где будет находиться данный сервис. Затем SCM создает в реестре раздел для сервиса по адресу HKLM\SYSTEM\CurrentControl-Set\Services. Раздел Services является постоянным представлением базы данных SCM. Индивидуальные разделы для каждого сервиса определяют пути к соответствующим исполняемым файлам, а также параметры и конфигурационные настройки сервисов.
   Зарегистрировав сервис, программа установки или управляющее приложение может запустить его через функцию StartService.Поскольку некоторые приложения, основанные на сервисах, нужно инициализировать при загрузке системы, программа установки обычно регистрирует сервис как автоматически запускаемый, просит пользователя перезагрузить систему для завершения установки и при загрузке системы позволяет SCM запустить сервис.
   При вызове CreateServiceпрограмма должна указывать некоторые параметры, описывающие характеристики сервиса. Эти характеристики включают тип сервиса (выполняется ли этот сервис в собственном процессе или совместно с другими сервисами), местонахождение его исполняемого файла, необязательное экранное имя, необязательные имя и пароль для запуска сервиса в контексте защиты определенной учетной записи, тип запуска (запускается ли он автоматически при загрузке системы или вручную под управлением SCP), код, указывающий, как система должна реагировать на ошибку при запуске сервиса, и необязательную информацию о моменте запуска относительно других сервисов (если данный сервис запускается автоматически).
 
   SCM хранит каждую характеристику как параметр в разделе, созданном для данного сервиса. Пример такого раздела реестра показан на рис. 4-8.
    Рис. 4-8. Пример раздела реестра для сервиса
 
   B таблице 4-7 перечислены характеристики сервиса, многие из которых применимы и к драйверам устройств. (Заметьте, что не все из них свойственны каждому типу сервисов или драйверов устройств.) Для хранения собственной конфигурационной информации сервиса в его разделе создается подраздел Parameters, в котором и будут находиться параметры конфигурации этого сервиса. Сервис получает значения параметров через стандартные функции реестра.
 
    ПРИМЕЧАНИЕ SCM не обращается к подразделу Parameters сервиса до тех пор, пока данный сервис не удаляется; лишь в момент его удаления SCM уничтожает весь раздел сервиса вместе с подразделами вроде Parameters.
   Заметьте, что к драйверам устройств применимы три значения параметра Туре. Они используются драйверами устройств, которые также хранят свои параметры в разделе реестра Services. SCM отвечает за запуск драйверов со значением Start, равным SERVICE_AUTO_START или SERVICE_DEMAND_START, поэтому база данных SCM естественным образом включает и драйверы. Сервисы используютдругие типы (SERVICE_WIN32_OWN_PROCESS и SERVICE_ WIN32_SHARE_PROCESS), которые являются взаимоисключающими. Программы, содержащие более одного сервиса, указывают тип SERVICE_WIN32_ SHARE_PROCESS. Преимущество совместного использования процесса несколькими сервисами - экономия ресурсов, которые в ином случае понадобились бы на диспетчеризацию индивидуальных процессов. Ho потенциальный недостаток этой схемы в том, что работа всех сервисов данного процесса прекращается, если один из них вызывает ошибку, из-за которой завершается их процесс. Кроме того, все сервисы в одном процессе должны выполняться под одной и той же учетной записью.
   Когда SCM запускает сервисный процесс, тот немедленно вызывает StartServiceCtrlDispatcher.Эта функция принимает список точек входа в сервисы - по одной на каждый сервис процесса. Каждая точка входа идентифицируется именем соответствующего сервиса. Установив соединение с SCM по именованному каналу, StartServiceCtrlDispatcherвходит в цикл, ожидая, когда от SCM поступит команда. SCM посылает команду запуска сервиса, и функция StartServiceCtrlDispatcher- всякий раз, когда получает такую команду, - создает поток, называемый потоком сервиса(service thread); он вызывает точку входа запускаемого сервиса и реализует цикл ожидания команд для сервиса. StartServiceCtrlDispatcherнаходится в бесконечном ожидании команд SCM и возвращает управление основной функции процесса только после остановки всех сервисов в этом процессе, давая ему время на очистку ресурсов.
 
   Первое, что происходит при передаче управления входной точке сервиса, - вызов RegisterServiceCtrlHandler.Эта функция принимает и хранит указатель на функцию - обработчик управления (control handler), которую реализует сервис для обработки различных команд SCM. Она не взаимодействует с SCM, а лишь хранит только что упомянутую функцию в локальной памяти процесса для функции StartServiceCtrlDispatcher.Входная точка продолжает инициализацию сервиса, выделяя нужную память, создавая конечные точки коммуникационного канала и считывая из реестра данные о собственной конфигурации сервиса. По соглашению, которому следует большинство сервисов, эти параметры хранятся в подразделе Parameters раздела соответствующего сервиса. Входная точка, инициализируя сервис, может периодически посылать SCM сообщения о ходе процесса запуска сервиса (при этом используется функция SetServiceStatus).Когда входная точка заканчивает инициализацию, поток сервиса обычно входит в цикл ожидания запросов от клиентских приложений. Например, Web-сервер должен инициализировать ТСР-сокет и ждать запросы на входящие НТТР-соединения.
   Основной поток сервисного процесса, выполняемый в функции Start-SetviceCtrlDispatcher,принимает команды SCM, направляемые сервисам в этом процессе, и вызывает функцию - обработчик управления (хранимой RegisterServiceCtrlHandler)для соответствующего сервиса. SCM посылает следующие команды: stop (стоп), pause (пауза), resume (возобновление), interrogate (опрос), shutdown (выключение) и команды, определенные приложением. Схема внутренней организации сервисного процесса показана на рис. 4-9. Ha этой иллюстрации изображены два потока процесса, предоставляющего один сервис (основной поток и поток сервиса).
 
Утилита SrvAny
 
   Если у вас есть какая-то программа, которую нужно запускать как сервис, вы должны модифицировать ее стартовый код в соответствии с требованиями к сервисам, кратко описанным в этом разделе. Если исходный код этой программы отсутствует, можно воспользоваться утилитой SrvAny из ресурсов Windows. SrvAny позволяет выполнять любое приложение как сервис. Она считывает путь файла, который должен быть загружен как сервис, из подраздела Parameters в разделе реестра, соответствующего данному сервису. При запуске SrvAny уведомляет SCM о том, что она предоставляет определенный сервис, и, получив от него команду, запускает исполняемый файл сервиса как дочерний процесс. Последний получает копию маркера доступа процесса SrvAny и ссылку на тот же объект WindowStation. Таким образом, сервис выполняется с параметрами защиты и настройками, указанными вами при конфигурировании SrvAny. Сервисы SrvAny не поддерживают значение параметра Туре, соответствующее совместному использованию процесса, поэтому каждое приложение, установленное утилитой SrvAny как сервис, выполняется в отдельном процессе с отдельной копией хост-программы SrvAny.
 
Учетные записи сервисов
 
   Контекст защиты сервиса очень важен для разработчиков и системных администраторов, поскольку он определяет, к каким ресурсам получит доступ этот сервис. Большинство сервисов выполняется в контексте защиты учетной записи локальной системы, если системным администратором или программой установки не указано иное. (B пользовательском интерфейсе название учетной записи локальной системы показывается как Local System или SYSTEM.) B Windows XP введены две разновидности учетной записи локальной системы: сетевой сервис (network service) и локальный сервис (local service). По сравнению с учетной записью локальной системы новые учетные записи обладают меньшими правами, и любой встроенный в Windows сервис, не требующий всей мощи учетной записи локальной системы, выполняется под соответствующей альтернативной учетной записью. Особенности этих учетных записей описываются в следующих разделах.
 
Учетная запись локальной системы
 
   Под этой учетной записью выполняются базовые компоненты пользовательского режима, включая диспетчер сеансов ( \Windows\System32\Smss.exe), процесс подсистемы Windows (Csrss.exe), подсистемулокальной аутентификации ( \Windows\System32\LSASS.exe) и процесс Winlogon (\Windows\System32 \Winlogon.exe).
 
   C точки зрения защиты, учетная запись Local System обладает исключительными возможностями - большими, чем любая другая локальная или доменная учетная запись. Вот ее характеристики.
    (o)Ee обладатель является членом группы локальных администраторов. B таблице 4-8 перечислены группы, которым назначается учетная запись локальной системы. (O том, как информация о членстве в группах используется при проверках прав доступа к объектам, см. в главе 8.)
    (o)Она дает право на задание практически любых привилегий (даже таких, которые обычно не назначаются учетной записи локального администратора, например создания маркеров защиты). Список привилегий, назначаемых учетной записи Local System, приведен в таблице 4-9. (Описание каждой привилегии см. в главе 8.)
    (o)Она дает право на полный доступ к большинству файлов и разделов реестра. Даже если какие-то объекты не разрешают полный доступ, процессы под этой учетной записью могут воспользоваться привилегией захвата объекта во владение (take-ownership privilege) и тем самым получить нужный вид доступа.
    (o)Процессы, работающие под учетной записью Local System, применяют профиль пользователя по умолчанию (HKU\.DEFAULT). Поэтому им недоступна конфигурационная информация, которая хранится в профилях пользователей, сопоставленных с другими учетными записями.
    (o)Если данная система входит в домен Windows, учетная запись Local System включает идентификатор защиты (SID) для компьютера, на котором выполняется сервисный процесс. Поэтому сервис, работающий под учетной записью Local System, будет автоматически аутентифицирован на других машинах в том же лесу. (Л - это группа доменов в Active Directory.)
    (o)Если только учетной записи компьютера специально не назначены права доступа (к общим сетевым ресурсам, именованным каналам и т. д.), процесс может получать доступ к сетевым ресурсам, разрешающим так называемые null-сеансы, т. е. соединения, не требующие соответствующих удостоверений защиты. Вы можете указывать общие ресурсы и каналы, разрешающие null-сеансы на конкретном компьютере, в параметрах NuIlSessionPipes и NullSessionShares раздела реестра HKLM\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters.
 
    1Учетная запись локальной системы в Windows Server 2003 эту привилегию не включает.
 
Учетная запись Network Service
 
   Эта учетная запись предназначена для сервисов, которым нужно аутентифицироваться на других компьютерах в сети по учетной записи компьютера, как это делается в случае учетной записи Local System, но не требуется членство в административных группах или привилегии, назначаемые учетной записи Local System. Поскольку учетная запись Network Service не относится к группе администраторов, выполняемые под ней сервисы по умолчанию получают доступ к гораздо меньшему количеству разделов реестра, а также каталогов и файлов в файловой системе, чем учетная запись Local System. Более того, назначение меньшего числа привилегий ограничивает возможности скомпрометированного процесса сетевого сервиса. Например, процесс, работающий под учетной записью Network Service, не может загрузить драйвер устройства или открыть произвольный процесс.
   Процессы, выполняемые под учетной записью Network Service, используют ее профиль; он загружается в раздел HKU\S-l-5-20, а его файлы и каталоги находятся в \Documents and Settings\NetworkService. B Windows XP и Windows Server 2003 под учетной записью Network Service выполняется DNS-кли-ент, отвечающий за разрешение DNS-имен и поиск контроллеров домена.
 
Учетная запись Local Service
 
   Эта учетная запись практически идентична Network Service с тем отличием, что позволяет обращаться лишь к тем сетевым ресурсам, которые разрешают анонимный доступ. B таблице 4-9 показано, что у нее те же привилегии, что и у учетной записи Network Service, а таблица 4-8 - что она принадлежит к тем же группам (если не считать группы Network Service и Local Service). Профиль, используемый процессами, выполняемыми под учетной записью Local Service, загружается в HKU\S-l-5-19 и хранится в \Documents and Settings\LocalService.
   B Windows XP и Windows Server 2003 под учетной записью Local Service работают такие компоненты, как Remote Registry Service (Служба удаленного реестра), предоставляющая удаленный доступ к реестру локальной системы, служба оповещения, принимающая широковещательные сообщения с административными уведомлениями, и служба LmHosts, обеспечивающая разрешение NetBIOS-имен.
 
Выполнение сервисов под другими учетными записями
 
   B силу вышеописанных ограничений некоторые сервисы должны работать с удостоверениями защиты учетной записи пользователя. Вы можете сконфигурировать сервис на выполнение под другой учетной записью при его создании или с помощью оснастки Services (Службы) консоли MMC, указав в ней пароль и учетную запись, под которой должен работать сервис. B оснастке Services щелкните правой кнопкой мыши нужный сервис, выберите из контекстного меню команду Properties (Свойства), перейдите на вкладку Log On (Вход в систему) и щелкните переключатель This Account (C учетной записью), как показано на рис. 4-10.
 
Интерактивные сервисы
 
   Другое ограничение сервисов, работающих под учетными записями Local System, Local Service или Network Service, заключается в том, что они не могут выводить окна на рабочий стол интерактивного пользователя (без специального флага в функции MessageBox,о котором мы расскажем чуть позже). Такое ограничение не является прямым следствием выполнения под этими учетными записями, а вызвано тем, как подсистема Windows назначает сервисные процессы объектам WindowStation.
   Дело в том, что подсистема Windows сопоставляет каждый Windows-процесс с объектом WindowStation. Он включает объекты «рабочий стол», а те в свою очередь - объекты «окно». Ha консоли видим только объект WindowStation, и только он принимает пользовательский ввод от мыши и клавиатуры. B среде Terminal Services видимым является лишь один объект WindowStation для каждого сеанса, а все сервисы выполняются как часть консольного сеанса. Windows присваивает видимому объекту WindowStation имя WinStaO, и к нему обращаются все интерактивные процессы.
 
   Если не указано иное, подсистема Windows сопоставляет сервисы, выполняемые под учетной записью Local System, с невидимым WindowStation-объектом Service-0x0-3e7$, который разделяется всеми неинтерактивными сервисами. Числовая часть его имени, 3e7, представляет назначаемый LSASS идентификатор сеанса входа в систему, который используется SCM для неинтерактивных сервисов, работающих под учетной записью Local System.
   Сервисы, сконфигурированные на запуск под учетной записью пользователя (т. е. под учетной записью, отличной от Local System), выполняются в другом невидимом объекте WindowStation, имя которого формируется из идентификатора, назначаемого LSASS сеансу входа в систему. Ha рис. 4-11 показано окно программы Winobj при просмотре каталога диспетчера объектов, в который Windows помещает объекты WindowStation. Обратите внимание на интерактивный WindowStation-объект WinSta0, неинтерактивный WindowStation-объект системного сервиса Service-0x0-3e7$ и неинтерактивный WindowStation-объект Service-0x0-6368f$, назначенный сервисному процессу, который зарегистрирован как пользователь.
   Независимо от того, работает ли сервис под учетной записью пользователя или под учетными записями Local System, Local Service либо Network Service, он не может получать пользовательский ввод или выводить окна на консоль, если он не сопоставлен с видимым объектом WindowStation. Фактически, если бы сервис попытался вывести обычное диалоговое окно, он бы казался зависшим, так как ни один пользователь не увидел бы это окно и не смог бы его закрыть с помощью мыши или клавиатуры. (Единственное исключение - вызов MessageBoxсо специальным флагом MB_SERVICE_NOTIFICATION или MB_DEFAULT_DESKTOP_ONLY. При MB_SERVICE_NOTIFICATION окно всегда выводится через интерактивный объект WindowStation, даже если сервис не сконфигурирован на взаимодействие с пользователем, а при MB_DEFAULT_DESKTOP_ONLY окно показывается на рабочем столе по умолчанию интерактивного объекта WindowStation.)
 
   Иногда, хоть и очень редко, сервису нужно взаимодействовать с пользователем через информационные или диалоговые окна. Чтобы предоставить сервису право на взаимодействие с пользователем, в параметр Туре в разделе реестра данного сервиса следует добавить модификатор SERVICE_INTERACTIVE_PROCESS. (Учтите, что сервисы, сконфигурированные для работы под учетной записью пользователя, нельзя помечать как интерактивные.) B случае сервиса, помеченного как интерактивный, SCM запускает его процесс в контексте защиты учетной записи Local System, но сопоставляет сервис с WinStaO, а не с неинтерактивным объектом WindowStation. Это позволяет сервису выводить на консоль окна и реагировать на ввод пользователя.
 
    ПРИМЕЧАНИЕ Microsoft не рекомендует запускать интерактивные сервисы (особенно под учетной записью Local System), так как это вредит безопасности. Windows, представленная интерактивным сервисом, уязвима перед оконными сообщениями, с помощью которых злонамеренный процесс, работающий как непривилегированный пользователь, может вызывать переполнения буферов в сервисном процессе и подменять его собой, чтобы повысить уровень своих привилегий в подсистеме защиты.
 
Диспетчер управления сервисами
 
   Исполняемым файлом SCM является \Windows\System32\Services.exe, и подобно большинству процессов сервисов он выполняется как консольная Windows-программа. Процесс Winlogon запускает SCM на ранних этапах загрузки (см. главу 5) вызовом функции SvcCtrlMain.Она управляет запуском сервисов, сконфигурированных на автоматический старт. SvcCtrlMainвыполняется почти сразу после появления на экране пустого рабочего стола, но, как правило, до загрузки процессом Winlogon графического интерфейса GINA, открывающего диалоговое окно для входа в систему.
   Прежде всего SvcCtrlMainсоздает синхронизирующее событие с именем SvcCtrlEvent_A3752DX и в занятом состоянии. SCM освобождает этот объект только по завершении всех операций, необходимых для подготовки к получению команд от SCR Последний устанавливает диалог с SCM через функцию OperiSCManager,которая не дает SCP связаться с SCM до его инициализации, реализуя это за счет ожидания перехода объекта SvcCtrlEvent_A3752DX в свободное состояние.
   Далее SvcCtrlMainпереходит к делу и вызывает функцию ScCreateService-DB,создающую внутреннюю базу данных сервисов SCM. Функция ScCreateServiceDBсчитывает и сохраняет в разделе HKLM\SYSTEM\CurrentControlSet\Control\ServiceGroupOrder\List параметр типа REG_MULTI_SZ, в котором содержится список имен и порядок определенных групп сервисов. Если сервису или драйверу нужно контролировать свой порядок запуска относительно сервисов других групп, в раздел реестра этого сервиса включается дополнительный параметр Group. Например, сетевой стек Windows построен по принципу «снизу вверх», поэтому сетевые сервисы должны указывать параметры Group, благодаря которым при загрузке системы они будут запускаться после загрузки сетевых драйверов. SCM создает в памяти внутренний список групп, где хранится порядок групп, считанный из реестра. B список входят NDIS, TDI, Primary Disk, Keyboard Port, Keyboard Class и другие группы. Дополнительные приложения и программное обеспечение от сторонних разработчиков могут определять собственные группы и вносить их в список. Так, Microsoft Transaction Server добавляет группу MS Transactions.
 
    ScCreateServiceDBсканирует раздел HKLM\SYSTEM\CurrentControlSet\ Services и создает для каждого его подраздела запись в базе данных сервисов. Такая запись включает все параметры, определенные для сервиса, и поля, предназначенные для слежения за состоянием сервиса. SCM добавляет записи для драйверов устройств и сервисов, так как отвечает за запуск компонентов, помеченных для автоматического запуска. При этом SCM обнаруживает любые ошибки, вызываемые драйверами, которые помечены как запускаемые при загрузке системы (boot-start) и после нее (system-start). (Он также предоставляет приложениям средства для запроса состояния драйверов.) Диспетчер ввода-вывода загружает такие драйверы до начала выполнения какого-либо процесса пользовательского режима, поэтому они загружаются до старта SCM.
    ScCreateServiceDBсчитывает параметр Group сервиса, определяя принадлежность этого сервиса к той или иной группе, и сопоставляет его значение с элементом в созданном ранее списке групп. Эта функция также считывает и сохраняет в базе данных зависимости сервиса от групп и других сервисов, запрашивая из реестра значения его параметров DependOnGroupи Depend-OnService.Ha рис. 4-12 показано, что представляет собой база данных SCM. Заметьте, что сервисы отсортированы в алфавитном порядке. Это вызвано тем, что SCM создает список на основе раздела Services, a Windows сортирует разделы реестра по алфавиту.
   При запуске сервиса SCM может понадобиться обращение к LSASS (например, для регистрации сервиса под учетной записью пользователя), поэтому он ждет, когда LSASS освободит синхронизирующее событие LSA_RPC_SERVER_ACTIVE, которое переходит в свободное состояние после инициализации LSASS. Winlogon тоже запускает процесс LSASS, поэтому инициализация LSASS проходит параллельно инициализации SCM, но завершаться эти операции могут в разное время. После этого SvcCtrlMainвызывает ScGetBootAndSystemDriverState,которая сканирует базу данных сервисов и ищет записи для драйверов устройств, запускаемых при загрузке системы и после нее. ScGetBootAndSystemDriverStateопределяет, успешно ли запустился драйвер, проверяя наличие его имени в каталоге \Driver пространства имен диспетчера объектов. При успешной загрузке драйвера его объект помещается в данный каталог пространства имен диспетчером ввода-вывода, так что имена незагруженных драйверов присутствовать там не могут. Содержимое каталога Driver, полученное с помощью Winobj, показано на рис. 4-13. Если драйвер не загружен, SCM ищет его имя в списке, возвращаемом функцией PnP_DeviceList,которая сообщает о драйверах, включенных в текущий профиль оборудования системы. SvcCtrlMainотмечает имена драйверов из текущего профиля, которые не удалось запустить, в списке с именем ScFailedDrivers.
   Перед запуском автоматически запускаемых сервисов SCM предпринимает еще несколько действий. Он создает именованный канал RPC с именем \Pipe\Ntsvcs, а затем RPC запускает поток, отслеживающий приходящие по этому каналу сообщения SCR Далее SCM освобождает свой объект SvcCtrlEvent_A3752DX, сигнализируя о завершении инициализации.
 
Буквы сетевых дисков
 
   SCM не только предоставляет интерфейс к сервисам, но и играет еще одну роль, никак не связанную с сервисами: он уведомляет GUI-приложения о создании или удалении сопоставления буквы с сетевым диском. SCM ждет, когда маршрутизатор многосетевого доступа (Multiple Provider Router, MPR) освободит объект \BaseNamedObjects\ScNetDrvMsg. Это происходит, когда приложение назначает букву диска удаленному сетевому ресурсу или удаляет ее. (Сведения о MPR см. в главе 13) При освобождении объекта-события маршрутизатором многосетевого доступа SCM вызывает Windows-функцию GetDriveType,чтобы получить список букв подключенных сетевых дисков. Если этот список изменяется в результате освобождения объекта-события, SCM посылает широковещательное Windows-сообщение типа WM_DEVICECHANGE с подтипом DBT_DEVICEREMOVECOMPLETE или DBT_DEVICEARRIVAL. Это сообщение адресовано главным образом Windows Explorer, чтобы он мог обновить любые открытые окна My Computer (Мой компьютер) с учетом изменений в наборе подключенных сетевых дисков.
 
Запуск сервиса
 
    SvcCtrlMainвызывает SCM-функцию ScAutoStartServicesдля запуска всех сервисов, значение параметра Start которых указывает на автостарт. ScAutoStartServicesтакже осуществляет автоматический запуск драйверов. Чтобы не запутаться, помните, что термином «сервисы» обозначаются как сервисы, так и драйверы, если явно не указано иное. Алгоритм ScAutoStartServicesразбивает процесс запуска сервисов на несколько фаз, причем в каждой фазе запускаются определенные группы сервисов. Порядок запуска определяется параметром List в разделе реестра HKLM\SYSTEM\CurrentControlSet\Control\ServiceGroupOrder. Этот параметр, показанный на рис. 4-14, включает имена групп в том порядке, в каком они должны запускаться SCM. Таким образом, единственное, для чего сервис включается в ту или иную группу, - точная настройка момента его запуска относительно других сервисов.
   B начале каждой фазы ScAutoStartServicesотмечает все сервисы, относящиеся к группе, которая запускается на данной фазе. После этого ScAutoStartServicesперебирает отмеченные сервисы, определяя возможность запуска каждого из них. При этом функция проверяет зависимость текущего сервиса от другой группы, на существование которой указывает наличие в соответствующем разделе реестра параметра DependOnGroup. Если сервис зависит от какой-либо группы, она должна быть уже инициализирована и хотя бы один ее сервис должен быть успешно запущен. Если сервис зависит от группы, запускаемой позже группы данного сервиса, SCM генерирует ошибку, которая сообщает о «круговой зависимости». Если