«Я вчера закончил ковку,
Я два плана залудил…»
… V.S. Vysotsky song...

Уже почти 3 года назад (в начале 2016) в issue проекта UEFITool на GitHub появилось пожелание пользователей: построить «Dependency Graph» для исполняемых модулей, входящих в BIOS/UEFI.

Завязалось даже небольшое обсуждение, в результате которого окончательно выяснилось, что данная задача отнюдь не тривиальна, имеющегося функционала для её решения недостаточно, перспективы в тот момент туманны…

И остался этот вопрос в подвешенном состоянии, с перспективой реализации в неопределённом будущем (но желание, наверное, осталось, а надежда, как известно, умирает последней!).

Есть предложение: отыскать, наконец, решение этой проблемы!

Определяемся с терминами


В дальнейшем предполагается, что мы имеем дело с Intel 64 и IA-32 Architecture.

Для того, чтоб однозначно определиться, что ж мы всё-таки решили построить, нам придётся более детально разобраться с функционированием отдельных фаз работы BIOS/UEFI.

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

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

+--------------------------------------------------------------------------+
|  File Types Information                                                  |
+--------------------------------------------------------------------------+
|    EFI_FV_FILETYPE_RAW                     =    6                        |
|    EFI_FV_FILETYPE_FREEFORM                =   83                        |
|    EFI_FV_FILETYPE_SECURITY_CORE           =    1                        |
|    EFI_FV_FILETYPE_PEI_CORE                =    1                        |
|    EFI_FV_FILETYPE_DXE_CORE                =    1                        |
|    EFI_FV_FILETYPE_PEIM                    =   57                        |
|    EFI_FV_FILETYPE_DRIVER                  =  196                        |
|    EFI_FV_FILETYPE_APPLICATION             =    1                        |
|    EFI_FV_FILETYPE_SMM                     =   60                        |
|    EFI_FV_FILETYPE_SMM_CORE                =    1                        |
|    EFI_FV_FILETYPE_PAD                     =    4                        |
+--------------------------------------------------------------------------+
|  Total Files :                             =  411                        |
+--------------------------------------------------------------------------+
Пример состава какой-то обычной (рядовой) прошивки.

Хотя в этой таблице и не помечены файлы, имеющие в своём составе исполняемые модули, тем не менее, это будут (по определению) все в данном списке, кроме файлов с суффиксами RAW, FREEFORM и PAD.

Файлы с суффиксом «CORE» (SECURITY_CORE, PEI_CORE и DXE_CORE) – это соответствующие «ядра» (головные модули соответствующей фазы), получающие управление из других фаз (или после старта), SMM_CORE является суб-фазой DXE-фазы и вызывается во время её выполнения. APPLICATION может выполняться только по запросу пользователя, конкретной привязки к фазам не имеет.

Остались не перечисленными самые распространённые типы файлов: PEIM (модули PEI-фазы), DRIVER (модули DXE-фазы) и SMM (модули суб-фазы DXE). В состав CORE-модулей фаз PEI и DXE входит диспетчер, который и управляет последовательностью загрузки/запуска модулей соответствующей фазы.

В приведённом выше примере отсутствуют комбинированные варианты, мы о них и не будем вспоминать: в реальных прошивках они хоть и встречаются, но достаточно редко. Желающим получить более детальную и подробную информацию предлагаем обратиться к статьям CodeRush 1, 2, 3. А также процитируем его совет: «Для фанатов оригинальной документации всегда в наличии спецификация UEFI PI, там все расписано намного подробнее.»

Каждый исполняемый модуль прошивки является модулем формата PE+ (Portable Executable) или производным от него (Terse Executable:TE-формат). Исполняемый модуль PE+ формата представляет собой набор «слегка» упакованных структурированных данных, содержащих информацию, необходимую загрузчику для отображения данного модуля в память.

Сам формат (структура) PE+ не располагает каким-либо механизмом взаимодействия между отдельными PE+ модулями. Каждый исполняемый модуль после загрузки и начала выполнения представляет собой автономный независимый процесс, (ну должно быть так!), т.е. модуль не должен ничего «предполагать» о том, что делается вне его.

Организация взаимодействия между отдельными исполняемыми модулями одной UEFI фазы организуется средствами «CORE»-модуля соответствующей фазы. Отдельные исполняемые модули могут определять (Install) протоколы, запрашивать (Locate) и использовать протоколы, объявленные другими модулями, устанавливать/объявлять события, объявлять (Notify) обработчики событий.

