Классификация без подмены понятий
Benthic в исследованном дереве — драйверный rootkit. Его основная точка сборки поведения находится в CustomDriverEntry, а не в EFI entrypoint. Драйвер создаёт управляющий объект, подключает обработчики IRP, клавиатурный фильтр, NSI, WFP и minifilter. Эта архитектура предполагает уже полученное исполнение в ядре. Само её наличие не демонстрирует первоначальное проникновение, обход Secure Boot или запись в SPI flash. E10
Сила этого образца как учебного объекта — в разнообразии механизмов. Один компонент меняет структуру данных ядра, другой подменяет результат запроса, третий пользуется официальным фильтрующим интерфейсом. Для защиты они требуют разных наблюдателей. Универсальная формула «проверить список драйверов» охватывает лишь часть вопроса.
DKOM: исчезновение из списка не равно исчезновению процесса
Модуль DirectKernelObjectManipulation использует фиксированные смещения полей процесса, обходит ActiveProcessLinks и при совпадении сохраняет соседние ссылки в собственном объекте учёта. Затем меняет связи в исходном списке. Процесс не уничтожается этим действием: меняется один способ его перечисления. E13
Отсюда следует защитная гипотеза cross-view: объекты, потоки, дескрипторы и сетевые следы могут описывать активность, которой нет в одном перечислении процессов. Но два приложения, использующие один и тот же источник перечисления, не дают два независимых вида. Для проверки нужен другой путь получения сведений и понимание его собственной зависимости от ядра.
Фиксированные offsets делают образец чувствительным к версии структуры. В отчёте должны быть архитектура, точная сборка ОС и используемые символы. Нельзя объяснять отсутствие скрытия «сильной защитой» до исключения банального несовпадения layout. Отдельного внимания заслуживают завершение процесса и конкурентное изменение списка: сохранённая ссылка на соседа не обещает, что сосед останется жив до обратной операции.
Keyboard filter: конкретный стек и конкретный выход данных
Клавиатурный модуль присоединяется к KeyboardClass0, направляет запрос чтения ниже по стеку и устанавливает completion routine. По завершении чтения он обрабатывает KEYBOARD_INPUT_DATA при включённом флаге. В просмотренном фрагменте наблюдается отладочный вывод scan codes, а не доказанный универсальный сбор всего пользовательского ввода с доставкой на сервер. E14
Это различие имеет практический смысл. Ввод через другой стек, виртуальное устройство или иной путь нельзя автоматически включать в подтверждённый охват. Для Blue полезны состав device stack, адрес completion routine, происхождение драйвера и его время жизни. Сам фильтр ещё не означает rootkit: легитимные драйверы также располагаются в стеке устройств. Существенны назначение, авторизация установки и поведение.
NSI и WFP решают разные задачи
NSI-компонент заменяет dispatch для IRP_MJ_DEVICE_CONTROL у целевого драйвера и меняет completion routine определённого запроса. При завершении он удаляет из возвращаемого набора записи, связанные с выбранными портами. Это искажение представления о соединениях; оно не стирает пакет из внешнего сетевого датчика. В коде видны зависимости от внутреннего формата буфера и его полей. E15
WFP-компонент, напротив, принимает решение о разрешении или блокировке на основании удалённого IPv4-адреса. Здесь используется механизм классификации сетевого потока. Поэтому пропавшая строка в локальном списке и реально заблокированный connect — разные наблюдения с разными причинными цепочками. E16
Red: вопрос к эксперименту — какой именно наблюдатель перестал видеть активность. Утверждение «соединение скрыто» должно содержать источник данных, иначе его нельзя проверить.
Blue: сравнение локального списка с независимым сетевым журналом выявляет расхождение представлений. Оно требует учёта NAT, временных окон, короткоживущих соединений и легитимных фильтров.
WSK: HTTP-клиент ещё не завершённый C2
Просмотренная реализация WSK формирует заранее заданный HTTP GET, соединяется, отправляет запрос, получает ответ и закрывает сокет. Периодический системный поток повторяет эту операцию. В этом пути не обнаружен разбор ответа в команды с диспетчеризацией действий. Поэтому корректное описание — канал периодических запросов из ядра и заготовка для коммуникации, а не подтверждённый полноценный командный сервер. E17
Для защиты источник сетевой активности важнее рекламного названия: исходящий запрос из kernel-компонента может быть виден за пределами машины, даже когда обычное процессное объяснение неполно. Однако странное соответствие процессу без анализа стека и драйверов не позволяет приписать запрос именно Benthic.
Minifilter: фильтрация каталога и исключение для KernelMode
Файловый модуль работает на постобработке IRP_MJ_DIRECTORY_CONTROL и выбирает layout под конкретный FILE_INFORMATION_CLASS. Он сравнивает путь и имя с собственным списком, затем меняет возвращаемую последовательность записей. В начале явно исключён RequestorMode == KernelMode. Это важный пример ограниченного охвата: поведение зависит не только от имени файла, но и от типа запросившей стороны. E18
Удаление записи из результата перечисления не равно удалению файла и не тождественно запрету открытия по известному имени. Для анализа нужны отдельные операции: перечисление, открытие, чтение метаданных и независимое исследование носителя. Нельзя выводить защитную гарантию из одного удачного запроса: присутствие иных перехватов способно менять другой путь.
Управляющий интерфейс сам становится проблемой
Объект устройства создаётся с FILE_DEVICE_SECURE_OPEN. Этот флаг не является полным описанием ACL и не доказывает, кому доступен handle. Для вывода о доступности нужны security descriptor, INF и фактическая конфигурация. Одна строка IoCreateDevice не позволяет объявить интерфейс открытым всем пользователям. E11
Зато в обработчике IRP виден конкретный дефект границы буфера: ответ копируется в SystemBuffer без сравнения его длины с OutputBufferLength в этой функции. Возвращённый вложенным обработчиком status сохраняется в локальную переменную, но IoStatus.Status затем принудительно выставляется в STATUS_SUCCESS. Следовательно, результат управляющего клиента может не отражать ошибку операции. Это наблюдение по коду; эксплуатация ошибки и достижимость из непривилегированного контекста не проверялись. E12
Cleanup — часть механизма, а не приложение к нему
CustomDriverEntry подключает несколько компонентов последовательно. В ряде последующих ветвей ошибки удаляются device object и symbolic link, но в показанном пути нет симметричного отсоединения всех уже подключённых механизмов. Основной DriverUnload вызывает несколько процедур выгрузки, однако не повторяет полный список инициализации. Это повод проверять владение callback, ссылками и ресурсами в каждом частично успешном состоянии. E10
Такой rootkit может одновременно скрывать объект и ухудшать устойчивость системы. Авария не доказывает успешную защиту, а успешная загрузка не доказывает безопасный unload. В защитном исследовании стабильность, скрытность и привилегии должны измеряться отдельно.
Матрица наблюдений
| Механизм | Изменяемый объект | Независимый ориентир | Ограничение вывода |
|---|---|---|---|
| DKOM | Связи списка процессов | Потоки и ссылки на объекты | Layout и конкурентные изменения |
| Keyboard filter | Путь завершения чтения | Состав стека устройств | Не весь ввод проходит один стек |
| NSI | Результат перечисления сети | Внешние сетевые наблюдения | Буфер и версия интерфейса |
| WFP | Решение о connect | Состояние callout и сетевые ошибки | Фильтрация бывает легитимной |
| WSK | Источник HTTP-запросов | Прокси и сетевой датчик | Запрос не доказывает C2 |
| Minifilter | Результат списка каталога | Иной путь доступа и образ диска | Перечисление не равно открытию |
Матрица описывает исследовательские гипотезы, выведенные из перечисленных исходников. Это не набор готовых сигнатур и не измеренные показатели обнаружения. Для каждого датчика ещё требуется установить, какой уровень системы он вынужден считать достоверным.
Источники и исходники
Ссылки E ведут к свидетельствам по локальному коду; S — к первичным внешним публикациям. Диапазон относится к оригинальному файлу. Статическое исследование, без запуска образцов.
E10 / Benthic-main/BenthicRootkit/BenthicZone02_KernelModeDriver/Main_Driver.c
Строки 86–254 · Последовательное присоединение компонентов и неполная симметрия cleanup.
Локальный снимок · файл: 259 строк · ссылка на публичный commit не установлена.
SHA-256 8112dd3602999a5f968c572626780c9b43043e238a9d82f34084239c9bb59993
E13 / Benthic-main/BenthicRootkit/BenthicZone02_KernelModeDriver/Functions/Techniques/DirectKernelObjectManipulation.c
Строки 60–195 · Фиксированные offsets и разрыв ActiveProcessLinks; отдельный список скрытых объектов.
Локальный снимок · файл: 511 строк · ссылка на публичный commit не установлена.
SHA-256 60bda2f1e3a03196e110ea5b6515a3ab0ff781478d53624d1eb34c77da6f76cd
E14 / Benthic-main/BenthicRootkit/BenthicZone02_KernelModeDriver/Functions/Techniques/KeyboardFilter.c
Строки 144–348 · KeyboardClass0, completion routine чтения; вывод scan codes в отладочный журнал.
Локальный снимок · файл: 455 строк · ссылка на публичный commit не установлена.
SHA-256 7e54d467502a91f4fd97a06275c66c25693afa7874d688a596523bb4d551f6ef
E15 / Benthic-main/BenthicRootkit/BenthicZone02_KernelModeDriver/Functions/Techniques/NetworkStoreInterface.c
Строки 163–325 · Фильтрация результатов NSI по портам через подменённый dispatch.
Локальный снимок · файл: 557 строк · ссылка на публичный commit не установлена.
SHA-256 4d36ee3a1264ee912db95adfb4e6e7285acf4b5f107e0735e66de623fba88489
E16 / Benthic-main/BenthicRootkit/BenthicZone02_KernelModeDriver/Functions/Techniques/WindowsFilteringPlatform.c
Строки 261–317 · Решение о блокировке адреса в classify callback.
Локальный снимок · файл: 837 строк · ссылка на публичный commit не установлена.
SHA-256 ae3962f1444ccfb7a45fc4e3ec3885d23f4187b02d93da0b44c47fe4e06da7a7
E17 / Benthic-main/BenthicRootkit/BenthicZone02_KernelModeDriver/Functions/Techniques/WinSockKernel.c
Строки 198–375 · Предзаданный HTTP-запрос и периодический поток; здесь нет разбора команд ответа.
Локальный снимок · файл: 533 строк · ссылка на публичный commit не установлена.
SHA-256 1771945bcbf3eba03427f1d3c1bb32bbf0adb8a3f7e9207dd6d5ebcb0e578531
E18 / Benthic-main/BenthicRootkit/BenthicZone02_KernelModeDriver/Functions/Techniques/MiniFilter.c
Строки 146–385 · Постобработка перечисления каталога; KernelMode исключён.
Локальный снимок · файл: 1046 строк · ссылка на публичный commit не установлена.
SHA-256 759ac86a7ab9220cff57b39a50eabaeadaa31455a1234a0c41ce368b0a1263bf
Посмотреть фрагмент исходника · строки 161–164
161 if (Data->RequestorMode == KernelMode || Data->Iopb->MinorFunction != IRP_MN_QUERY_DIRECTORY || (Flags & FLTFL_POST_OPERATION_DRAINING))
162 {
163 return FLT_POSTOP_FINISHED_PROCESSING;
164 }E11 / Benthic-main/BenthicRootkit/BenthicZone02_KernelModeDriver/Functions/Functions00Device.c
Строки 52–115 · Именованный device object; FILE_DEVICE_SECURE_OPEN не задаёт ACL сам по себе.
Локальный снимок · файл: 120 строк · ссылка на публичный commit не установлена.
SHA-256 f0b2082b75f5770c9faefbcab0b0888827b8fe9cef0af7b7ce7439dd0a73dda7
E12 / Benthic-main/BenthicRootkit/BenthicZone02_KernelModeDriver/Functions/Functions02IRPs.c
Строки 112–185 · Ответ копируется без сравнения с OutputBufferLength; status обработчика не передаётся вызывающему.
Локальный снимок · файл: 235 строк · ссылка на публичный commit не установлена.
SHA-256 b0b780d90dbbfb5a4e61c3f1e1fcd1ab575d8dedc2b870987c4cb0cf8f217182
Посмотреть фрагмент исходника · строки 163–165
163 RtlCopyMemory(pIrp->AssociatedIrp.SystemBuffer, responseBuffer, strlen(responseBuffer) + 1);
164 pIrp->IoStatus.Information = strlen(responseBuffer) + 1;
165 pIrp->IoStatus.Status = STATUS_SUCCESS;