BlackIris: проверка обещаний исходным кодом

Почему имя функции не доказывает её семантику, а директория KernelMode — исполнение в ядре Windows.

Что именно исследовано

Объект этого досье — локальная директория BlackIris-main с кодом, подписанным AitorFirmware в LICENSE. Название само по себе не устанавливает связь с известной кампанией, коммерческим продуктом или другим bootkit. В частности, BlackIris нельзя отождествлять с BlackLotus: последний описан в отдельном исследовании ESET с собственной цепочкой и CVE. S28

В этом снимке главное расхождение проходит между широтой README и полнотой достижимой реализации. Вход ведёт в InitializeBootkit; эта функция сохраняет таблицы UEFI и вызывает установку перехвата ExitBootServices. Она не связывает все каталоги в единую доказанную цепочку. Некоторые подсистемы содержат явные заглушки. E19 E25

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

MmMapIoSpace: знакомое имя, другая реализация

В Boot/MemTools.c определена локальная функция MmMapIoSpace. Она вызывает UEFI AllocatePages с AllocateAddress и возвращает адрес выделенной области. Это не одноимённая Windows DDI. Выделить страницы по желаемому адресу и отобразить произвольный уже существующий физический диапазон — разные операции с разными предусловиями. E22

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

Копирование функции не является загрузчиком

Перехват ExitBootServices копирует фиксированный объём байтов от KernelMain в выбранную область и вызывает её как функцию без параметров. Но KernelMain объявлена с аргументом EFI_SYSTEM_TABLE*. Уже на уровне сигнатур видна несогласованность. Дополнительно фиксированный размер копирования не доказывает перенос всех зависимых данных, относительных ссылок и вызываемых функций. E20 E21

Это архитектурная проблема доказательства: чтобы утверждать перенос исполняемой программы, нужно показать её формат, релокации, зависимости, ABI и время жизни. Название KernelRegion не переводит процессор в Windows kernel context. В момент вызова перед оригинальным ExitBootServices всё ещё действует загрузочный контекст UEFI.

Официальный контракт UEFI различает службы до и после завершения загрузочной фазы. Он не обещает, что произвольная функция, скопированная в runtime data, сможет продолжать пользоваться console input и boot services под запущенной ОС. S24

RemoteControl и Keylogger: какой канал существует на самом деле

InitRemoteControl выводит сообщение и возвращает TRUE. RemotePoll читает строку из локального буфера по условному физическому адресу, сравнивает её с предыдущей и передаёт обработчику. В этом участке нет сетевого соединения, аутентификации удалённой стороны или приёма TCP-сообщения. Наличие другого серверного каталога в проекте не доказывает транспорт между ним и этой функцией. E23

Клавиатурная часть использует EFI_SIMPLE_TEXT_INPUT_PROTOCOL и ReadKeyStroke. Подтверждённая семантика — чтение консольного ввода UEFI, пока соответствующий протокол доступен. Это не фильтр Windows keyboard stack и не основание заявлять сбор ввода из любых приложений после загрузки. E24

Red: обязательный вопрос к демонстрации — в каком контексте получен вывод. Строка из EFI-консоли не подтверждает работу на рабочем столе Windows, а локальный shared buffer не подтверждает удалённое управление.

Blue: опирайтесь на реально присутствующий артефакт и путь исполнения. Не стоит строить сетевой детектор несуществующего в просмотренной ветви протокола лишь потому, что README использует слово remote.

SMM: название обработчика не пересекает аппаратную границу

InstallSMIHandler в Shared/SyscallBridge.c прямо сообщает placeholder и возвращает TRUE. Отдельный PatchSMI.c сканирует память в поисках условной сигнатуры, а затем пытается изменить найденный участок. Этот код не показывает преодоление блокировок SMRAM и не доказывает, что найденная последовательность является настоящим SMI handler. E25 E27

Для вывода о SMM-импланте потребовались бы свидетельства выполнения в SMM, идентификации защищённого региона и условий доступа к нему. Здесь их заменяет предположение о доступности адресов. Методологически это тот же разрыв, что и с MmMapIoSpace: лексика проекта выглядит как низкоуровневая архитектура, но названия не дают привилегий.

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

Криптографическое обещание тоже проверяется в определении

В CryptoDefs.h комментарий прямо говорит, что SimpleAES не реализует полноценный AES. Тело выполняет собственные преобразования с таблицей подстановок и битовыми операциями. Поэтому называть этот компонент реализацией AES-256 на основании имён AESCore и AES_CONTEXT некорректно. E26

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

Таблица заявлений и свидетельств