Таким образом, для каждого исполняемого модуля прошивки нас интересует наличие следующих артефактов:

  1. Список протоколов, которые данный модуль определяет. (Каждый протокол идентифицируется уникальным номером – guid).
  2. Список протоколов, которые данный модуль использует (пытается использовать).
  3. Список событий, которые данный модуль объявляет. (Событие имеет уникальный номер – guid).
  4. Список обработчиков событий присутствующих (реализованы и могут быть установлены/инициализированы) в данном модуле.
Static Dependency Graph для заданной фазы BIOS/UEFI считается определённым, если для каждого исполняемого модуля фазы, нам известны все артефакты, перечисленные выше в п.п.1-4. (Другими словами, если у нас определена вся информация, описывающая взаимозависимости между модулями).
Мы будем рассматривать только вариант статического анализа, это означает, что некоторые элементы кода, реализующие п.п.1-4 могут быть недостижимы (являются фрагментами «dead» кода) или будут достижимы только при определённых вариантах входных данных/параметрах.

Всё, что мы рассматривали до настоящего времени, основывается только на спецификации BIOS/UEFI. А чтоб разобраться во «взаимоотношениях» имеющихся исполняемых модулей рассматриваемой прошивки, нам придётся несколько углубиться в их структуру, а значит, заняться хотя бы частичным их реверсом (восстановлением исходных алгоритмов).

Как уже было написано выше, исполняемый модуль PE+ формата – это всего-навсего набор структур для загрузчика, строящего в памяти объект, на который будет передаваться управление, и этот объект по своей природе состоит из команд процессора, а также данных для этих команд.
Будем говорить, что произведено полное дизассемблирование исполняемого модуля, если удалось решить задачу разделения команд и данных, представленных в данном модуле.
При этом мы не будем накладывать никаких требований на структуру и типы данных, достаточно, если для каждого байта, принадлежащего образу исполняемого модуля, полученному загрузчиком, мы можем однозначно сказать, к какой из двух категорий он принадлежит: байт команды или байт данных.

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

Предположим:

  1. Мы уже решили задачу полного дизассемблирования для конкретного исполнительного модуля BIOS/UEFI, т.е. мы сумели разделить команды и данные.
  2. Имеется исходный текст модуля на языке «C» (в нынешних BIOS/UEFI прошивках модули большей частью разработаны как раз на языке «C»).

Даже в этом случае просто сопоставление полученных результатов (ассемблерный текст – это просто текстовое представление команд процессора) с исходными текстами на языке «C» потребует почти всегда неслабого опыта/квалификации, исключением будут только абсолютно вырожденные случаи.

Полное изучение примеров, показывающих трудности идентификации или сопоставления результатов дизассемблирования с исходным кодом не входит в наши текущие планы.
Рассмотрим лишь пример, когда в полученном ассемблерном листинге мы встречаем «Indirect Call» команду – неявный вызов процедуры.

Это пример вызова процедуры, ссылка на которую задаётся в таблице. Таблица, содержащая ссылки на различные процедуры, – типичный случай реализации представления интерфейсов произвольного протокола.

Подобная таблица не обязательно должна состоять только из ссылок на процедуры, никто не запрещает хранить в этой структуре (а это пример типичной «C»-шной структуры) просто произвольные данные.

Вот одна из форм такого вызова (вместо регистра “ecx” возможны почти все варианты 32-х разрядных регистров процессора):
FF 51 18 call dword ptr [ecx+18h]
Попав, в результате анализа, на подобную команду, разобраться, что за процедура вызывается, список её параметров, тип и значение возвращаемого результата, возможно только, если нам известен тип объекта (протокола), вызов интерфейса которого производится данной командой.

Если нам известно, что в предыдущем примере регистр “ecx” содержит указатель (адрес начала таблицы EFI_PEI_SERVICES), мы можем получить (представить) эту команду в следующем более понятном и «приятном» виде:
FF 51 18 call [eсx+EFI_PEI_SERVICES.InstallPpi]
Получение информации о содержимом регистра, участвующего в «Indirect Call» команде, чаще всего выходит за пределы возможностей «типичного» дизассемблера, задачей которого является просто анализ и преобразование двоичного (binary) кода процессора в человеко-читабельный вид – текстовое представление соответствующей команды процессора.

