Управление службами Windows через командную строку

Граница между администратором, который настраивает систему, и атакующим, который в ней закрепляется, проходит не по инструменту. Она проходит по контексту запуска. sc.exe присутствует на каждой Windows-машине, подписан Microsoft и за тридцать лет так вшит в нормальную работу системы, что его нельзя заблокировать без поломки десятков легитимных сценариев. Именно поэтому его используют с обеих сторон. https://t.me/seberd_ru

Что такое sc.exe и как он устроен

sc.exe расшифровывается как Service Control. Файл живёт в C:\Windows\System32 и входит в базовую поставку Windows начиная с NT 4.0. Утилита не управляет службами напрямую. Весь реальный контроль сосредоточен в компоненте Service Control Manager, SCM. sc.exe выступает интерфейсом между командной строкой и SCM.

Связь происходит через Remote Procedure Call, RPC. Именно поэтому утилита умеет работать не только с локальной машиной, но и с удалёнными хостами без установки дополнительного ПО. Достаточно прав администратора на целевой машине и открытого порта 135.

Один момент, который часто упускают при автоматизации: sc.exe не парсит вывод самостоятельно. Он передаёт данные напрямую от SCM, и форматирование зависит от локали системы. Для разбора вывода в скриптах лучше использовать PowerShell или WMI-запросы, а не пытаться парсить текст sc.exe через findstr.

Синтаксис sc.exe с пробелами

Базовый синтаксис выглядит так: sc [имя_хоста] <команда> <имя_службы> [параметры]

Самая частая ошибка при работе со sc.exe связана с пробелом после знака равенства в параметрах. После = обязательно нужен пробел:

sc config Spooler start= auto     ← правильно
sc config Spooler start=auto      ← ошибка синтаксиса

Особенность парсинга аргументов в Win32 API не менялась с NT 4.0. Если скрипт падает с непонятной ошибкой и всё выглядит верным, в большинстве случаев причина здесь.

Вторая ловушка: кавычки в binPath. Если путь к исполняемому файлу содержит пробелы, весь путь берётся в кавычки, но сам параметр идёт без них:

binPath= "C:\Program Files\MyApp\agent.exe"

Двойные кавычки внутри binPath экранируются обратным слешем:

binPath= "cmd.exe /c \"C:\scripts\start.bat\""

Запомнить просто: пробел после знака равенства, кавычки вокруг значения, но не вокруг самого параметра.

Как проверить статус службы через sc query

sc query <имя_службы> показывает текущее состояние. Для диспетчера очереди печати:

sc query Spooler

Вывод содержит состояние службы (RUNNING, STOPPED, PAUSED), PID процесса при активной работе, тип запуска и контрольные точки с кодами ожидания. Полный список всех служб получается командой sc query state= all.

Фильтрация через findstr ускоряет работу с большими списками:

sc query state= all | findstr "SERVICE_NAME STATE"

Для полной конфигурации используется sc qc, а не sc query. Команда qc расшифровывается как query config и показывает binPath, тип запуска, зависимости и учётную запись.

sc qc Spooler

Краткое правило: query покажет статус прямо сейчас, qc покажет конфигурацию из реестра. Их часто путают, и при расследовании инцидента нужны обе.

Запуск и остановка службы

Запуск: sc start Spooler

Остановка: sc stop Spooler

Обе команды возвращают код состояния сразу. Стандартные коды ошибок при запуске: 1053 означает таймаут, служба не ответила SCM в течение 30 секунд; 1060 говорит о том, что служба с таким именем не существует.

Почему sc.exe не ждёт результата

sc.exe отправляет запрос в SCM и немедленно возвращает управление. Если служба запускается долго или инициализируется поэтапно, скрипт продолжит выполнение, пока служба ещё находится в состоянии START_PENDING.

SCM работает асинхронно по дизайну. В автоматизации это означает одно: после sc start нужна отдельная проверка через sc query с ожиданием нужного состояния. Без неё скрипт может считать задачу выполненной, хотя служба так и не стартовала.

sc start MyService
:wait
sc query MyService | findstr "RUNNING" >nul
if errorlevel 1 (timeout /t 2 >nul & goto wait)
echo Служба запущена

