bootdoor: передача управления через границы загрузки

Runtime-копия, виртуальная карта и ACPI.SYS как мост между UEFI и ядром Windows.

Архитектурный смысл образца

bootdoor показывает сравнительно компактную цепочку переноса исполнения. Входной EFI-код сохраняет исходные функции таблиц, размещает свою копию в памяти типа EfiRuntimeServicesData и перехватывает ExitBootServices и SetVirtualAddressMap. Далее он пытается получить контроль в момент перехода загрузчика к ядру и использует уже загруженный драйвер ACPI.SYS как промежуточную точку исполнения. Авторство GuidePoint Security указано в заголовках файлов. E28 E31

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

Первый вопрос: откуда взялось право стартовать

EfiMain начинается уже в исполняющемся EFI-контексте. Это исходное условие, а не доказательство пути его получения. В комментарии упоминается намерение поддерживать Secure Boot, но само описание намерения не показывает эксплуатацию доверенного загрузчика или преодоление политики подписи. E28

Для сравнительной таблицы поэтому используются две независимые колонки: «начальный контроль» и «действия после него». Изменённая прошивка виртуальной машины, контролируемый EFI-файл и эксплуатация уязвимости загрузчика могут привести к схожей поздней стадии, но означают совершенно разные исходные возможности противника. Смешивание этих сценариев искусственно завышает доступность атаки.

Runtime-память решает только одну часть задачи

Вход вычисляет объём копии, выделяет страницы и сохраняет физический адрес. Пока код работает в одной среде, адрес выглядит достаточным. Однако после передачи управления ОС он может потребовать иного представления. Именно поэтому существует отдельный SetVirtualAddressMap: он обходит дескрипторы карты, находит диапазон с собственной копией и вычисляет соответствующий виртуальный адрес. E28 E30

Этот участок делает явным важный принцип: «память не освобождена» и «код может корректно обратиться к этой памяти» — разные свойства. Необходимо также, чтобы страницы имели допустимые права и чтобы указатели, которыми пользуется программа, относились к действующему адресному пространству. Наличие типа RuntimeServicesData не является гарантией исполнимости на любой платформе.

Официальная спецификация UEFI описывает отдельный контракт runtime services и виртуального отображения. Из него не следует разрешение использовать произвольные boot services после завершения загрузочной фазы. Поэтому в анализе bootdoor адресный переход рассматривается как конкретный механизм, а не как универсальная магия «ниже ОС». S24

ExitBootServices как точка наблюдения

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

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

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

Почему используется ACPI.SYS

OslArchTransferToKernel обходит список загруженных модулей, ищет ACPI.SYS и его ресурсную секцию, сохраняет исходные параметры драйвера и меняет точку входа вместе с характеристиками секции. Модификация происходит в загруженном образе. Поэтому наличие исходного подписанного файла на диске не описывает весь путь исполнения этой копии. E31

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

Из этого нельзя заключать, что весь ACPI.SYS подменён на диске, или что процесс обязательно переживёт любой механизм контроля целостности. Надёжность восстановления и совместимость с HVCI требуют отдельной проверки. В частности, восстановление отдельных полей PE не тождественно доказанному восстановлению всех инвариантов загрузчика и драйвера.

Red и Blue смотрят на разные свидетельства

Red: содержательная проверка гипотезы должна показать переход между контекстами. Вывод EFI-функции, вызов функции ядра и дальнейшая жизнеспособность ОС — отдельные контрольные точки.

Blue: ищите не только неизвестный файл, но и несоответствие происхождения указателей. Если entrypoint или исполняемая область не объясняется нормальной загрузкой соответствующего образа, это повод углубить расследование.

При работе с дампом сравнение должно учитывать релокации, исправления импортов и законные особенности загрузки. Подозрительное изменение становится убедительнее, когда одновременно объясняет перенаправленный control flow, необычное владение памятью и несоответствие исходному образу. Один необычный флаг секции — слабее такой совокупности.

Резидентность, закрепление и восстановление

СвойствоЧто видно в кодеКакой факт ещё нужен
Сохранение копииВыделение runtime pagesРеальная карта и права страниц
Переход к ядруИзменение пути boot driverНаблюдение вызова на выбранной ОС
Продолжение загрузкиВызов оригинальных функцийПроверка полного жизненного цикла
Повторный запуск после выключенияНе определяется одним RAM-hookНоситель и механизм активации
Первичный обход Secure BootНе показан точкой EfiMainОтдельная цепочка допуска к исполнению

Восстановление системы должно соответствовать найденному источнику повторного запуска. Очистка текущей памяти не удаляет изменённый firmware image, а переустановка Windows не гарантирует восстановление прошивки виртуальной машины. Это причинный вывод из разделения уровней, а не утверждение, что bootdoor в данном исследовании был установлен на какой-либо системе.

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

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

E28 / bootdoor-master/EfiMain.c

Строки 25–83 · Копия в runtime data и два hook: ExitBootServices / SetVirtualAddressMap.

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

SHA-256 150c3a1d791f17ec3fe6c7b05345d890cd94da952c53ad5ac3621bb28ef6daf4

E31 / bootdoor-master/OslArchTransferToKernel.c

Строки 36–113 · ACPI.SYS как переход к исполнению в ядре; модификация entrypoint и секции в памяти.

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

SHA-256 562ed3f9bc1003658cffdd798d19e1fb3eb042e51fb43dc699bf2a180b68eae3

E30 / bootdoor-master/SetVirtualAddressMap.c

Строки 21–51 · Пересчёт адреса своей копии по дескрипторам виртуальной карты.

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

SHA-256 a1ba49c77e6a21accddfa520eec8f28bd089d1de0a4f5fa5dab301d80a8222f1

S24 / UEFI 2.11: Runtime Services ↗

UEFI Forum · 2.11. Первичный внешний источник. Доступ: 12.09.2026.

E29 / bootdoor-master/ExitBootServices.c

Строки 21–109 · Поиск PE вызывающего образа и сигнатур winload до передачи управления.

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

SHA-256 39085d6547f3a7f09f45dd0f9d0a415ab0c4284058bce85d1f3f5e5487e917bb

E32 / bootdoor-master/DrvMain.c

Строки 48–100 · Вызов переданной нагрузки и восстановление части состояния драйвера.

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

SHA-256 eff6d30747a38e4f51ed7cee0a0717a930c5ded22d359c4b4ec2fbf2ca5831ed

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