Для решения этой задачи часто требуется использовать дополнительную (Мета) информацию, которая отсутствует в двоичном исполняемом модуле (утеряна в результате компиляции и линковки – она используется при преобразованиях из одного представления алгоритма в другое, но уже не нужна процессору для выполнения полученных команд).

Если эти Метаданные всё-таки доступны нам из дополнительных источников, то, используя их и проводя дополнительный анализ, мы и получаем более понятное (и более точное) представление «Indirect Call» команды.

Вообще-то этот расширенный анализ уже больше напоминает процесс «декомпиляции», хоть результат и не выглядит, как исходный текст модуля на языке «C», тем не менее, в дальнейшем будем именовать сей процесс декомпиляцией команд, являющихся «Indirect Call» или «частичной» декомпиляцией.

Итак, мы уже готовы для определения достаточных условий построения так желаемого нами графа взаимозависимости исполняемых модулей прошивки для заданной фазы BIOS/UEFI:
Для получения Static Dependency Graph (любой из фаз – PEI или DXE) достаточно осуществить полное дизассемблирование всех исполняемых модулей соответствующей фазы (по крайней мере отделить все команды), и произвести декомпиляцию «Indirect Call» команд, присутствующих в дизассемблированных модулях.
Возникает сразу уйма вопросов по поводу того, каким образом связаны наши знания о «Indirect Call» командах с межмодульными взаимодействиями.
Как уже упоминалось выше, весь сервис по организации взаимодействия предоставляется «CORE»-модулем соответствующей фазы, а сервисы в фазах оформлены в виде «базовых» таблиц сервисов.

Так как модели взаимодействия модулей в PEI и DXE фазах хоть и идеологически (конструктивно) подобны, технически всё-таки отличаются, предлагается перейти от несколько формальных рассуждений к рассмотрению конкретного непосредственного построения Static Dependency Graph для PEI-фазы.

Нам даже удастся определить и сформулировать необходимые и достаточные условия возможности построения Static Dependency Graph для PEI-фазы.

Построение Static Dependency Graph для PEI-фазы


Описания решения задачи полного дизассемблирования исполняемых модулей PEI-фазы и декомпиляции «Indirect Call» команд, присутствующих в этих модулях, выходит за рамки нашего повествования и приводиться в нём не будет – само по себе изложение этого материала по объёму может превышать размеры данного опуса.

Вполне возможно, что со временем это произойдёт в виде отдельного материала, а пока – «know how».

Отметим только, что привлечение Метаданных, плюс наличие определённой структуры построения бинарного кода, позволяет на практике осуществлять полное дизассемблирование исполняемых модулей BIOS/UEFI. Формального доказательства данного факта не предполагается ни сейчас ни в будущем. По крайней мере при анализе/обработке более сотни (100) BIOS/UEFI различных производителей не встретилось примеров, где бы не удалось осуществить полное дизассемблирование .

Далее только конкретные результаты (с объяснениями: что, как и почем…).

Структура EFI_PEI_SERVICES – это базовая структура PEI-фазы, которая передается в качестве параметра в точку входа каждого PEI-модуля и содержит ссылки на базовые сервисы, необходимые PEI-модулям для функционирования.

Нас будут интересовать только поля, расположенные в самом начале структуры:



Фрагмент реальной структуры типа EFI_PEI_SERVICES в дизассемблере IDA Pro.

А вот так она представляется в исходном коде на языке «C» (напоминаю, это только фрагмент структуры):

struct EFI_PEI_SERVICES {
	EFI_TABLE_HEADER	Hdr;
	EFI_PEI_INSTALL_PPI	InstallPpi;
	EFI_PEI_REINSTALL_PPI	ReInstallPpi;
	EFI_PEI_LOCATE_PPI	LocatePpi;
	EFI_PEI_NOTIFY_PPI	NotifyPpi;
    //...Ещё много ссылок на различные базовые сервисы...
};

В начале структуры EFI_PEI_SERVICES, как и во всех «базовых» таблицах сервисов (Services Tables), находится EFI_TABLE_HEADER структура. Значения, представленные в этой заголовочной структуре, позволяют нам однозначно утверждать, что на фрагменте из дизассемблера реально присутствует если и не сама структура EFI_PEI_SERVICES (см. поле «Hdr.Signature»), то, по крайней мере, шаблон этой структуры!

struct EFI_TABLE_HEADER {
	UINT64	Signature;
	UINT32	Revision;
	UINT32	HeaderSize;
	UINT32	CRC32;
	UINT32	Reserved;
};

