
Процедура обновления любого ПО для меня всегда была загадкой. Ты нажимаешь кнопку «Обновить», и за кулисами начинает происходить какое‑то таинство с кучей индикаторов
и диагностических сообщений на экране монитора. Что уж говорить про системное ПО, такое как BIOS, затрагивающее самую суть — компьютерное железо… Мне, как исследователю из команды Raccoon Security, всегда хотелось узнать, как изнутри обновляется BIOS с учётом всех защит, которые её окружают. Разбираться будем вместе, а для примера возьмём самую распространённую UEFI BIOS H2O фирмы Insyde Software и её сервисную утилиту для обновления BIOS — H2OFFT.
В статье вы узнаете:
о способах защиты флеш‑памяти UEFI BIOS от записи;
особенностях записи во флеш‑память при обновлении UEFI BIOS;
интерфейсе взаимодействия между программой H2OFFT и драйвером, драйвером программы H2OFFT и SMM.
Немного теории
BIOS (Basic Input/Output System, Базовая Система Ввода/Вывода) — набор микропрограмм, реализующих низкоуровневый API для работы с аппаратным обеспечением компьютера, а также создающих необходимую программную среду для запуска операционной системы. На протяжении многих лет в персональных компьютерах использовалась традиционная BIOS — Legacy. В начале 2000-x появился новый стандарт BIOS — UEFI (Unified Extensible Firmware Interface). Это всё тот же BIOS, но нового образца, расширяемый, с улучшенными функциями безопасности и лишённый ряда архитектурных недостатков.
Код UEFI, как и для Legacy BIOS, хранится в микросхеме энергонезависимой (флеш) памяти, установленной на материнской плате компьютера. На фотографии ниже (см. рисунок 1) представлен фрагмент материнской платы, в центре — корпус микросхемы флеш‑памяти MX25L12872F, в которой хранится UEFI BIOS.

UEFI BIOS получает управление после включения компьютера и работает до старта операционной системы (ОС). Если злоумышленнику удастся внедрить в UEFI BIOS вредоносный код, то он сможет отключить средства защиты ОС и развёртывать свои компоненты в режиме ядра (Kernel Mode) или режиме пользователя (User Mode) на начальных этапах загрузки.
Вредоносное ПО (ВПО), которое внедряется в прошивку, называется «UEFI‑буткит».
Это ВПО представляет большую опасность для пользователей ОС: UEFI‑буткит полностью контролирует процесс загрузки ОС и его трудно обнаружить. Переустановка ОС не поможет избавиться от UEFI‑буткита.
Известны примеры успешных атак с использованием UEFI‑буткитов. В 2018 году был обнаружен первый буткит — LoJax. Он модифицировал прошивку, размещённую в микросхеме флеш‑памяти с интерфейсом SPI, и после успешного закрепления в системе запускал вредоносные модули в среде ОС. Уже тогда всем стало ясно, что UEFI‑буткиты представляют собой реальную угрозу и носят не только теоретический характер.
Для исследования процесса обновления я выбирал BIOS среди трёх основных компаний‑разработчиков UEFI BIOS (см. рисунок 2):

В итоге я остановил свой выбор на программном обеспечении H2OFFT (Flash Firmware Tool) фирмы Insyde Software, предназначенном для обновления UEFI BIOS на компьютерах с процессорами фирмы Intel. В основе работы программы H2OFFT используется программный интерфейс Insyde H2O internal Soft‑SMI interface (IHISI), разработанный компанией Insyde Software. Это запатентованная технология взаимодействия прикладного программного обеспечения фирмы Insyde Software и кода режима SMM через SMI.
Технологии защиты флеш-памяти BIOS от модификации
Теперь перейдём к основным механизмам защиты флеш‑памяти BIOS от записи (согласно терминологии Intel, см. рисунок 3):
Global Flash Write Protection (Write Protect);
BIOS Range Write Protection.

