Повертаємо оригінальні сторінки меню в Phoenix SCT UEFI

Здрастуйте, шановні читачі Хабра.
З вами знову я і ми продовжуємо копатися в різних реалізаціях UEFI в ім'я добра. Є у мене один старий китайський GSM-модем, який на моєму Dell Vostro 3360 визначається через раз, а на більш старих ноутбуках — нормально. Після декількох експериментів з підключенням його через перехідник до основного ПК з'ясувалося, що йому чомусь не подобається підключення через PCIe Gen2, і хотілося б перемкнути порт на Gen1, але в UEFI Setup потрібної налаштування не виявилося. Прикро, але не смертельно, адже дуже часто виробники пристроїв не видаляють оригінальні меню виробника UEFI, а просто приховують їх, або показують на їх місці свої, тому після невеликого реверс-інжинірингу оригінальне меню можна повернути на місце, що у мене і вийшло. В цей раз однією IDA Demo вже не обійтися, т. к. DXE-драйвери в більшості сучасних UEFI збираються для архітектури x86-64, тому замість неї будемо використовувати radare2.
На лаври першовідкривача не претендую і подібним модифікаціям сто років в обід, але постараюся показати, як зробити подібну модифікацію самостійно.
Якщо вам все ще цікаво — ласкаво просимо під кат.

Мотивація
Модифікації меню — досить старий, відомий і затребуваний вид модифікацій серед тих, кому доступного спочатку меню з якихось причин мало. Частіше всього ці причини надумані, «тому що можна», але буває і так, що виявляються прихованими важливі настройки начебто можливості практично повністю відключити МО, включити налагодження по USB EHCI Debug Port), налаштувати режими роботи PCIe і т. п. Виробникам заліза буває простіше приховати подібні пункти меню «не для всіх», ніж описувати їх в документації і витрачати гроші на їх підтримку, але такі приховані пункти найчастіше можна відновити, чим і займемося. Але для початку — необхідна інформація про пристрої Setup-меню.

Коротко про пристрої UEFI Setup
Меню Setup в UEFI влаштовано досить цікавим чином і описано в специфікації UEFI Human Interface Infrastructure (глави 29 — 31), але про все в короткій статті не розповісти, тому якщо кому цікаві подробиці — пишіть у коментарях.
Тим не менш, основи пояснити варто. Складається це саме меню з форм, описаних мовою VFR і Unicode рядків (правда, це не зовсім чесний Unicode, а лише UCS-2), зберігаються окремо. Форми пов'язані з рядками через ID, що полегшує його локалізацію.
Найпоширеніший елемент меню, комбобокс, на VFR описується приблизно так:
oneof varid = SETUP_DATA.PrimaryPcie,
prompt = STRING_TOKEN(STR_PRIMARY_PCIE),
help = STRING_TOKEN(STR_PRIMARY_PCIE_HELP),
option text = STRING_TOKEN(STR_COMMON_AUTO), value = 0, flags = DEFAULT | MANUFACTURING | RESET_REQUIRED;
option text = STRING_TOKEN(STR_COMMON_PCIE1), value = 1, flags = RESET_REQUIRED;
option text = STRING_TOKEN(STR_COMMON_PCIE2), value = 2, flags = RESET_REQUIRED;
option text = STRING_TOKEN(STR_COMMON_PCIE3), value = 3, flags = RESET_REQUIRED;
option text = STRING_TOKEN(STR_COMMON_PCIE4), value = 4, flags = RESET_REQUIRED;
option text = STRING_TOKEN(STR_COMMON_PCIE5), value = 5, flags = RESET_REQUIRED;
option text = STRING_TOKEN(STR_COMMON_PCIE6), value = 6, flags = RESET_REQUIRED;
option text = STRING_TOKEN(STR_COMMON_PCIE7), value = 7, flags = RESET_REQUIRED;
endoneof;

А рядки до нього — ось так:
#string STR_PRIMARY_PCIE #language eng "Primary PCIe"

Пояснення потребує, напевно, тільки varid = SETUP_DATA.PrimaryPcie. Справа в тому, що зсередини меню на 95% — просто інтерфейс до змінних NVRAM. Змінні зберігаються в різних блоках (т. зв. varstore), але настройки, до якого є доступ з Setup, найчастіше зберігаються в здоровенном блоці SETUP_DATA, який в свою чергу цілком зберігається у змінній з іменем Setup. Решта 5% — це інтерактивні елементи меню на зразок значень поточного часу, температури компонентів, швидкості обертання вентиляторів і т. п., вони обробляються callback-функції, прив'язаними до відповідного елементу меню, але це вже інша історія.
Елементи меню збираються форми та форми компілюються у внутрішнє представлення (IFR), збираються в formset'и і надходять на вхід FormBrowser'а — движка, який і показує користувачеві всі отримані форми у вигляді UI. Реалізації FormBrowser'ів відрізняються в деяких деталях, і сильніше за всіх від еталонної реалізації від Intel відійшли в AMI, з простої причини — спочатку еталонна реалізація дико гальмувала, т. к. меню зберігалося в десятці різних місць і його доводилося збирати при кожному виклику UI, тому AMI адаптували свою реалізацію TSE AMIBIOS8 для UEFI, яку (з перемінним успіхом) підтримують і понині.
У моєму ж випадку UEFI заснований на платформі Phoenix SecureCore Tiano 2.3, в якій FormBrowser влаштовано майже стандартно: formset'и для кожної вкладки (Main, Advanced, Security, Boot, Exit) зберігаються в окремих DXE-драйвери, а FormBrowser спілкується з ними через протоколи, які реєструють ті. Залишилося знайти потрібний драйвер (в якому лежить оригінальне меню Advanced) і пояснити FormBrowser', що потрібно показувати саме його, а не те, що він показує замість нормального Advanced зараз. Поїхали!