В PowerShell та же логика реализуется через Start-Service -Name MyService с последующей проверкой (Get-Service MyService).Status.

Изменение параметров через sc config

sc config меняет конфигурацию существующей службы. Самый частый сценарий касается изменения типа запуска:

sc config Spooler start= auto

Параметр start принимает четыре значения. auto запускает службу при загрузке. demand оставляет ручной запуск. disabled полностью отключает. delayed-auto реализует автозапуск с задержкой после загрузки системы.

Про delayed-auto стоит сказать отдельно. Параметр появился в Windows Vista как оптимизация загрузки: службы с низким приоритетом не замедляют старт системы. Служба запускается примерно через две минуты после завершения основных процессов. Ряд инструментов аудита и мониторинга активны при старте системы, но не отслеживают новые процессы настолько же активно в более позднем окне загрузки. Эта особенность известна тем, кто проектирует persistence-механизмы.

Несколько параметров меняются одной командой:

sc config MyService binPath= "C:\tools\agent.exe" start= auto DisplayName= "My Agent Service"

Перед изменением конфигурации рабочей службы полезно сохранить текущее состояние:

sc qc MyService > C:\backup\MyService_config.txt

Создание службы через sc create

sc create MyAgent binPath= "C:\tools\agent.exe" start= auto DisplayName= "My Monitoring Agent"

Основные параметры при создании: binPath принимает путь к исполняемому файлу или командную строку; start задаёт тип запуска; DisplayName определяет отображаемое имя в оснастке services.msc; obj указывает учётную запись запуска; depend перечисляет зависимости от других служб.

Данные о службе записываются в реестр по пути HKLM\SYSTEM\CurrentControlSet\Services\<имя_службы> немедленно, до первого запуска службы. При форензике это важно: запись в реестре появляется раньше, чем служба когда-либо стартовала. Если запись есть, а Event 7045 в логах не зафиксирован, стоит проверить, каким путём запись появилась.

Учётные записи служб и почему LocalSystem не безопасный выбор по умолчанию

Если параметр obj не указан при создании, служба получает учётную запись LocalSystem. Максимально привилегированная учётка в системе: полный доступ к локальным ресурсам, возможность взаимодействовать с сетью от имени компьютерного аккаунта в домене. Для большинства служб такой уровень прав избыточен.

Правильная практика предполагает создание выделенной учётной записи с минимально необходимым набором прав. В доменных средах для этого существует gMSA, Group Managed Service Account. Специальный тип аккаунта, пароль которого система обновляет автоматически каждые 30 дней. Хранить пароль в скрипте или конфиг-файле при gMSA не нужно совсем.

sc create MyService binPath= "C:\tools\agent.exe" obj= "DOMAIN\svc_myservice$" start= auto

Знак $ в конце имени аккаунта обозначает gMSA. Для его создания требуются права на уровне Active Directory и контроллер домена с поддержкой этого механизма.

Если gMSA недоступен, используется обычная доменная учётка с выделенным паролем. Хранить пароль в скриптах допустимо только как временное решение. Для продакшена пароли служб управляются через специализированные хранилища секретов или через LAPS.

Про Alternate Data Streams в binPath. Служба технически создаётся с таким binPath:

binPath= "\"c:\folder\file.txt:cmd.exe\" /c echo works"

Синтаксис легитимный с точки зрения SCM. Часть антивирусных решений пропускает такие записи при сканировании. Понимание этого вектора важно и при создании правил детектирования, и при анализе подозрительных служб.

Как удалить службу через sc delete

sc delete MyService

Команда удаляет запись из реестра. Файл бинарника при этом остаётся на диске. При форензике это создаёт характерный артефакт: исполняемый файл есть, записи в Services нет. Либо службу удалили, забыв убрать файл, либо файл появился на хосте в обход создания службы через SCM.

sc delete не останавливает активную службу. При попытке удалить работающую службу команда вернёт ошибку. Правильная последовательность: sc stop, ожидание состояния STOPPED через sc query, затем sc delete.

В продакшене разумнее действовать по-другому: сначала перевести службу в disabled, выждать один цикл перезагрузки, убедиться что ничего не сломалось, и только после этого удалять запись. При быстром удалении без тестирования риск упустить зависимость значительно выше.