Попутно мы можем установить, что прошивка разрабатывалась в то время, когда версия UEFI PI спецификации была 1.2, период актуальности которой был с 2009 по 2013 год, ну а на текущий момент (начало 2019) актуальная версия спецификации уже доросла (буквально на днях подросла) до версии 1.7.

Из поля «Hdr.HeaderSize» можно определить, что полная длина структуры равна 78h (и это отнюдь не длина заголовка, как должно следовать из названия, а длина всей структуры EFI_PEI_SERVICES).

Интерфейсы EFI_PEI_SERVICES разделены на 7 категорий/классов. Мы их просто перечислим:

  1. PPI Services.
  2. Boot Mode Services.
  3. HOB Services.
  4. Firmware Volume Services.
  5. PEI Memory Services.
  6. Status Code Services.
  7. Reset Services.

Всё дальнейшее повествование будет непосредственно касаться процедур, относящихся к категории/классу «PPI Services», предназначенных для организации межмодульного взаимодействия исполняемых модулей PEI-фазы.

И их всего четыре для PEI-фазы.

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

Ниже приводятся прототипы этих процедур:

typedef EFI_STATUS (__cdecl *EFI_PEI_INSTALL_PPI)(
	const EFI_PEI_SERVICES		**PeiServices,
	const EFI_PEI_PPI_DESCRIPTOR	*PpiList);
typedef EFI_STATUS (__cdecl *EFI_PEI_REINSTALL_PPI)(
	const EFI_PEI_SERVICES		**PeiServices,
	const EFI_PEI_PPI_DESCRIPTOR	*OldPpi,
	const EFI_PEI_PPI_DESCRIPTOR	*NewPpi);
typedef EFI_STATUS (__cdecl *EFI_PEI_LOCATE_PPI)(
	const EFI_PEI_SERVICES		**PeiServices,
	const EFI_GUID			*Guid,
	UINTN				Instance,
	EFI_PEI_PPI_DESCRIPTOR 		**PpiDescriptor,
	void				**Ppi);
typedef EFI_STATUS (__cdecl *EFI_PEI_NOTIFY_PPI)(
	const EFI_PEI_SERVICES		**PeiServices,
	const EFI_PEI_NOTIFY_DESCRIPTOR	*NotifyList);

Отметим только, что кроме «Indirect Call» команд, вызывающих процедуры/интерфейсы класса «PPI Services» возможен явный (непосредственный – не табличный) вызов этих процедур, что иногда случается в исполнительных модулях, где производится определение/создание структуры EFI_PEI_SERVICES.

Открою один небольшой секрет: как ни странно, хоть это и «базовая» таблица сервисов PEI-фазы, тем не менее, как показывает практика, она может определяться не только в PEI_CORE модуле.

В реальной природе существуют прошивки, в которых структура EFI_PEI_SERVICES определялась/формировалась и использовалась в нескольких модулях, и это были отнюдь не копии PEI_CORE модуля.

Таким образом, возможны следующие варианты кода:

seg000:00785F0D B8 8C A6 78+	mov     eax, offset ppiList_78A68C
seg000:00785F12 50		push    eax			   ; PpiList
seg000:00785F13 57		push    edi			   ; PeiServices
seg000:00785F14 89 86 40 0E+	mov     [esi+0E40h], eax
seg000:00785F1A E8 70 FC FF+	call    InstallPpi

Пример явного вызова процедуры «InstallPpi».

seg000:00787CBB 8B 4D FC	mov     ecx, [ebp+PeiServices]
seg000:00787CBE 50		push    eax			   ; PpiList
seg000:00787CBF C7 00 10 00+	mov     dword ptr [eax], 80000010h
seg000:00787CC5 C7 43 3C A8+	mov     dword ptr [ebx+3Ch], offset guid_78A9A8
seg000:00787CCC 8B 11		mov     edx, [ecx]
seg000:00787CCE 51		push    ecx			   ; PeiServices
seg000:00787CCF FF 52 18	call    [edx+EFI_PEI_SERVICES.InstallPpi]

Пример неявного вызова интерфейса «InstallPpi».

FF 51 18  call    dword ptr [ecx+18h]
FF 51 18  call    [eсx+EFI_PEI_SERVICES.InstallPpi]

