Прості прийоми реверс-інженірингу UEFI PEI-модулів на корисному прикладі

Здрастуйте, шановні читачі Хабра.
Після довгої перерви з вами знову я і ми продовжуємо копатися в нутрощах UEFI. На цей раз я вирішив показати кілька технік, які дозволяють спростити реверс і налагодження програмних компонентів UEFI на прикладі застарілого-але-все-ще-популярного PEI-модуля SecureUpdating, який покликаний захищати прошивку деяких ноутбуків HP від модифікації.

Передісторія така: одного разу ввечері мені написав знайомий ремонтник ноутбуків з Білорусі і попросив подивитися, чому ноутбук з заміненим VideoBIOS'ом не хоче стартувати, хоча такий же точно поруч успішно стартує. Відповідь виявився на поверхні — не стартує після модифікації ноутбук мав більш нову версію UEFI, в яку добрі люди з HP інтегрували захист від модифікації DXE-томи (а там і знаходиться потрібний нам VideoBIOS разом з 80% коду UEFI), щоб злісні віруси і не менш злісні користувачі нічого там не зламали ненароком. Тоді проблема вирішилася перенесенням PEI-модуля SecureUpdating з старої версії UEFI в нову, але через два тижні той же чоловік звернувся знову, на цей раз на схожому ноутбуці стара версія модуля працювати відмовилася, і моя допомога знадобилася знову.
Якщо вас зацікавили мої подальші пригоди в світі UEFI PEI-модулів з дизассемблером і пропатченными переходами — ласкаво просимо під кат.

Пара посилань на лікнепЯкщо вам майже нічого не зрозуміло — нічого страшного, у мене є кілька пояснювальних термінологію статей: 1, 2, 3, почитайте і повертайтеся. Для фанатів оригінальної документації завжди в наявності специфікація UEFI PI, там все розписано набагато докладніше.

Необхідні файли та інструментиДля розбирання вищезгаданої прошивки нам знадобляться:
  1. Власне файл з прошивкою, мені був висланий ось цей.
  2. Будь-яка утиліта для роботи з образами UEFI, я буду використовувати UEFITool на правах її автора, але ви можете використовувати будь-який на ваш смак, наприклад uefi-firmware-parser або PhoenixTool — це не принципово.
  3. Hex-редактор на ваш вибір, я скористаюся HxD.
  4. Дізассемблер з підтримкою PE32-файлів, тут нам ідеально підійде IDA 6.6 Demo, т. к. PEI-модулі в переважній більшості випадків 32-бітові та обмеження демо-версії занадто сильно не завадять. Якщо шановний xvilka зможе показати, як в radare2 довантажити структури з C-файлу, я спробую наступного мод зробити саме в ньому, а поки IDA — наше все.
  5. З комплекту efi-utils знадобиться здоровенний файл behemoth.h, містить у собі визначення практично всіх можливих структур даних, використовуваних в UEFI. У нашому випадку знадобляться всього пара-трійка.
Відправна точкаОтже, зі слів товариша-ремонтника нам відомо наступне: будь-яка зміна DXE-тома призводить до мертвого ноутбука, моргающему Caps-lock'ом, а зміни в інших частинах образу до такого результату не призводять. Це означає, що десь зберігається або контрольна сума, або ЕЦП, яка перевіряється кодом одного з PEI-модулів, і, якщо вона зійшлася, управління передається в фазу DXE, а якщо немає — воно передається кудись туди, де нам не раді.

Нам потрібно з'ясувати наступне:
  1. Де саме зберігається КС/ЕЦП?
  2. Хто перевіряє?
  3. І, головне, як зробити так, щоб перевірка завжди закінчувалася успішно?
Поїхали!
Роби раз!Відкриваємо файл з прошивкою в UEFITool і дивимося уважно:

На вигляд нічого незвичайного, крім повідомлення про те, що всередині вільного простору одного з UEFI-томів знайшлися дані, яких по специфікації там бути не повинно. Саме так виробники (з тих, хто не дуже вірить у специфікацію) зазвичай ховають свої контрольні суми або цифрові підписи. Подвійним клацанням за повідомленням вибираємо те, в якому ці самі дані знайшлися, і дістаємо його цілком у файл dxe.vol для аналізу. UEFITool закривати не треба — ще стане в нагоді.

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

Тут же знаходиться дуже підозрілий шматок даних розміром 100h (позначено червоним), а за ним — сигнатура $SIG, версія прошивки F. 50 і кодове ім'я платформи 68CPK. Таким чином, на перше питання відповідь, ймовірно, отриманий.

Роби два!Щоб відповісти на другий, потрібно пошукати PEI-модулі, які звертаються до цього блоку даних. Це буває досить непросто і часто доводиться пробувати кілька варіантів. Найпростіший — пошукати інші входження сигнатури $SIG, але в даному випадку нас відразу ж осягає невдача — інших входжень такого рядка в образі немає. Але якщо блок шукається не по сигнатурі, значить він шукається або усунути, або за абсолютною адресою. Зміщення його всередині тома — 12FEE0h. Перемикаємося на UEFITool і шукаємо пошуком без урахування заголовків Hex-патерн E0FF12 (процесори Intel все ще little-endian, тому порядок байт довелося поміняти):


Іііі… БІНГО, всього 2 входження, і обидва в одному і тому ж PEI-модулі з багатообіцяючою назвою SecureUpdating. Виймаємо його без заголовків у файл su.bin для подальшого аналізу:

Таким чином, імовірно, отримано відповідь і на друге питання.

Роби три!Залишилося розібратися з третім. Для цього потрібен дізассемблер, трохи знань про будову PEI-модулів і багато-багато терпіння. Запускаємо IDA, погоджуємося з умовами демонстраційного режиму і відкриваємо раніше отриманий файл.
Йдемо в Options -> Compiler... і виставляємо їх ось таким чином:


Потім йдемо в File -> Load File -> Parse C header file... і завантажуємо вищезгаданий у списку необхідних файл behemoth.h з визначеннями UEFI-структур:

На помилки розбору звертати увагу не варто — вони в даному разі не зашкодять.

Тепер відкриваємо вкладку Structures, йдемо в Edit -> Add structure type... (або натискаємо Insert, так швидше), там натискаємо Add standard structure у списку знаходимо найважливішу для PEI-файлів структуру — EFI_PEI_SERVICES, яку і додаємо:

Заодно додамо EFI_GUID і EFI_FFS_FILE_HEADER — знадобляться.

Структура EFI_PEI_SERVICES (якщо зовсім точно — подвійний покажчик на її екземпляр, створений ядром PEI) передається як параметр в точку входу кожного PEI-модуля і практично всі його функції. Зроблено так тому, що частина PEI змушена виконуватися безпосередньо з флеш-пам'яті, яка в той момент доступна тільки для читання, тому глобальні змінні в таких PEI-модулях недоступні і все своє доводиться носити з собою. Це неприємне обмеження для програміста, проте воно дуже допомагає у дослідженні та налагодження PEI-модулів, т. к. розіменування подвійного покажчика — не дуже популярна процедура в звичайному коді, і тому більшу частину викликів PEI-сервісів можна відстежити очима прямо в лістингу. Ось до нього і повернемося, але початку згадаємо (або дізнаємося), як виглядає точка входу в PEI-модуль. Не поспішайте гуглити, виглядає вона ось так:
EFI_STATUS 
EFIAPI 
PeimEntry(
IN EFI_FFS_FILE_HEADER *FfsFileHeader, 
IN EFI_PEI_SERVICES **PeiServices
) {
...
}