Ещё есть защита на основе Flash Descriptors, но в контексте этой статьи её описание не привожу.
Global Flash Write Protection (Write Protect)
Этот вид защиты обеспечивается на уровне чипсета компьютера и применяется ко всей микросхеме флеш‑памяти. В механизме задействован режим SMM, в контексте которого принимается решение о статусе защиты, и специальные регистры чипсета, относящиеся к LPC‑контроллеру (Low Pin Count), через который подключается микросхема.
В контексте защиты от записи интерес представляет регистр BIOS_CNTL, доступный через конфигурационное пространство PCI LPC‑контроллера (Device 31: Function 0, offset 0xDC), см. рисунок 4:

и следующие его биты (см. рисунок 5):
Бит BIOS_CNTL.BLE (BIOS Lock Enable, BLE). Если бит равен 1, то блокируется изменение бита SMM_BWP (см. ниже) и разблокирование защиты через BIOSWE (перевод из 0 в 1) активирует SMI, обработчик которого определяет легитимность данной операции и итоговый статус защиты. Этот бит можно снять только при сбросе системы (сигнал PLTRST#, Platform Reset). Реализация обработчика SMI выполняется производителями оборудования (OEM).
Бит BIOS_CNTL.BIOSWE (BIOS Write Enable, BIOSWE). Определяет статус защиты микросхемы от записи: 1 — запись разрешена, 0 — доступно только чтение. Изменения контролируются кодом SMM, если установлен бит BLE (см. выше).
Бит BIOS_CNTL.SMM_BWP (SMM BIOS Write Protect Disable, SMM_BWP). Определяет статус защиты микросхемы от записи за пределами SMM BIOS. Если установлено значение 1, то для записи в BIOS все ядра должны находиться в режиме SMM. Если 0, такие условия не выставляются.
Анализ документации на чипсеты Intel показал (см. рисунок 6), что аналогичный набор битов доступен через регистр BIOS_SPI_BC контроллера SPI (Device 31: Function 5, offset 0xDC):

BIOS Range Write Protection
BIOS Range Write Protection (защита диапазонов от записи) позволяет организовать (например, производителю оборудования) защиту от модификации отдельных диапазонов (регионов) микросхемы флеш‑памяти. Защита реализуется на уровне контроллера SPI из состава чипсета, отвечающего за операции доступа к содержимому микросхемы через одноимённый интерфейс, и действует даже на уровне SMM. Контроллер позволяет определить до пяти защищаемых регионов, для каждого из которых в выделенном ему адресном диапазоне, начиная со смещения SPIBAR+0×74, имеется отдельный рабочий регистр PR0−PR4, см. рисунок 7.

В каждом PR‑регистре есть поля, определяющие защищённый диапазон (если упростить, поле PRB — начало, а поле PRL — конец), а также бит WPE, определяющий, включена ли защита от записи (см. рисунок 8).
В контексте защиты диапазонов BIOS также можно рассмотреть регистр Hardware Sequencing Flash Status (HSFS) контроллера SPI (SPIBAR + 0×04) и его бит Flash Configuration Lock‑Down (FLOCKDN). Его установка блокирует дальнейшие изменения определённых регистров SPI‑контроллера, включая PR‑регистры. Если установлено значение 1, регистры PR0−PR4 не могут быть перезаписаны в runtime и значение больше не может быть изменено. Перезапись бита FLOCKDN возможна только после аппаратного сброса. Таким образом, обновление диапазонов BIOS, защищаемых PR‑регистрами, должно осуществляться до установки бита FLOCKDN.
С учётом вышесказанного можно предположить, как выглядит псевдокод для снятия упомянутых защит. В представленных выкладках учтено, что базовый адрес рабочих регистров SPI‑контроллера (SPIBar) извлекается из его конфигурационного регистра PCI BAR0 по смещению 0×10. Функционировать этот код должен также в контексте режима SMM.
Зануление PR-регистров (снятие защиты диапазонов)
Чтобы иметь доступ к записи в защищённый диапазон, который контролируется PR‑регистрами, их нужно занулить:
Int Register = 0x10; //BAR0 Int Function = 5; Int Device = 31; Int Bus = 0; // Получение SPIBAR. UINT32 SpiBar = PciRead32 ( (Register & 0xfff) | ((Function & 0x07) << 12) | ((Device & 0x1f) << 15) | ((Bus & 0xff) << 20) ); // SpiBar — физический адрес MMIO-области SPI-контроллера (SPIBAR). В SMM/DXE этот физический адрес можно использовать как системный адрес. SpiBaseAddress = SpiBar;
Для зануления PR0−PR4:
#define PR0 0x74 // Protected Register 0 for (int i = 0; i < 5; ++i) { *(SpiBaseAddress + PR0 + (i << 2) = 0;
Разблокировка записи через LPC-контроллер (установка бита BIOSWE)
Для разблокировки записи в UEFI BIOS нужно установить бит BIOSWE в значение 1:
Int Register = 0xDC; Int Function = 0; Int Device = 31; Int Bus = 0; mLpcBiosCntlRegAddr = (Register & 0xfff) | ((Function & 0x07) << 12) | ((Device & 0x1f) << 15) | ((Bus & 0xff) << 20); Value = PciRead8 (mLpcBiosCntlRegAddr); Value |= 0x01; PciWrite8 (mLpcBiosCntlRegAddr, Value);
Итак, резюмируем результаты проведённого анализа.
Чтобы снять защиту флеш‑памяти UEFI BIOS от записи, нужно установить SMM_BWP=0 (запись доступна не только в режиме SMM), BIOSWE=1, SPI (PRx = 0), WPE=0, FLOCKDN=0.
Чтобы сбросить регистры PR0−PR4, следует получить базовый адрес из конфигурационного пространства (регистра BAR) SPI‑контроллера и обратиться по нужным смещениям, занулив их. Типовой адрес SPI‑контроллера на шине PCI — Bus 0: Device 31, Function 5.
Процесс обновления
Компания Insyde Software обеспечивает на рынке широкую номенклатуру ПО (см. рисунок 9), связанного с BIOS и ориентированного как на разработчиков и тестеров, так и на конечных пользователей.

На официальном сайте компании можно выбрать модель своего ноутбука и скачать программу H2OFFT‑W, предназначенную для конфигурации, проверки и обновления BIOS. Её я и решил изучить... Сразу приведу общую блок‑схему, иллюстрирующую работу программы в комплексе, чтобы удобнее было следить за ходом изложения (см. рисунок 10).

Цикл работы программы можно условно разбить на следующие этапы:
Запуск программы H2OFFT‑W.exe, чтение конфигурации из файла platform.ini. На этом этапе внутренний архив программы распаковывается и появляются новые компоненты (об этом речь пойдёт ниже).
Установка драйвера H2OFFT32.sys или H2OFFT64.sys (в зависимости от разрядности системы).
Копирование образа прошивки WinJUCN59WW.fd в системный EFI‑раздел ПК.
Конфигурация, настройка и проверка системы программой H2OFFT‑W.exe в соответствии с конфигурационным файлом platform.ini.
Обращение через IOCTL программы H2OFFT‑W.exe к драйверу. На этом этапе выполняется вызов IHISI, передача конфигурации (вызов IHISI_1Dh) и передача управления файлу isflash.bin после перезагрузки системы.
Автоматическая перезагрузка системы.
Запуск файла isflash.bin. Распаковка контейнера с прошивкой (Capsule), вызов IHISI_15h для обновления прошивки.
Теперь рассмотрим каждый этап подробнее.
Запуск и распаковка
После запуска программа H2OFFT‑W.exe распаковывает свои компоненты во временный каталог C:\Windows\Temp\7zS43D8.tmp. Его содержимое представлено на рисунке 11:

Установка драйвера
Далее запускается программа WDFInst.exe, которая устанавливает драйвер H2OFFT32.sys или H2OFFT64.sys (в зависимости от разрядности ОС). Основная программа H20FFT‑W.exe и драйвер взаимодействуют через IOCTL (функция DeviceIoControl). Если перейти по перекрёстным ссылкам вызовов, то можно найти IOCTL‑коды драйвера.
Держа в уме известные мне механизмы защиты микросхемы флеш‑памяти, первым делом решил поискать функции взаимодействия с шиной PCI. В программе нашёл вот такую функцию (см. рисунок 12):

А вот как драйвер обрабатывает запрос с соответствующим IOCTL‑кодом — 0×222290
(см. рисунок 13):

Вызываемая функция ReadPci32 (см. рисунок 14) манипулирует портами ввода/вывода 0xCF8 и 0xCFC (в x86-архитектуре отвечают за доступ к конфигурационному пространству PCI‑устройств), «склеивая» адрес конфигурационного регистра из шинного адреса ц��левого устройства (bus/device/func) и смещения (offset):

На первый взгляд, конкретных обращений к конфигурационным регистрам SPI‑контроллера нет, поэтому я решил двигаться дальше.
Посмотрев на драйвер, обнаружил в нём следы ранее упомянутого интерфейса IHISI. Ниже
(см. рисунок 15) представлены возможные команды. Их назначение пока ещё оставалось для меня загадкой, но это только пока.

Загрузка файла прошивки
Среди распакованных файлов программы (см. рисунок 11) я обнаружил файл WinJUCN59WW.fd. Это и есть файл прошивки. Программа переименовывает его в isflash.bin и сохраняет в системный EFI‑раздел (FS0:\EFI\Insyde) (см. рисунок 16):

Файл прошивки состоит из различных компонентов, в том числе SMM‑модулей, DXE‑драйверов (подробнее обо всех компонентах рассказано в этой статье). Для каждого модуля определена своя очерёдность загрузки и привилегии при работе, некоторые привилегированные операции могут быть выполнены только из SMM‑режима. Сам файл прошивки — UEFI Capsule, специальный контейнер, в котором упакована обновлённая прошивка.
Обработка конфигурации
Параметры конфигурации программа читает из файла platform.ini (также находится среди распакованных файлов, см. рисунок 11). Этот файл содержит подробные комментарии, а конфигурационные поля могут быть изменены. Например, можно указать, каким образом будет передан capsul. Также параметры по умолчанию хранятся в теле программы и в случае отсутствия файла конфигурации будут прочитаны оттуда.
Во временном каталоге программы (см. рисунок 11) есть EFI‑приложение InterToolx64.efi для расширения возможностей обновления. Оно запускается только при наличии специфических параметров в файле конфигурации.
После извлечения конфигурации из файла platform.ini и выполнения всех проверок компьютер перезагружается. В ходе перезагрузки отключается защита микросхемы флеш‑памяти BIOS (см. раздел про защиты выше) и управление передаётся файлу isflash.bin, ранее размещённому в EFI‑разделе накопителя компьютера.
Передача управления
Один из способов передачи управления на нужный файл после перезагрузки компьютера — настройка данных конфигурации загрузки (Boot Configuration Data, BCD) с помощью утилиты bcdedit ОС Windows (пример добавления нашего файла isflash.bin в загрузку — bcdedit /set '{bootmgr}' path \EFI\Insyde\isflash.bin). И, как я понял, в изучаемой программе (H2OFFT) такой механизм предусмотрен (есть поддержка в коде).
В нашем случае передача управления прошивке реализуется через IHISI и SMI (команда IHISI_1Dh). Конфигурационные параметры обновления BIOS передаются в соответствующую функцию (smiToBuffer, см. рисунок 17) через буфер.

Рассмотрим функцию smiToBuffer. IOCTL‑вызов (код 0×2223С0) внутри функции, соответствующий передаче буфера с параметрами, показан на рисунке 18.

А вот как эта операция выглядит со стороны драйвера (см. рисунок 19).

Теперь про функцию Ihishi_1Dh. В ней вызывается функция TriggerAuthSmi (см. рисунок 20), фактически являющаяся обёрткой для функции TriggerSmi (см. рисунок 21), в которой, в свою очередь, осуществляется IOCTL‑вызов с кодом 0×2223FC для генерации SMI.


А вот как эта операция выглядит со стороны драйвера (см. рисунок 22):

В драйвере вызывается одноимённая функция, которая записью в порт ввода/вывода активирует SMI (см. рисунок 23).

При генерации SMI компьютер перезагружается, и после запуска EFI‑приложения isflash.bin из EFI‑раздела (см. выше) управление передаётся на содержащийся в нём по определённому смещению Capsule, реализующий логику обновления.
В ходе выполнения приложение регистрирует несколько фирменных EFI‑протоколов Insyde, например протокол H2OCapsuleUpdateProgressProtocol {142B9A2A-2E71-486F-80BD-5B7BDAC1DEEC}, реализующий отображение прогресс‑индикатора обновления. На рисунках 24−26 для него представлены: GUID, процедура регистрации и использование внешнего протокола отображения графической информации EfiGraphicsOutputProtocol:



Обновление BIOS
Все обновления «живут» в Capsule, а сама флеш‑память BIOS обновляется в функции UpdateRomFlow_Main (её фрагмент — на рисунке 27) и реализуется в функции flasher.

EC (Embedded Controller) также обновляется через flasher. Фрагмент соответствующей функции UpdateExternalEcMain — на рисунке 28:

В ходе обновления EC проверяется некий флаг защиты, выставляемый по ходу инициализации блоков памяти после получения информации о флеш‑памяти (функция IHISI_13h, Flash part information), ну и, конечно, обновляется прогресс‑индикатор (функция BaseUpdateTotal_4E0B0 на рисунке 28) с использованием фирменного EFI‑протокола H2OCapsuleUpdateProgressProtocol (см. рисунки 24−26).
При обновлении BIOS проверка этого бита отсутствует, он проверяется при обновлении EC. Функция имеет название «ProtectAllBlock», и, исходя из логов, бит выставляется следующим образом (см. рисунок 29):

Запись во флеш‑память в упомянутой выше функции flasher реализуется также через IHISI
и SMI (функция Ihisi_15h), см. рисунок 30:

Формат вызова функции Ihisi_15h аналогичен формату, представленному на рисунках 20−23.
Вызов SMI аналогичен вызову в драйвере через порт ввода/вывода (см. рисунок 24).
Обработка SMI
В предыдущих разделах я нашёл код, активирующий SMI для выполнения различных операций. И, конечно же, я захотел узнать, а как, собственно, выглядят его обработчики, выполняющие всю основную работу.
Препарировав прошивку с использованием знаменитой UEFITool, я обнаружил в ней два интересных SMM‑модуля, связанных с IHISI (см. рисунок 31):
IhisiServicesSmm;
IhisiSmm.

Я уже писал выше, что за запись обновлённой прошивки во флеш‑память отвечает функция Ihisi_15h приложения, задействующая SMI (см. рисунок 30). Соответствующий обработчик, как и другие обработчики, я обнаружил в модуле IhisiServicesSmm. Модуль IhisiSmm не содержал обработчиков.
Фрагмент списка имён обработчиков (команд ISIHI) представлен на рисунке 32, примеры структур, содержащих их адреса, в том числе для Ihisi_15h, — на рисунке 33.


Перехожу по адресу и нахожу реализацию обработчика нашей Ihisi_15h. Всё как положено: цикл по блокам, сначала стирание блока, потом его запись (см. рисунок 34).

Операции с флеш‑памятью реализуются посредством EFI‑протокола EFI_SMM_FW_BLOCK_SERVICE_PROTOCOL_GUID с GUID, равным {0×2970687c, 0×618c, 0×4de5, 0xb8, 0xf9, 0×6c, 0×75, 0×76, 0xdc, 0xa8, 0×3d}.
Взаимодействие с защитой флеш-памяти
В DXE‑модуле BiosRegionLockEntry прошивки я обнаружил регистрацию протокола с GUID, равным {E687E50B‑C98D-4780-A7B0-E4C7C4AF7F69}, и следующей реализацией
(см. рисунок 35):

Указанный протокол и, в частности, его функции SetRegionByAddress и LOCK задействуются в DXE‑модуле с говорящим именем BiosProtectDxe.
Заключение
Ну вот и закончилось моё погружение в мир прошивки BIOS. Я ещё раз убедился, что это сложный и многоэтапный процесс, включающий распаковку файлов программы, установку драйверов, настройку конфигурации, передачу управления «прошивальщику», загрузку и запись обновлённого кода в микросхему флеш‑памяти. И всё это «размазано» по нескольким программным средам/режимам: прикладной и ядерный режимы в ОС, UEFI, SMM. Интереснее всего было взглянуть изнутри на протокол взаимодействия прикладного ПО, специализированных драйверов и кода SMM, в котором и происходит основная «магия» — прошивка микросхемы флеш‑памяти. Заглянув в мир возможностей UEFI, я ещё раз убедился в важности защиты содержимого микросхемы BIOS от модификаций.