ЗаявлениеНаблюдение по снимкуДопустимый вывод
Работа в Windows KernelModeEFI-типы и console input в KernelMainНазвание каталога не устанавливает контекст
Runtime persistenceHook до оригинального ExitBootServicesНужно отдельно доказать жизнь после перехода
Remote controlPolling локальной памятиСеть в этом пути не подтверждена
SMI handlerЯвная заглушкаРегистрация обработчика не реализована здесь
AES-256Собственное преобразование, авторский комментарийСтандартный AES не подтверждается
Secure Boot bypassВход предполагает уже исполняемый EFIПервичный обход доверия не показан этим входом

Чем полезен такой образец

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

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

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

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

S28 / BlackLotus UEFI bootkit: Myth confirmed ↗

Martin Smolár / ESET Research · 01.03.2023. Первичный внешний источник. Доступ: 12.09.2026.

E19 / BlackIris-main/src/Boot/Loader.c

Строки 9–22 · Достижимая из входа инициализация ограничена установкой ExitBootServices hook.

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

SHA-256 d109ec001c162ac5a3145bdf21083d1a566cc6acf9b8d25c9b3048d94bb3109c

Посмотреть фрагмент исходника · строки 16–19
  16      Status = InstallExitBootServicesHook();
  17      if (EFI_ERROR(Status)) {
  18          return Status;
  19      }

E25 / BlackIris-main/src/Shared/SyscallBridge.c

Строки 19–33 · SMI-функция явно placeholder; отдельный локальный массив патча слишком мал.

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

SHA-256 28fe6edcb56e8b47cbea1f48fb34e2bd48b800a235a3466472a123cb7451bdcb

Посмотреть фрагмент исходника · строки 19–22
  19  BOOLEAN InstallSMIHandler(VOID* HandlerFunc) {
  20      LogInfo(L"SMI Handler installed (placeholder).");
  21      return TRUE;
  22  }

E22 / BlackIris-main/src/Boot/MemTools.c

Строки 5–26 · Локальная функция с именем MmMapIoSpace использует AllocatePages; это не Windows DDI.

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

SHA-256 1cb87560aac88e6e795ced89d04f2ad85d56398716a794b8363845417ed7b004

E20 / BlackIris-main/src/Boot/DXEHook.c

Строки 11–31 · Фиксированное копирование тела функции и вызов без параметра SystemTable.

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

SHA-256 71307a623e9089577d029ee1b058aa43261e6f638de816905dfe56e556d115b5

E21 / BlackIris-main/src/KernelMode/BotStartup.c

Строки 13–35 · KernelMain остаётся функцией с UEFI-типами, принимающей SystemTable.

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

SHA-256 26767647b36b3e9f71844b75f555a0453507db770ee80dcfe6af9244242ec149

S24 / UEFI 2.11: Runtime Services ↗

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

E23 / BlackIris-main/src/KernelMode/RemoteControl.c

Строки 11–28 · Инициализация лишь возвращает TRUE; polling читает локальный буфер.

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

SHA-256 ad9f9af9b440ac4fbb7fbd8fad89f547024a688c318b88b76a19a9a284b5756c

Посмотреть фрагмент исходника · строки 11–14
  11  BOOLEAN InitRemoteControl(void) {
  12      LogInfo(L"Started successfully\n");
  13      return TRUE;
  14  }

E24 / BlackIris-main/src/KernelMode/Keylogger.c

Строки 6–37 · Чтение EFI console input; не Windows keyboard stack.

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

SHA-256 e56871a4a89cca497f8a69b546c6d03e0f5433823f834e61da5e51f1a200a4ac

E27 / BlackIris-main/src/Boot/PatchSMI.c

Строки 11–46 · Поиск условного маркера памяти не показывает преодоление аппаратной защиты SMRAM.

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

SHA-256 fa6ab793cb394c08e847fcab2d3471366c4547f2ba1723ac363c338877f55265

E26 / BlackIris-main/src/Encryptor/CryptoDefs.h

Строки 42–72 · Комментарий прямо отрицает полноценную реализацию AES; ниже собственное преобразование.

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

SHA-256 221627915332bc03dd0230fe2e4633b7aff575701f63cf43b4d2554fd26bc725

Посмотреть фрагмент исходника · строки 42–45
  42  
  43  //
  44  // Inline functions for encrypting and decrypting fcking blocks
  45  // It does not implement full real AES by size, but simulates a complex XOR + SBOX.
← Предыдущий материалBenthic: шесть способов исказить картину WindowsСледующий материал →bootdoor: передача управления через границы загрузки