Удалённое управление службами через sc.exe

sc.exe поддерживает работу с удалёнными хостами через явное указание имени или адреса:

sc \\SERVER01 query Spooler
sc \\192.168.1.10 start MyService

Требования для работы: права локального администратора на целевой машине, открытые порты TCP 135 и динамический диапазон 49152–65535, доступ к административным шарам ADMIN$ и IPC$, разрешение входящих RPC-соединений в брандмауэре.

Архитектурный момент, который важен для понимания модели угроз: sc.exe работает через RPC, а не через SMB напрямую. Psexec, для сравнения, копирует бинарник на целевую машину через SMB, и файловый след на диске там остаётся. sc.exe через RPC файлового следа на целевом хосте не создаёт. Только сетевой трафик и события в логах.

Блокировка файла sc.exe на рабочей станции не защищает от удалённого создания служб. Атакующий с правами администратора может вызвать функцию CreateService напрямую из любого процесса, не используя sc.exe совсем.

Чем sc.exe отличается от PowerShell Get-Service

Главное преимущество sc.exe состоит в отсутствии зависимостей. Работает в чистой cmd, в Recovery Environment, в минимальных образах Windows Server Core, в среде без установленного .NET. Там, где PowerShell недоступен, sc.exe остаётся единственным нативным вариантом.

PowerShell (Get-Service, Set-Service, New-Service) удобнее для автоматизации в обычных условиях. Возвращает объекты вместо текстового вывода, поддерживает пайплайны и фильтрацию, легче встраивается в скрипты с условиями и логированием. Для сложной автоматизации на современном парке машин PowerShell будет очевидным выбором.

На практике разумно совмещать оба подхода: основные скрипты развёртывания пишутся на PowerShell, рядом держится набор команд sc.exe для аварийных сценариев. В расследованиях инцидентов анализируются артефакты от обоих инструментов, потому что атакующий выберет тот, который доступен в конкретной среде.

schtasks решает другую задачу. Утилита работает с планировщиком задач, а не со службами. Механизмы разные. Службы управляются через SCM и запускаются как процессы от определённой учётки. Задачи управляются через Task Scheduler Service и запускаются по расписанию или событиям. При этом оба механизма используются для persistence, и оба оставляют следы в разных местах системы.

Как искать подозрительные службы на хосте

Быстрая ручная проверка начинается с получения списка всех служб:

sc query state= all | findstr "SERVICE_NAME"

Для каждой подозрительной записи запускается проверка конфигурации:

sc qc <имя_службы>

На что обращать внимание в binPath: командный интерпретатор вместо прямого пути к exe (cmd.exe, powershell.exe, wscript.exe, mshta.exe), закодированные аргументы (-EncodedCommand, -enc, IEX, DownloadString), путь ведёт в пользовательскую директорию при учётке LocalSystem (%TEMP%, %APPDATA%, %USERPROFILE%), синтаксис Alternate Data Streams с двоеточием внутри имени файла.

На что обращать внимание в именах: имя почти совпадает с легитимным, но с опечаткой (Svchost32, WindosUpdate, Microsft), короткое случайное имя без семантики (abc123, tmp7f2a, xsvc), DisplayName не соответствует ServiceName или отсутствует.

Легитимные службы обычно имеют описание в поле Description. У вредоносных поле часто пустое. Не потому что существует такое правило, а потому что установщики малвари обычно его не заполняют.

Автоматизация строится на сравнении текущего списка с базовым образом. Разница становится кандидатом на анализ. Базовый образ нужно обновлять при каждой плановой установке ПО, иначе легитимные изменения начнут генерировать ложные срабатывания.

Чеклист для проверки хоста после подозрительного события:

[√] Получить список всех служб командой sc query state= all [√] Для каждой подозрительной службы выполнить sc qc <имя> [√] Проверить Event ID 7045 в System log за последние 24-48 часов [ ] Сравнить список служб с базовым образом системы [ ] Проверить цифровую подпись каждого бинарника через Get-AuthenticodeSignature [ ] Сверить хеши подозрительных файлов с корпоративным TI-фидом или публичными базами [x] Блокировать sc.exe через AppLocker: ломает легитимные сценарии и не защищает от API-вызовов

