- PVSM.RU - https://www.pvsm.ru -
Продолжаем разговор о форматах NVRAM в UEFI-совместимых прошивках, начатый в первой части [1]. На этот раз на повестке дня формат блоков Fsys из прошивок компании Apple, FTW из прошивок, следующих заветам проекта TianoCore и FDC, который можно найти в прошивках, основанных на кодовой базе компании Insyde.
Если вам интересно, зачем нужны и как выглядят не-NVRAM данные, которые можно обнаружить рядом с NVRAM в прошивках различных производителей — добро пожаловать под кат.
Как всегда, автор не несет ответственности ни за что, кроме возможных очепяток, все баги автоматически признаются фичами, вы используете полученные сведения на свой страх и риск, короче, вы уже и так в курсе. Если вам все еще непонятно, что в этой статье происходит, куда бежать, что делать, и кто виноват — почитайте тут [2] и здесь [3], и возвращайтесь. Вернулись или не уходили? Отлично, можно продолжать.
Начнем с формата блока Fsys, в котором Apple хранит настройки для конкретной модели железа. Настройки эти затем при помощи специального DXE-драйвера превращаются в данные SMBIOS (те самые, которые из ОС можно прочитать утилитой dmidecode).
Формат, понятное дело, специфичен для прошивок компании Apple, и «был всегда», т.е. встречается как в самых ранних, так и в самых новых прошивках. Блок данных в этом формате обычно находится сразу за первыми двумя хранилищами VSS (основным и резервным), и, по идее, не должен изменяться пользователем, а данные из него не доступны через UEFI runtime-сервисы, поэтому я и не считаю их NVRAM'ом, но если уж им (не) повезло лежать с NVRAM в одном томе — пришлось разобраться и с ними, тем более, что формат оказался тривиальным, и его можно почти весь показать на одном скриншоте без всяких C-структур. Заголовок блока и переменные выглядят вот так:
Начинается блок с четырехбайтовой сигнатуры, обычно это Fsys (на относительно старых машинах был еще второй блок того же формата с сигнатурой Gaid, на более современных все кладут в один блок Fsys). За сигнатурой следуют 5 неизвестных байт, во всех дампах, что есть у меня, они равны 0x01 0x0E 0x00 0x00 0x00, но у вас они, понятно, могут отличаться. За ними следует двухбайтовый общий размер блока, сразу за которым начинаются переменные, без всякого выравнивания и с максимальной упаковкой. Переменная (лучше назвать эту сущность «записью», т.к. изменять эти данные Apple конечному пользователю не разрешает) хранится так: однобайтовая длина имени, имя в кодировке ASCII, двухбайтовая длина данных, и сами данные. Получается, что на скриншоте видны, кроме заголовка, 3 с половиной записи — dckt, dckh, dck_ и overrides.
Обратите внимание на начало данных последней — сигнатура BZ, h — указание на использование кода Хаффмана, 1 — указание на размер блока, а затем и вообще закодированное в BCD число Пи… Ба, старый знакомый, формат Bzip2 [4]! Достаем, распаковываем, и получаем вот такое:
Записи в блоке следуют друг за другом, пока не встречается запись с говорящим именем EOF, после которой до самого конца блока следуют нули, а в последних четырех байтах записана контрольная сумма CRC32 всего содержимого блока, кроме тех самых последних четырех байт. Apple вообще очень любит CRC32, и считают они её буквально для всего — для записей Fsys, для переменных VSS NVRAM, для исполняемых файлов EFI, для томов и для всего образа целиком тоже. Целостности богу целостности, контроля к трону контроля!
Если разбирать вручную нет настроения, на помощь снова приходит UEFITool NE [5], в котором блок Fsys со скришнота выше выглядит вот так:
Следующий блок в нашем списке на препарирование — код проекта TianoCore [6]. Впрочем, теория теорией, а на практике вместо одного красивого и приятного заголовка в прошивках внезапно встречается два почти одинаковых, вот такой:
struct EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32 {
EFI_GUID Signature; // EFI_SYSTEM_NV_DATA_FV_GUID
UINT32 Crc; // CRC32 от заголовка с пустыми полями Crc и State.
// Значение пустого байта определяется битом ErasePolarity тома NVRAM
UINT8 State; // Состояние блока, валидный (0xFE или 0x01, в зависимости от ErasePolarity) или нет (остальные значения)
UINT8 Reserved[3]; // Зарезервированное поле
UINT32 WriteQueueSize; // Размер данных блока, внезапно UINT32
//UINT8 WriteQueue[WriteQueueSize]; // Данные
};
И такой:
struct EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64 {
EFI_GUID Signature; // EFI_SYSTEM_NV_DATA_FV_GUID или EDKII_WORKING_BLOCK_SIGNATURE_GUID
UINT32 Crc; // ~~~
UINT8 State; // ~~~
UINT8 Reserved[3]; // ~~~
UINT64 WriteQueueSize; // Нормальный UINT64, как написано в спецификации
//UINT8 WriteQueue[WriteQueueSize]; // ~~~
};
Такое неожиданное разнообразие создает определенные трудности при попытке угадать, какой именно вариант структуры перед нами. К счастью, чаще всего суммарный размер блока FTW кратен 16 байтам, и потому достаточно проверить значение WriteQueueSize на делимость нацело на 16, если делится — перед нами второй вариант, если в остатке 4 — первый, если в остатке что-то другое — мы нашли еще один вариант этой структуры, ура.
На скриншоте я покажу только второй тип заголовка, т.к. первый встречается лишь в некоторых старых прошивках времен царя Гороха:
Все по спецификации, GUID — FFF12B8D-7696-4C8B-A985-2747075B4F50, CRC32 — 0xB0458FB9, состояние блока — валидный, размер данных — 0xFE0, что отлично делится на 16, поэтому последние 4 байта заголовка — все-таки еще заголовок, а не уже кусок данных.
В UEFITool NE тот же самый блок выглядит вот так:
После того, как UEFI Forum решил хранить ключи для SecureBoot в NVRAM, понадобилось не только серьезно переделать формат VSS (о котором я рассказывал в первой части [3]), но и решать вопрос с хранением «умолчаний» для этих переменных, причем вендорам опять позволили решать его самостоятельно. Одно из таких решений от компании Insyde, а именно блок FDC, мы сейчас и разберем.
Формат там очень простой, но мне совершенно не ясно, чем руководствовался его разработчик. Заголовок блока вот такой:
struct FDC_VOLUME_HEADER {
UINT32 Signature; // Сигнатура _FDC
UINT32 Size; // Полный размер блока вместе с заголовком
//EFI_FIRMWARE_VOLUME_HEADER VolumeHeader; // Заголовок NVRAM-тома, зачем он тут - совершенно непонятно
//VSS_VARIABLE_STORE_HEADER VssHeader; // Заголовок хранилища VSS, тоже нужен как собаке пятое колесо
// Еще и размер в нем указан неверный, чаще всего
};
На скриншоте весь этот кошмар выглядит вот так:
Итого: сигнатура — _FDC, общий размер блока — 0x4000, заголовок NVRAM-тома, из которого не используется вообще ничего, сигнатура хранилища VSS с незаполненным размером в отформатированном и здоровом состоянии, и область с переменными. Получается что целых 88 байт потрачено на заголовки, которые вообще ни для чего не нужны, мой внутренний оптимизатор негодует.
В UEFITool NE я решил не выводить все эти ненужные заголовки вообще, и потому тот же блок FDC в нем выглядит вот так:
Ну вот, определились и с форматом всяких странных блоков, хранящихся посреди тома NVRAM, на сладкое остались EVSA и NVAR, о которых поговорим в третьей части. Спасибо за внимание.
Автор: CodeRush
Источник [7]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/formaty-danny-h/117677
Ссылки в тексте:
[1] в первой части: https://habrahabr.ru/post/281242
[2] тут: https://habrahabr.ru/post/267491/
[3] здесь: https://habrahabr.ru/post/281242/
[4] формат Bzip2: https://en.wikipedia.org/wiki/Bzip2#File_format
[5] UEFITool NE: https://github.com/LongSoft/UEFITool/tree/new_engine
[6] код проекта TianoCore: https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Include/Guid/SystemNvDataGuid.h
[7] Источник: https://habrahabr.ru/post/281412/
Нажмите здесь для печати.