EFI_STATUS — це typedef для unsigned int, EFIAPI — typedef для stdcall, перший параметр вказує на той FFS-файл, в якому знаходиться викликається PEI-модуль (на випадок, якщо модуль зберігає поруч з собою якісь дані і йому потрібен доступ до них), а другий — вже описаний вище подвійної покажчик на таблицю PEI-сервісів. Озброївшись цим знанням, сміливо змінюємо тип функції start (виділивши її і натиснувши клавішу Y), виходить приблизно так:


Тепер з лістингу видно наступне: спочатку йде низка викликів функцій, для яких PeiServices не потрібен. Найчастіше вони займаються введенням-виведенням/IO-портів та іншої магією такого роду, перевіримо це припущення, перейшовши в першу по порядку:


Дійсно, функція виконує виведення даних у порт 24Eh. Наступні кілька я опущу (вони дуже схожі, пишемо-читаємо IO-порти) і перейду до тих, які PeiServices все ж використовують.
Перша виявляється тривіальною і просто зберігає PeiServices в глобальну змінну (що вказує на те, що наш PEI-модуль виконується вже з оперативної пам'яті, але пильне око фахівця помітив би це ще за інформацією про PE-файлі в UEFITool):


Наступна вже набагато більше і набагато цікавіше, особливо якщо виставити їй правильні типи параметри і значення, що повертається:


Виділений червоним фрагмент відразу після прологу і обнулення локальних змінних — той самий помітний патерн з разыменованием подвійного покажчика, про який я говорив вище. Щоб зрозуміти, який саме PEI-сервіс був викликаний, нам і потрібні були всі ці танці навколо структур, тепер можна встановити курсор на [eax + 28], натиснути T та вибрати у вікні EFI_PEI_SERVICES.GetBootMode:


Подивившись її сигнатуру, можна зробити висновок, що var_134 — це насправді змінна на стеку, в яку буде записано значення поточного режиму завантаження. Потім це значення порівнюється з 11h і якщо воно йому одно — обчислення йдуть далі, а якщо все ж одно — кладемо в eax нуль і йдемо на return. 11h в даному випадку — це BOOT_ON_S3_RESUME, тобто якщо система прокидається з ACPI Sleep Mode, то функція завжди повертає 0 (а це на місцевому діалекті EFI_SUCCESS). Якщо ж система завантажується в іншого стану, то виконання йде далі, і в підсумку проходить через ось таке цікаве місце:


Ба, старі знайомі! Ті самі входження 12FEE0h, за яким ми цей модуль і знайшли. І спочатку функцією CopyMem та підозріла КС/ЕЦП копіюється в буфер, а потім оригінальне місце затирається байтом FFh, яким порожнє місце в DXE-томе і заповнено спочатку, а далі йде виклик функції, яка цю саму КС/перевіряє ЕЦП.
Можна, звичайно, почати тепер досліджувати її, але ж ця частина коду взагалі не виконується, якщо система прокидається з S3 (що логічно, оскільки нічого з DXE-тома S3 не потрібно, зате прокидатися потрібно як можна швидше), і тим не менш працює, тому для початку зробимо так, щоб цей конкретний PEI-модуль думав, що у нас вічне літо і завжди S3_RESUME, і пропускав будь-які перевірки.
Для цього достатньо поміняти cmp [ebp+BootMode], 11h на xor eax, eax, тоді наступний за ним jnz не виконається ніколи, але якщо він ніколи не повинен виконається, то простіше замінити сам перехід на пару NOP'ов:


Міняємо в Hex-редакторі виділений фрагмент на 90 90 і все готово.

Висновок
Подальше — справа техніки. Замінюємо через Replace Body… вміст оригінальної PE32-секції на модифікований файл, вносимо потрібні нам зміни в DXE-те, зберігаємо зміни і прошиваємо отриманий образ на програматорі. У мене цього ноутбука не було, зробив модифікацію і відправив результати прохачеві. Через пару годин була отримана відповідь: «величезне спасибі, все працює, клієнт задоволений», і я з чистою совістю пішов писати статтю, яку ви тільки що прочитали.
Дякуємо за увагу та вдалих вам модифікацій.

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

0 коментарів

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