Что фиксировать в логах для детектирования аномалий

Event 7045 и как его обходят через реестр

Event ID 7045 в System log фиксирует создание новой службы. Содержит имя службы, binPath, тип запуска и учётную запись. Базовый индикатор, который должен быть покрыт правилами в SIEM.

Правильный фильтр для алерта по 7045 включает признаки в binPath: командные интерпретаторы (cmd.exe, powershell.exe, wscript.exe), закодированные команды (-enc, IEX, DownloadString, %COMSPEC%), пути в нестандартных директориях. Алерт на каждый 7045 без фильтра создаёт слишком много шума: легитимный софт тоже создаёт службы.

Event ID 4697 в Security log аналогичен 7045, но находится в журнале безопасности. Генерируется при включённом расширенном аудите событий (Advanced Audit Policy, категория System). Многие организации не включают этот аудит, полагаясь только на 7045.

Sysmon Event ID 1 (создание процесса) позволяет коррелировать цепочки: services.exe запустил cmd.exe, который запустил powershell.exe. Родительский процесс sc.exe с аномальными аргументами виден именно здесь.

Теперь о том, что Event 7045 не покрывает.

Событие генерируется только при создании службы через SCM. Если атакующий записывает данные напрямую в реестр через reg.exe, PowerShell Set-ItemProperty или через WinAPI, событие не появится. Запись в реестре выглядит так:

HKLM\SYSTEM\CurrentControlSet\Services\<имя_службы>
  ImagePath  = "C:\payload\evil.exe"
  Start      = 2  (автозапуск)
  Type       = 16 (отдельный процесс)

При следующей перезагрузке SCM читает реестр и запускает службу, не зная, как запись там оказалась. Sysmon Event ID 13 (изменение реестра) покрывает этот вектор. Отсутствие 7045 не означает отсутствия новой службы. Только то, что создание прошло в обход SCM.

Кластер активности Blue Mockingbird, который распространял криптомайнер, использовал sc config для подмены существующей службы wercplsupport. Команда меняла binPath у уже существующей службы, а не создавала новую. При модификации существующей службы Event 7045 не генерируется вовсе. Фиксируется Event ID 7040 (изменение службы), который во многих окружениях не покрыт правилами детектирования.

Группа RagnarLocker шла другим путём. Через sc.exe создавалась служба VBoxDRV для запуска драйвера VirtualBox. Шифровальщик выполнялся внутри виртуальной машины. EDR на хосте видел стандартный процесс VirtualBox и не срабатывал. Event 7045 при этом генерировался с именем VBoxDRV. Внешне выглядело как легитимная инсталляция виртуализации.

Отдельно про переменную %COMSPEC%. Большинство правил детектирования ищут строку cmd.exe в binPath. %COMSPEC% является переменной среды, которая указывает на тот же cmd.exe. Правило, написанное под литеральное cmd.exe, записи с %COMSPEC% не поймает. SCM при запуске службы раскрывает переменные среды, и служба с binPath= "%COMSPEC% /c script.bat" отработает штатно.

Почему sc.exe не блокируют в корпоративных средах

sc.exe входит в категорию LOLBin, Living-off-the-Land Binary. Нативные утилиты, подписанные вендором ОС, используются атакующими именно потому, что их нельзя заблокировать без разрушения нормальной работы системы.

Блокировка sc.exe через AppLocker или WDAC ломает скрипты развёртывания, аварийное восстановление, мониторинг и аудит. На практике это инциденты при деплое и администраторы в нестандартных ситуациях без возможности управлять службами.

Вместо блокировки применяется трёхуровневый подход.

Первый уровень охватывает ограничение привилегий. Права на создание служб только у выделенных административных аккаунтов. Тиеринг административного доступа: Tier 0 для контроллеров домена, Tier 1 для серверов, Tier 2 для рабочих станций. Атакующий с доступом к Tier 2 аккаунту не сможет создать службу на сервере Tier 1.

Второй уровень касается сетевой сегментации. Запрет RPC между рабочими станциями, разрешение только с выделенных jump-хостов. Удалённое управление службами через sc.exe требует открытого порта 135. Закрытый порт между конечными точками делает удалённое создание служб через эту утилиту невозможным.