FF 51 1С  call    dword ptr [ecx+1Ch]
FF 51 1C  call    [eсx+EFI_PEI_SERVICES.ReInstallPpi]

FF 51 20  call    dword ptr [ecx+20h]
FF 51 20  call    [eсx+EFI_PEI_SERVICES.LocatePpi]

FF 51 24  call    dword ptr [ecx+24h]
FF 51 24  call    [eсx+EFI_PEI_SERVICES.NotifyPpi]
Примеры неявных вызовов интерфейсов до и после идентификации.

Отметим одну характерную особенность: в случае PEI-фазы для IA-32 архитектуры интерфейсы, класса «PPI Services» имеют смещения (offset) 18h, 1Ch, 20h, 24h.

А теперь сформулируем следующее утверждение:
Для построения Static Dependency Graph PEI-фазы необходимо и достаточно осуществить полное дизассемблирование всех исполняемых модулей фазы (по крайней мере отделить все команды), и произвести декомпиляцию «Indirect Call» команд со смещениями 18h, 1Ch, 20h, 24h в дизассемблированных модулях.
По сути дела, мы полностью сформулировали алгоритм решения задачи, и как только удалось выделить все вызовы интерфейсов/процедур класса «PPI Services», остаётся только определить, какие параметры передаются в эти вызовы. Задача может быть и не самая тривиальная, но, как показала практика, полностью разрешимая, у нас есть для этого все данные.

А теперь реальные примеры реальных данных для реальных модулей PEI-фазы. Намеренно не указываем, для BIOS/UEFI какой фирмы были получены результаты, просто приводятся примеры, как они выглядят.

Два примера описания PEIM-модулей с полной информацией об использовании «PPI Services» интерфейсов в этих модулях


    -- File 04-047/0x02F/: "TcgPlatformSetupPeiPolicy" : [007CCAF0 - 007CD144]
                DEPENDENCY_START
                  EFI_PEI_READ_ONLY_VARIABLE_ACCESS_PPI
                DEPENDENCY_END
        Install Protocols:
            [1] TCG_PLATFORM_SETUP_PEI_POLICY
        Locate Protocols:
            [2] EFI_PEI_READ_ONLY_VARIABLE_ACCESS_PPI

    -- File 04-048/0x030/: "TcgPei"                   : [007CD160 - 007CF5DE]
                DEPENDENCY_START
                  EFI_PEI_MASTER_BOOT_MODE_PEIM_PPI
                  EFI_PEI_READ_ONLY_VARIABLE_ACCESS_PPI
                      AND
                DEPENDENCY_END
        Install Protocols:
            [1] AMI_TCG_PLATFORM_PPI
            [2] EFI_PEI_TCG_PPI
            [2] PEI_TPM_PPI
        Locate Protocols:
            [1] EFI_PEI_TCG_PPI
            [1] EFI_PEI_READ_ONLY_VARIABLE_ACCESS_PPI
            [1] TCG_PLATFORM_SETUP_PEI_POLICY
            [5] PEI_TPM_PPI
        Notify Events:
            [1] AMI_TCM_CALLBACK
        ReInstall Protocols:
            [1] PEI_TPM_PPI

Списки протоколов по типам интерфейсов, в которых они использовались


Ниже под спойлерами приводятся сокращенные примеры списков PPIM-протоколов для каждого из интерфейсов класса «PPI Services».

Формат списков следующий:
| порядковый номер | имя_PPI | guid_PPI | имя_исполняемого_модуля : адрес_использования |

***** Install 99 Ppi in "Firmware"


***** Locate 194 Ppi in "Firmware"


***** ReInstall 5 Ppi in "Firmware"


***** Notify 29 Ppi in "Firmware"


Заключительный список всех guid-ов протоколов, на которые есть ссылки в конкретном BIOS/UEFI с легендой, указывающей в каких «PPI Services» встречаются данные протоколы


Ниже под спойлером приводится список из 97 PPi-guid-ов, встречающихся и явно используемых в конкретной прошивке, данные по которой приводились ранее.

Каждый элемент из списка предваряется легендой, в которой отражены все виды использования конкретного протокола.


	"D" - in DEPENDENCY section used
	"I" - in "InstallPpi"   functions used
	"L" - in "LocatePpi"    functions used
	"R" - in "ReInstallPpi" functions used
	"N" - in "NotifyPpi"    functions used

***** List Ppi in "Firmware"




