Insomnia: ранняя модификация системного вызова

Компактный bootkit с жёсткими предположениями об ABI, адресах и внутреннем устройстве Windows.

Что делает видимая цепочка

Insomnia начинает с UefiMain, пытается перенести свой образ и устанавливает ExitBootServicesHook. В этом обработчике ищутся winload, внутренний контекст загрузчика, LoaderBlock и ntoskrnl. Далее код находит системную службу NtShutdownSystem и меняет связанный путь диспетчеризации до нормального старта ядра. Это отличает проект от runtime-канала через переменные UEFI у Abyss. E39 E40

Центральный механизм — повторное использование существующего системного вызова как входа к иной логике. Здесь нет необходимости предполагать отдельный network listener или управляющий driver device: канал описан через изменённую обработку вызова ядра. Но его доступность и корректность на конкретной системе не проверены динамически.

Вход содержит проверяемое несоответствие ширины

Переменная нового расположения образа объявлена как uint32_t, а в AllocatePages передаётся её адрес, приведённый к EFI_PHYSICAL_ADDRESS*. На x64 это несоответствие размеров объекта и типа указателя, через который вызываемая функция записывает результат. Кроме того, дальнейшая логика использует усечённое представление адреса. Это конкретная статическая находка, а не предположение по README. E39

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

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

Winload: внутренние структуры вместо устойчивого интерфейса

ExitBootServicesHook использует сигнатуры для поиска BlpArchSwitchContext и ссылки на OslLoaderBlock. Затем переключает контекст, получает базу ntoskrnl и ищет дальнейшие внутренние структуры. Это сильная зависимость от выпуска ОС, компилятора и конкретной последовательности машинных инструкций. E40

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

Внутренние offsets и представление таблицы служб также требуют проверки по точной сборке. Публичный ABI приложения и внутреннее кодирование данных ядра — разные уровни стабильности. Наличие экспортируемого имени NtShutdownSystem не делает все структуры вокруг него документированным интерфейсом для модификации.

Kernel hook: маркер не является авторизацией

NtShutdownSystemHook читает данные, которые связывает с пользовательским стеком, проверяет служебный маркер и затем использует переданные аргументы для вызова адреса. При несовпадении признаков пытается найти сохранённый оригинал и вернуться к обычной операции. E41

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

Точная возможность использования из конкретного пользовательского процесса зависит от полного пути вызова, политики ОС и состояния изменённого ядра. Досье не выдаёт эти условия за проверенные. Статически подтверждён характер перехода доверия: привилегированный код допускает, что переданные данные описывают допустимый адрес и аргументы.

Почему «до старта ядра» не означает «защита больше не действует»

Ранняя запись может предшествовать обычной инициализации отдельных проверок, но из её времени нельзя вывести универсальный обход всей платформы. Если независимый механизм контролирует допустимость исполняемых страниц, изменённый объект должен ещё пройти его ограничения. Документация HVCI как раз отделяет решение об исполнении от обычного ядра. S15

Также ранняя модификация не доказывает закрепление после выключения. Нужен путь, который снова запустит EFI-компонент. Три вопроса — первый запуск, перенос в ядро и повторная активация — должны иметь три набора свидетельств. В исходнике UefiMain начинается уже после решения о допуске самого EFI-образа. E39

Red: проверяемая гипотеза ограничивается конкретной сборкой и состоянием защит. Нельзя расширять успешный эпизод на другие версии только потому, что у них совпадает имя системной службы.

Blue: полезны происхождение обработчика системной службы, целостность соответствующих областей ядра и состояние более привилегированного enforcement. Отдельный пользовательский клиент без следов модификации ядра не доказывает, что цепочка активна.

Как читать результаты лаборатории

РезультатВозможное объяснениеСледующий вопрос
EFI напечатал сообщениеДостигнут участок входаКорректны ли перенос и адреса?
Загрузка завислаПоиск или память не соответствуют ожиданиямГде последний подтверждённый переход?
ОС работаетИсходная загрузка продолжиласьИзменён ли целевой путь?
Вызов возвращает необычный результатВозможно, достигнут hookКак подтверждён адрес исполненного кода?
После перезапуска следов нетНе сработала повторная активацияГде должен храниться её источник?

Это таблица интерпретации, а не результаты выполненных опытов. Она защищает от привычной ошибки: считать каждую аварию победой защиты, а каждую строку журнала победой bootkit.

Вывод для исследователя

Insomnia представляет интерес как компактная демонстрация идеи раннего изменения syscall-пути и одновременно как пример хрупкости низкоуровневых предположений. Его анализ требует проверки ширины типов, ABI, времени жизни памяти и точных структур ОС. Без этого красивая схема из нескольких стрелок скрывает основную часть реальной сложности.

Источники и исходники

Ссылки E ведут к свидетельствам по локальному коду; S — к первичным внешним публикациям. Диапазон относится к оригинальному файлу. Статическое исследование, без запуска образцов.

E39 / Insomnia-main/Bootkit/main.cpp

Строки 9–46 · 32-битная переменная передаётся как EFI_PHYSICAL_ADDRESS*.

Локальный снимок · файл: 46 строк · ссылка на публичный commit не установлена.

SHA-256 e810b61617191c5e2e7a2ca57edf1e3cf88827704a0cae4ba33b6891c8ff8ef2

Посмотреть фрагмент исходника · строки 19–23
  19      uint32_t new_location = 0;
  20      uint32_t pages_count = EFI_SIZE_TO_PAGES(global::ImageSize);
  21  
  22      /* allocate new pages */
  23      if (global::BootServices->AllocatePages(AllocateAnyPages, EfiLoaderCode, pages_count, (EFI_PHYSICAL_ADDRESS*)&new_location) == EFI_SUCCESS)

E40 / Insomnia-main/Bootkit/ExitBootServices.cpp

Строки 3–95 · Поиск winload/ядра и изменение таблицы системных служб до старта ядра.

Локальный снимок · файл: 95 строк · ссылка на публичный commit не установлена.

SHA-256 bd199edae4ea0d52a513ff9a28f168f22468980ab79009dc2038ea517fe1fa0b

E41 / Insomnia-main/Bootkit/NtShutdownSystem.cpp

Строки 3–41 · Пользовательские данные ведут к вызову адреса в ядре; magic не является аутентификацией.

Локальный снимок · файл: 41 строк · ссылка на публичный commit не установлена.

SHA-256 f93a7d57b94127dd9d7e514502ec09dceaf8e8287c4fea1d48942714696b10bf

S15 / Memory integrity and VBS enablement ↗

Microsoft Learn · доступ 12.09.2026. Первичный внешний источник. Доступ: 12.09.2026.

← Предыдущий материалbootlicker: от firmware-контекста к пользовательскому процессуСледующий материал →Antarctic: разрыв между подписанным файлом и ядром в памяти