Третий уровень строится на детектировании. Алерты на Event 7045 с фильтром по аномальным признакам binPath. Корреляция между сетевым входом (Logon Type 3) и последующим созданием службы в течение короткого времени на одном хосте.

Детектирование важнее блокировки по одной причине: атакующий с правами администратора обойдёт sc.exe через прямой вызов CreateService из WinAPI. Блокировка утилиты не блокирует функцию. Детектирование аномального поведения работает независимо от того, каким способом служба была создана.

Скрипты со sc.exe без ошибок синтаксиса

Пробел после знака равенства обязателен в каждом параметре, разбирали выше. Кавычки в binPath при пробелах в пути: весь путь в кавычках как значение параметра, но не весь аргумент целиком.

Проверка кода завершения после каждой команды обязательна в автоматизации. sc.exe возвращает 0 при успехе и ненулевой код при ошибке. В batch-скриптах проверяется через %ERRORLEVEL%, в PowerShell через $LASTEXITCODE.

Шаблон скрипта с базовыми проверками:

set SVC_NAME=MyAgent
set BIN_PATH=C:\tools\agent.exe

sc create %SVC_NAME% binPath= "%BIN_PATH%" start= auto DisplayName= "My Agent"
if %ERRORLEVEL% NEQ 0 (
    echo Ошибка создания службы
    exit /b 1
)

sc start %SVC_NAME%
if %ERRORLEVEL% NEQ 0 (
    echo Ошибка запуска службы
    sc delete %SVC_NAME%
    exit /b 1
)

timeout /t 5 >nul
sc query %SVC_NAME% | findstr "RUNNING" >nul
if %ERRORLEVEL% NEQ 0 (
    echo Служба не достигла состояния RUNNING
    sc stop %SVC_NAME%
    sc delete %SVC_NAME%
    exit /b 1
)

echo Служба успешно развёрнута

Переменные для повторяющихся значений снижают риск опечаток. Логирование команд sc.exe в файл ускоряет анализ инцидентов: если что-то пошло не так, видно точно когда и с какими параметрами создавалась служба.

Тестирование на чистой виртуальной машине перед запуском в рабочей среде не является факультативным шагом. Ошибка в имени бинарника или в параметрах учётной записи не всегда заметна сразу, но обнаруживается при перезагрузке системы.

Частые ошибки и диагностика

Access is denied. Скрипт или команда запущены без прав администратора. Текущие привилегии проверяются командой whoami /priv. Нужна привилегия SeServiceLogonRight для учётных записей служб и права локального администратора для управления.

The specified service does not exist as an installed service. Опечатка в имени службы или служба уже удалена. Поиск с нечувствительностью к регистру: sc query state= all | findstr /i "фрагмент_имени".

The service did not respond to the start or control request in a timely fashion. Служба зависла при запуске или остановке. Стандартный таймаут SCM составляет 30 секунд. sc qc <имя> покажет конфигурацию, журнал приложений Windows может содержать сообщения от самой службы о причине зависания.

Error 5 при удалённом подключении. Кроме прав администратора на целевой машине нужна открытая связь по RPC. Доступность порта проверяется командой Test-NetConnection SERVER01 -Port 135 в PowerShell. При закрытом порту sc.exe не поможет, нужно открывать правило брандмауэра или использовать jump-хост.

The database does not exist. Признак повреждения раздела реестра с записями служб. В рабочих системах без предшествующего серьёзного события не встречается. Восстановление через System Restore или резервную копию реестра.

Если служба создаётся нормально, но при запуске немедленно останавливается, причину стоит искать в Event Viewer, журнал приложений, а не в sc.exe. Утилита передала команду SCM, SCM попытался запустить бинарник, бинарник вернул ошибку. sc.exe в этой цепочке выполнил свою функцию корректно.

Нестандартное поведение с очень длинными путями в binPath воспроизводится непоследовательно в некоторых сборках Windows 11. Вероятная причина кроется в изменениях обработки командных аргументов в последних обновлениях. При столкновении с этим стоит проверить New-Service в PowerShell как альтернативу.


#кибербезопасность #инфобез #IT #технологии #безопасностьIT #защитаданных #безопасностьсети #информационнаябезопасность #киберзащита #безопасность

Оставьте комментарий