В данном BIOS/UEFI обращают на себя внимание следующие интервалы списка протоколов:

  1. №№ 38-50.
    Определение протоколов/событий (InstallPpi), которые не используются ни одним модулем.
  2. №№ 87-95.
    Попытка запроса протоколов, которые не были установлены ни одним модулем данной прошивки.
  3. №№ 96-97.
    Два «Notify» события, для которых ни один модуль не удосужился объявить соответствующий интерфейс, соответственно, эти процедуры хоть и объявлены в исполняемых модулях, но никогда не будут работать.

Заключение


  • Результаты, подобные приведённым выше, были получены для BIOS/UEFI различных производителей, именно поэтому все примеры анонимны.
  • На самом деле решались более общие задачи реверса алгоритмов исполняемых модулей BIOS/UEFI, а полученный граф – побочный результат, этакий дополнительный бонус.
  • Корректное решение задачи «Получения Static Dependency Graph» для исполняемых модулей BIOS/UEFI требует проведения статического анализа бинарного кода, который включает в себя проведение полного дизассемблирования исполняемых модулей и частичной декомпиляции «Indirect Call» команд этих модулей.

Комментарии (19)


  1. CodeRush
    12.02.2019 19:43

    Статья отличная, спасибо, но «что делать, чтобы собрать данные для графа» было в принципе ясно с самого начала, и сложность там не в отсутствии хороших идей, а в отсутствии реализаций этих хороших идей. Вот и тут видит око, а исходников нет.
    Нужна эта функция на самом деле буквально паре человек, а автоматизация ее прямо в UEFITool'е потребует включения туда дизассемблера, поиска по сигнатурам и затем сотен часов отладки еще. Гораздо лучше, на мой взгляд, со стороны UT ограничится UEFIDump'ом и все дальнейшее делать скриптами для IDA/R2/Binja/чтотамувас, потому что оно все специально под подобные задачи заточено.


    1. raven19 Автор
      12.02.2019 20:16

      Естестественно, это было понятно, что и как, но не у всех такой богатый опыт, как у Вас, Николай!
      Кто-то ещё только начинает, пусть это будет просто Tutorial.
      Да, основная задача — реверс. Решил начать не с него — небольшая интрига!

      Я и сам начинал не с исходников типа «Teano...», а именно с реверса.
      Кстати, всё не так уж и тривиально там сделать. И, например, даже готового скрипта на чем бы то ни было будет не достаточно. Очень существенная вещь при дизассемблировании/декомпиляции — «метаданные». Как они выглядят, как организованы — будет существенно зависеть от средства, которым пользуемся… Но это отдельная тема — есть планы, но время покажет…


      1. CodeRush
        12.02.2019 20:38

        Если бы там было тривиально, уже были бы публичные готовые решения, а пока более менее готовое что-то есть только у Педро (EfiSwissKnife).
        С другой стороны, даже не готовое, кривое и на коленке — сильно лучше, чем ничего., поэтому даже такие «сугубо теоретические» статьи я поддерживаю всеми руками. Если найдете возможность выложить что-то — отлично, если нет — другим будет проще подступиться к проблеме.


        1. raven19 Автор
          12.02.2019 20:54

          В настоящий момент мне проще «выдать» готовый результат (по запросу), чем решение. В данный момент это больше набор методик решения задачи. Кое-где надо напильничком! Хотя практически всё можно на автомате решать!
          А публичных готовых решений и не будет! Вот в этом я точно уверен!
          А законченных решений и не может быть вообще!
          В принципе, для того, чтоб получить описанный результат, мне понадобилось немало времени. Да и работа не закончена!
          Кстати, как написал в «Заключении», задача была не в получении графа зависимостей, он — бонус.
          Да, для DXE объём побольше, а методически всё попроще.
          "...Knife" смотрел мельком, но там вроде точность невысокая…


  1. BiosUefi
    13.02.2019 11:44

    >> пожелание пользователей: построить «Dependency Graph» для исполняемых модулей
    а зачем?
    Дизассемблировав и поняв логику, перегнать ее в coreboot?


    1. raven19 Автор
      13.02.2019 11:53

      Сомнительно. Задачи разные бывают.


    1. JerleShannara
      14.02.2019 18:51

      Флаг вам в руки, если сможете это выполнить. Правда с засильем ME/PSP теряется весь смысл. Я уже давно забил на попытки понять логику настройки памяти(новее DDR2) даже по исходникам.


      1. BiosUefi
        15.02.2019 10:18

        >> с засильем ME/PSP теряется весь смысл
        а выкусывать их из оригинальных БИОСов, не способ?

        >>Флаг вам в руки, если сможете это выполнить.
        это работа, чуть ли не на года, по каждому из чипсетов.
        Делать это на авось, бесплатно, потешить самолюбие, интересно первые пару месяцев, не более.

        >> попытки понять логику настройки памяти
        Да, новее DDR2 это лес.
        В это даже АМИ не ввязывается, просто подвязывая код от Интел/АМД.


        1. JerleShannara
          15.02.2019 18:21

          Ну с ME положим переключить в АНБ режим можно, а что делать с PSP, который вообще основным x86 ядрам ресет не снимет, пока всё не проверит, плюс не удостоверится, что прошивка подписана, режим «йа разработчег» там есть, но AMD во всех бумагах ставит полный запрет выпуска такой платы в продажу и вообще куда-то дальше самих разработчиков. Я три года назад уже пришел к выводу, что полный OpenSource PC x86 это фантастика, либо компьютер образца 2011-14 года максимум, остается ARM архитектура, но у неё проблемы с производительностью и универсальностью. Ещё можно на Talos 2 посмотреть, но опять проблема с софтом и ещё более редкой архитектурой.
          Собственно вся трудоёмкость этого реверса и выливается в то, что никто этого не делает за редким исключением (KGPE-D16 может быть примером обратного, но я хз, что там рапторовцы делали на самом деле, может по полным даташытам переписали код)


  1. swmicro
    14.02.2019 08:57

    Есть идея использовать вашу утилиту для разработчиков. Полный билд занимает много времени, когда нужны изменения в каком-то модуле, то быстрее будет вставить его в уже готовый образ.


    1. raven19 Автор
      14.02.2019 09:25

      Извините, не понял идею, очень уж она сумбурно выражена. Ну меняйте изменённый модуль, есть для этого MMTool или UEFITool (в тех случаях, когда она работает — но в PEI томе у неё как раз проблемы). Кто мешает-то?

      Вообще-то информация типа последнего списка наиболее полезна как раз разработчикам.

      Она покажет, какие интерфейсы вообще не работают, где перекосы, возможный «мёртвый» код.

      Дело в том, что увидеть по исходникам подобные вещи — весьма нетривиальное занятие.
      Те, кто видел даже древние утекшие исходники Aptio IV, тот это должен понимать.

      Я не останавливался на том, где может понадобиться подобного рода информация, но для аналитиков из команды разработчиков подобная информация была бы не заменима.

      Даже с точки зрения информационной безопасности, подобные дыры — это дар, место, где можно размещать свои поделки, т.е. места «повышенного внимания».

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

      На PEI-фазе, например, можно проанализировать, кто и какие HOB-ы создаёт и как их использует, а потом посмотреть это же и на DXE фазе.
      И это так — самые простенькие идеи.

      Другое дело, что я не описывал, как в реалити это можно делать, но там места надо раза в 2-4 больше, если только кратенько.

      Результаты определённые есть, идей хватает, возможно и продолжение (развитие).
      Статический анализ бинарного кода — штука весьма занятная.


      1. swmicro
        14.02.2019 20:53

        Идея как раз в том чтобы найти полезное применение вашей утилите. Анализатор тоже звучит интересно, особенно если BMC имеет доступ к ROM. Можно сделать антивирус.
        Я работал в AMI Core R&D Aptio IV и V но что-то не припомню особых отличий от Tiano по части проблемы мертвого кода.
        Вставить в область мертвого кода свой код, без изменения подписи — занятие песполезное. Поэтому я подумал что это полезно только для разработчиков.


        1. CodeRush
          14.02.2019 21:11

          Если вы про полезное применение UEFITool, то их масса полезных уже сейчас, но в билды встраивать именно эту утилиту я все равно не стал бы, даже если результат получился бы немного быстрее, чем у нынешней билд-системы в EDK2 (которая тормозит просто забей) — надежность низкая, плюс всякие вендорские фишки поддерживаются весьма слабо.
          С последним предложением я бы очень сильно поспорил, потому что в теории теория от практики тоже не отличается, а на практике всю цепочку доверия большинства IBV и OEM уже не раз обходили, и еще не раз обойдут, потому что никто вообще не заморачивается проверкой того, что занятие то действительно бесполезное. А когда начали проверять, неожиданно оказалось, что вся «защита» обходится удалением драйвера BootGuardDxe, который раньше на бит в HOBе реагировал, при помощи упомянутого уже UEFITool'а, упс.


          1. swmicro
            15.02.2019 01:30

            На практике это всего лишь ошибки имплементации или баги. Иначе каким образом BootGuard в PEI допустил запуск измененного DXE? Gigabyte наверняка уже пофиксили эту уязвимость.


            1. CodeRush
              15.02.2019 02:00

              Атакуют при этом чаще всего не идею, а реализацию.
              Intel BootGuard сам по себе отработал штатно, и корень доверия он вполне себе обеспечил, т.к. в PEI-томе, накрытом им, ничего никто не менял. Зато драйвер BootGuardPei, написанный кстати именно AMI, а не OEMом, проверив хеши DXE-тома и обнаружив несовпадение из с эталоном (который тоже не даст исправить именно Intel BootGuard, потому что они в файле в PEI-томе лежат) вместо того, чтобы машину завесить или перезагрузить, поставил единицу в HOB и продолжил работу. Это потом уже BootGuardDxe должен был среагировать на эту единицу и выдать пользователю надпись «хеши поломались, все пропало», но вот незадача, его удалили уже, и потому реагировать на эту сиротливую единицу стало некому.
              В итоге платформа замечательно работает с модифицированным DXE-томом, цепочка доверия нарушена, все счастливы, а Gigabyte виноват только в том, что не стал перепроверять реализацию цепочки доверия, предоставленную ему IBV.
              Наверняка пофиксили, да. Если поняли, если согласовали фикс с AMI, если получили обновленный референс-код, если интегрировали его, если выпустили обновление, если запретили откат на него, если пользователь это обновление поставил. Несколько многовато если, на мой взгляд.


    1. CodeRush
      14.02.2019 20:27

      Не надо лучше, не удаляйте все артефакты после успешной сборки, и повторная сборка много времени не займет.
      Как разработчики спецификации не старались, все равно не получилось избавиться от всех горизонтальных и вертикальных связей, и потому такая «полевая хирургия» иногда работает хорошо, а иногда не работает никак.


      1. swmicro
        14.02.2019 21:00

        Повторная сборка тоже занимает существенное время. На мой взгляд ROM распаковать, обновить и переподписать новый имидж должно быть быстрее.


        1. CodeRush
          14.02.2019 21:16

          Очень много информации, нужной для 100% правильной пересборки, в образе либо нет, либо она там в крайне неудобном виде и ее придется доставать при помощи хитрого парсинга, дизассемблера и такой-то матери. Те же номера динамических PCD или AMI'шных SDL TOKENов брать неоткуда, и потому при крупных изменениях все равно придется половину пересобирать, а отличить крупные от некрупных труднее, чем не заморачиваться и собирать каждый раз заново.


          1. raven19 Автор
            14.02.2019 23:21

            На мой взгляд дискуссия уехала совсем в сторону. Ну пересборка тут совсем ни при чем.
            Открою маленький секрет. Когда разбираюсь с прошивкой, то она вся в памяти и дизассемблирую и декомпилирую всё сразу (ну 32 и 64 отдельно, хотя тоже можно сразу), при этом вся память (все файлы под руками), и PCD базы и NVRAM и RAW и FREEFORM файлы.

            Вот почему описываю уже результат некоторой аналитики (простейшей).
            На самом деле подобной аналитики вариантов можно напридумывать.
            Кому такие инструменты были бы полезны… Флаг в руки, думайте, мечтайте…

            Это и фишка и проблемы, та же ИДА работает в этом случае через… (не скажу через что).
            Но Ильфак вряд слушать будет, чего не хватает. Ну глюки он правит. Когда мне не лень, напрягаю его, с поддержкой у него не плохо налажено, особенно если не с ним лично взаимодействуешь!!!

            Честно говоря, на последних прошивках, когда они всё в динамику толкают, даже имена файлов (нет UI-секций), с отдельным файлом разбираться — вряд ли просто.
            Но полезный анализ никто не отменит.
            Задачи надо правильно ставить.

            Ну пытается же Интел что-то получить/доказать, используя символьную информацию.
            Просто ещё один подход, кстати, как показывает практика, вполне реальный.
            Не говоря уж о той проблеме, что иногда просто интересно, а соответствует ли бинари тому исходному коду, который тебе выдали?..