Простые приемы реверс-инжиниринга 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 все еще LittleEndian, поэтому порядок байт пришлось поменять):


    Ииии… БИНГО, всего 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 вынуждена исполняться непосредственно из flash-памяти, которая в тот момент доступна только для чтения, поэтому глобальные переменные в таких 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 + 28h], нажать 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 и все готово.

    UPD


    Внезапно выяснились некоторые новые обстоятельства. Оказывается, и в этой, «старой» версии защиты имеется копия PEI-тома, которая может быть использована системой для восстановления оригинального состояния PEI-тома, и в этой копии тоже нужно заменить модуль SecureUpdating на пропатченый. Копия хранится в файле с GUID 05B3AFFD-F7CC-4C0A-A19A-A9774E2675D7 типа RAW, поэтому содержимое этого файла в текущих версиях UEFITool'а не отображается:

    На деле это файл типа Freeform, и чтобы получить доступ к его содержимому, нужно извлечь его через Extract as is..., заменить в извлеченном файле байт по смещению 12h (File type) с 01 на 02 и вставить полученный файл вместо оригинального через Replace as is...:

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

    Заключение


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

    Similar posts

    Ads
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More

    Comments 20

      +4
      Отличная статья, жанр ассемблерный детектив
        0
        История повторяется «благими намерениями intel и и же с ним на тему UEFI» вымощена в дрога в пользовательский ад.
        Так и не ясно что такого надо пихать в эту UEFI что не может сделать ядро операционной системы.
          +5
          Низкоуровневую инициализацию оно не может, ровно потому, что ядро одно на всех, а инициализация отличается от платы к плате даже внутри одной линейки.
          Я рад, что BIOS заменили на UEFI, ибо если его правильно готовить (выкинуть все лишнее, не увлекаться слишком новыми фичами и не добавлять ненужных искусственных ограничений, вроде описанного в статье) — сложность написания кода прошивки снижается на порядок, как и сложность доработки уже существующего. А вот пихать туда все больше хлама — это действительно не очень правильно, но маркетологов уже не остановить.
          AMI уже добавляет поддержку OpenGL в Setup screen, дуалбут во встроенный Android, браузер и прочее такое. HP зачем-то ринулись писать «защиты», и сейчас последние версии проверяют DXE из PEI, а потом еще PEI из DXE (смогу открутить эту новую защиту — напишу продолжение к этой статье), оборудование проверяется по белым спискам, а прошить что-то чужое без программатора стало не то, чтобы невозможно, но очень трудно.
            0
            Если как вы говорите «все лишнее выкинуть из UEFI» то получит uboot. И ой! Оказывается что uboot умеет делать все тоже самое и на куче платформ.
            Поддерживая UEFI это хороший корм для вещей типа разрешается использовать только «сертифицированного» оборудование. А по сути не сертифицированное, а тот кто больше проплатил.
            А можно вопрос зачем OpenGL в Setup-е?
              +6
              А вы пробовали Uboot'ом проинициализировать хоть что-то более или менее сложное, да тот же контролер DRAM в AMD APU? Я вот пробовал и могу сказать — пусть хоть UEFI, хоть coreboot, хоть FSP, хоть черт лысый, лишь бы поддержка от производителя CPU была и возможность реализовать свою инициализацию без слишком уж сильного колдунства. А Legacy BIOS за 30+ лет его использования оброс таким количеством костылей и подпорок, что UEFI по сравнению с ним — верх логики, архитектуры и продуманности.
              Про сертификацию и прочее — все вопросы к вендорам. Думаете, вам в Uboot или coreboot белый список WiFi-карт не добавят и загрузчик своим ключем не подпишут? Еще как добавят и подпишут, только выковыривать будет гораздо сложнее и одной IDA Demo уже не обойдешься. Все эти защиты и списки — желание вендрора, а не свойство UEFI, и я своими руками пишу UEFI-совместимые прошивки, в которых не только нет защиты от модификации, но и можно при помощи штатных утилит добавить свои DXE-драйверы, загрузчики и приложения в прошивку — было бы желание. А у «закрывальщиков» советую просто ничего не покупать.
              Про OpenGL, графику и прочий модный Setup — это продает. Вследствие интеграции всего подряд под крышку процессора платы разных линеек и даже разных вендоров практически перестали отличаться, и поэтому отдел маркетинга лезет из кожи вон, чтобы выделить свои платы из череды таких же. Отсюда и серо-буро-малиновые текстолиты, и системы водяного охлаждения на чипах, которые без радиатора кое-как греются до 60 градусов, и карта звездного неба на фоне Setup, и прочий дуалбут в Android. Это все кошмар-кошмар, конечно, и лучше бы баги правили, но в сегменте «для хомяков» надежность и предсказуемость уже не продает, а вот красивая картинка — еще как. Альтернатива — производители для Embedded и Industrial, с другими подходами, целями и ценами. Вот там как был текстовый Setup, так и есть, только поддержку тач-скринов добавили, а то не у всех систем клавиатуры есть.
                0
                Альтернатива — производители для Embedded и Industrial, с другими подходами, целями и ценами. Вот там как был текстовый Setup, так и есть, только поддержку тач-скринов добавили, а то не у всех систем клавиатуры есть.

                Dell с Lenovo пока держатся, а с тайваньцами всегда так было. Даже Intel в последние годы сделал убогий Visual BIOS, прошивка которого на DH61CR требовала перепрошивки через рекавери.
                  0
                  Блин не поверите да инициализировал DDR2 контроллер в чем проблема? Через командную строку получал новые параметры и устанавливал их. Так через это несчастный uboot сохранял новые настройки в spi eerom.
                  Сейчас пишу модуль для ядра что бы можно было рулить тайминагми оперативки, т.к. «вендоры» просто уроды. Официально объявляют частоту CPU 700 Мгц, режут на 560 МГц. Ставят чип на 166, а тактируют его 100 МГц. И для все этого не нужен огород, а нужны просто нормальные спеки.
                  А про закрытые uboot есть очень хороший пример. Есть не безизвестная контора Huawei с домашней железякой WS880. Все там хорошо и железо гуд и цена. Вот правда решили они поиграться с DRM в uboot и как следствие ни кому эта железяка не нужна да и цен из-за этого поднять не могут. А вот конкуренты аналоги без бреда в виде «защиты от самого себя» успешно продают тоже железо почти в 2 раза дороже.
                  • UFO just landed and posted this here
                  • UFO just landed and posted this here
                      0
                      Жаль их больше не будет, остаются брендовые компьютеры с сборе и платы для рабочих станци от SMC.
                    +3
                    Сейчас смотрю на AMIшный APTIO на asrock-овской плате и фигею: фон — меремигивающееся звездное небо. Из биоса можно запустить easy driver install, обновить биос по интернету, тутже написать о проблеме асроковцам, причем приложить файло с любого диска.
                    А нафига opengl? Да чтобы это чертова картинка не тормозила(а сейчас она реально тормозит, мышка рывками двигается). А нафига картинка? А чтобы приблизиться к суперэффектам в биосе, оно так лучше продаваться будет.
                +2
                По поводу работы со структурами в radare2 рекомендую прочитать эти две статьи radare.today/parsing-a-fileformat-with-radare2/ и radare.today/types/.
                Если чего-то не хватает или непонятно — пожалуйста, всегда на связи.
                  0
                  Спасибо огромное, буду пробовать. GUI не хватает, но это печеньки темной стороны, и с ними надо бороться.
                • UFO just landed and posted this here
                    +1
                    Не знаю, работают ли скрипты в IDA demo, а основную покупать адски дорого, если рассматривать это все как хобби. Буду изучать radare2, когда время появится. Псто ждем с нетерпением, у меня тут тоже назревает продолжение про те же яйца, только в другой руке и на более новых ноутбуках HP.

                    • UFO just landed and posted this here
                        0
                        Hopper пробовал, но он показался примитивным совсем, даже с учетом встроенного генератора псевдокода и цены в 70 долларов. Можно написать и про него, на самом деле, материала хватает.
                        0
                        Дождались. Отличный пост, отличное дополнение к CHIPSEC, прогоню у себя обязательно.
                        Напиши что-нибудь на Хабр, d_olex, да хоть перевод этого же поста на русский, дабы тебе можно было поставить плюс в карму.
                    • UFO just landed and posted this here
                        0
                        Ну не буду же я писать, что мой софт лучше, чем у Тедди или Энди. :) Да и багов море в любом софте такого рода, т.к. большая часть вендоров спецификацию в глаза не видели и на соответствие ей свои образы не проверяют, поэтому что-то хорошо работает в одной программе, что-то другое — в другой., так и живем.

                      Only users with full accounts can post comments. Log in, please.