Необхідні інструменти
Редагувати образ будемо з допомогою UEFITool, діставати форми — з допомогою Universal IFR Extractor, розбирати і досліджувати драйвери formset'ів і сам FormBrowser — за допомогою radare2, а прошивати модифікований файл довіримо китайському програматора за п'ять баксів.

Пошук
Знімаємо дамп прошивки, відкриваємо в UEFITool і шукаємо те, що нам потрібно на самому початку — налаштування швидкості PCIe-порту по імені «Gen1»:

4 входження, три з яких — у драйвері по імені PlatfromHiiAdvancedDxe, готовий кандидат на діставання з нього форм і дизасемблювання, витягаємо його через Extract body…
Запускаємо Універсальний IFR Extractor, вказуємо шлях до витягнутого файлу, натискаємо Extract і отримуємо текстовий файл, в якому описана структура меню Advanced в тому вигляді, в якому воно нам потрібно:

Шукаємо в файлі «Gen1» і знаходимо ось таку настройку:
0x0B018 Form Set: Advanced
...
0x44020 Setting: PCIe Speed, Variable: 0x25
0x44046 Default: 8 Bit, Value: 0x0
0x44053 Default: 8 Bit, Value: 0x0
0x44060 Option: Auto, Value: 0x0
0x4406E Option: Gen1, Value: 0x1
0x4407C Option: Gen2, Value: 0x2

Тепер сумнівів не залишається — це потрібний файл, але настройки з нього в UEFI Setup не видно.
Зате видно інший Advanced, який знаходиться у файлі DellSetupAdvancedDxe (знайдений пошуком по рядку Advanced в UEFITool), дістаємо з його виконувану секцію для подальшого вивчення:

Ну от, залишилося дослідити різницю між файлами і зрозуміти, що і де потрібно змінити, щоб замість другого відображався перший.

Дослідження
Копіюємо обидва файли у ВМ з Linux, збираємо radare2 і відкриваємо два терміналу, в одному з яких запускаємо r2 PlatfromHiiAdvancedDxe.bin, а в іншому — r2 DellSetupAdvancedDxe.bin, а після запуску переходимо у візуальний режим з дизассемблером командою Vp:

Спостерігаємо вражаючу одностайність, порушувана тільки різними адресами переходів. Все говорить про те, що код згенерований з одного і того ж шаблону, тому сильно він не відрізнятиметься. Знаючи архітектуру FormBrowser'а, можна припустити, що відрізняються файли тим, що публікують протокол доступу до них під різними GUID'ами. Протокол можна опублікувати через виклик gBS->InstallProtocolInterface, який у лістингу буде виглядати приблизно так:
mov reg, offset gBS ; покажчик на BootServices
lea rcx, offset Handle ; перший параметр - ідентифікатор протоколу або NULL
lea rdx, offset ProtocolGuid ; другий параметр - GUID реєстрованого протоколу
xor r8d, r8d ; третій параметр - тип інтерфейсу, зараз він завжди 0
lea r9, offset Interface ; четвертий параметр - інтерфейс реєстрованого протоколу або NULL
call [reg + 80h] ; виклик gBS->InstallProtocolInterface

Після нетривалих пошуків дуже схожий шаблон знаходиться в обох файлах:


Вже з коментаря radare2 навпаки lea rdx ясно, що GUID'и реєстрованих протоколів відрізняються:

Тепер можна спробувати замінити GUID у файлі PlatfromHiiAdvancedDxe на GUID з DellSetupAdvancedDxe і видалити останній, але краще пошукати, хто саме використовує протокол з GUID з DellSetupAdvancedDxe і замінити вже в ньому. Вбиваємо в пошук:

Знаходимо два екземпляри, один з яких нам вже відомо, а інша знаходиться в драйвері SystemFormBrowserCoreDxe по зсуву 2C0h від початку. Залишилося замінити і спробувати в справі.

Тестування і висновок
Замінюємо знайдений GUID, зберігаємо зміни, пересобираем образ і прошиваємо на програматорі, після чого заходимо в UEFI Setup, відкриваємо Advanced і вуаля, оригінальні настройки як на долоні. Деякі, зрозуміло, краще не чіпати, деякі інші не працюють, але головне — нарешті можна виставити обмеження швидкості для PCIe Port 1, заради якого я і затіяв ці танці з бубном.
Насправді, можна було обмежитися дослідженням текстового файлу з IFR і заміною одного байта в NVRAM на потрібний, але раз вже вийшло повернути оригінальне меню — нехай буде так.
В інших вендорів все може бути влаштовано інакше, так що не сприймайте цей пост як універсальне керівництво.
Дякуємо за увагу та вдалих вам модифікацій.

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.