И снова здравствуйте, уважаемые хабрачитатели.
В рамках борьбы за возможность модификации UEFI на ноутбуках HP пришлось отломать еще одну защиту, на этот раз более современную. Видимо, отдел разработки прошивок в HP догадался, что предыдущая защита была не ахти, и потому они решили радикально её улучшить, поэтому метод обхода защиты DXE-тома из предыдущей статьи перестал работать, и мне опять понадобилось вооружаться дизассемблером, разрабатывать конвертер из TE в PE и отвечать на те же вопросы: где находится цифровая подпись, кто именно ее проверяет и как сделать так, чтобы проверка всегда заканчивалась успехом.
Если вас интересуют ответы и описание процесса их поиска — прошу под кат.
Краткая предыстория
Защита, о взломе которой пойдет речь в этом тексте, была введена HP около полутора лет назад, и (насколько мне известно) до сих пор считалась надежной. Пользователи ноутбуков HP с защищенным ей UEFI периодически наведывались на форум bios-mods.com в надежде на помощь в избавлении от белых списков оборудования или открытии скрытых настроек UEFI Setup, но с той же периодичностью получали ответ — защита стойкая и ничего не сделать. В начале лета 2014 года одному из администраторов bios-mods это надоело и он решил взять инициативу по взлому в свои руки. Процесс пошел, но крайне медленно, и результата публичного до сих пор не появилось, а он очень нужен ремонтникам, которые рады бы ставить на эти ноутбуки другие процессоры/видеокарты/модемы, но не могут, т.к. для их правильной работы нужна модификация DXE-драйверов, а после нее прошивка не стартует. В итоге мне эта ерунда надоела и я решил отломать защиту самостоятельно, благо, как выяснилось, ничего радикально сложнее предыдущей защиты HP придумать не смогли.
Ликбеза не будет
Если вам непонятно, что тут происходит, прочтите предыдущую статью, там имеется краткое введение в курс дела и список используемых инструментов.
Что дано и что требуется найти
Имеется ноутбук HP, который на любое изменение в DXE-томе отзывается сначала перезагрузкой почти сразу после включения, а затем впадает в бесконечный цикл, моргая при этом лампочками на клавиатуре. Также имеется дамп его прошивки и добрые ребята-ремонтники, готовые потратить время на то, чтобы проверить очередную измененную прошивку на работоспособность.
Конечная цель — прошивка должна работать после модификаций.
Поиски
Снова открываем прошивку в UEFITool и смотрим на панель Messages. Благодаря опыту, полученному в прошлой статье, утилита теперь умеет отображать нестандартные данные в дереве (т.е. не теперь не обязательно распаковывать весь DXE-том, что добраться до них) и сохраняет их при пересборке тома:
Как и в прошлый раз, подозрительные данные оказываются в самом конце DXE-тома, но теперь можно достать их непосредственно из дерева, что и делаем, получаем файл key.bin, который затем открываем в Hex-редакторе:
И снова перед глазами предстает неизвестный кусок данных размером 100h (похожий на RSA2048 public key), но теперь у него есть сигнатура — $HSS. Как я уже писал, данные такого рода могут быть найдены либо по сигнатуре, либо по смещению внутри тома, либо по абсолютному адресу (с учетом того, что последний байт микросхемы SPI-flash оказывается при старте по адресу FFFFFFFFh). Проверим сначала первый вариант, воспользовавшись поиском текста в UEFITool:
Находим 4 вхождения искомой строки, одно из которых мы уже посмотрели (то, которое внутри Non-UEFI data), нужно разобраться с остальными. Первое оказывается в DXE-драйвере по имени BiosImageInterface, но он сам находится в томе, который призван проверять, так что наличие там основной проверки весьма маловероятно. Если в других местах не найдем — надо будет к нему вернуться.
Последние же два вхождения указывают две копии одного и того же PEI-модуля, первая из которых находится в упакованной копии PEI-тома (она может использоваться для автоматического восстановления поврежденного PEI-тома), а другая — в «настоящем» PEI-томе. Вот и наш подозреваемый, все указывает на то, что целостность проверяет именно он, и нужно достать его содержимое в файл hss.bin для дальнейшего анализа.
В прошлый раз на этом месте мы загрузили полученный файл в IDA и начали анализировать его, но в данном случае так смогут сделать только обладатели IDA 6.7 и выше, т.к. именно в 6.7 появилась нативная поддержка формата Terse Executable, в котором и хранится наш PEI-модуль. Формат этот разработан авторами спецификации UEFI PI для экономии места в кэше процессора, по факту это PE32 с сильно обрезанным заголовком, от которого оставили только жизненно необходимые для правильной загрузки образа поля. Более того, большая часть производителей использует в фазе PEI крайне примитивный загрузчик исполняемых файлов, который не поддерживает relocation'ы, и потому его базовый адрес должен совпадать с физическим адресом. Все это замечательно, конечно, но нам нужен не непонятный TE, а понятный PE32, поэтому пришлось написать конвертер из TE в PE, который пытается восстановить заголовок PE-файла по тем данным, которые получилось найти в TE-файле. Утилита писалась за полчаса и под конкретный файл, так что за правильность конвертирования любого наперед заданного TE-образа поручиться не могу.
Вот теперь у нас есть файл pe.bin и можно открывать его в IDA и вести анализ. В этот раз пойдем немного другим путем и будем начинать не с точки входа, а с того места, где встречается сигнатура $HSS. Идем в Search -> Text... (или нажимаем Alt+T), вводим $HSS, ставим галку Find all occurences и… ничего не находим. Думаем немного и понимаем, что у нас LittleEndian-машина, и $HSS при загрузке в регистр будет выглядеть как SSH$ или 53534824h. Ищем этот вариант и вуаля, одно вхождение:
Переходим двойным щелчком на сообщении и попадаем вот сюда:
А вот и проверка на то, что по адресу из EBX лежит 53534824h, и если оно там действительно лежит — в переменную на стеке сохраняется EBX + 20h, т.е. как раз адрес первого байта ключа, а если не лежит — к EBX прибавляется 10h и проверяется снова. Видно также, что из другой ветки кода в ту же переменную может попасть значение FFF3DF00h, которое является физическим адресом первого байта ключа. Можно это проверить, для чего достаточно от 100000000h отнять FFF3DF00h и отступить полученные C2100h байт от конца файла — как и предполагалось, мы оказываемся точно на начале блока с ключом. Будет ли ключ найден по сигнатуре или абсолютному адресу — зависит от BootMode. Если текущий режим 20h, т.е. BOOT_IN_RECOVERY_MODE — ключ будет найден по абсолютному адресу, иначе — по сигнатуре.
Смотрим дальше:
А дальше происходит вызов функции из какого-то специфичного для HP PPI, которая и осуществляет проверку. Если эта функция вернула 0 (т.е. EFI_SUCCESS), то мы уходим с этого экрана вниз на return, если же проверка не удалась, все опять зависит от BootMode, в случае BOOT_IN_RECOVERY_MODE происходит вызов функции из другого PPI (которая, я подозреваю, постарается восстановить DXE-том из Recovery-образа). Затем уже независимо от BootMode вызывается его одна функция из второго PPI, после чего в стек кладутся 6 и 0CF9h и выполняется вызов коротенькой функции, в нашем случае выглядящей так:
mov al, 6
mov dx, 0CF9h
out dx, al
Если вдруг не узнали в гриме — это самый распространенный ныне способ hard reset'а через IO-порты. А если ресет вдруг не случился, случится бесконечный цикл, который сразу за ним.
Ну вот, с причинами ресета разобрались, теперь надо его обойти, причем так, чтобы вернуть EFI_SUCCESS в EAX из той функции, в которой это все происходит. На мой взгляд, самый простой способ достичь желаемого — поменять первый text eax, eax на xor eax, eax, в результате и в EAX появится 0, и JGE сработает в 100% случаев, а ресет станет мертвым кодом.
Тестирование и заключение
Сказано-сделано, меняем 85 C0 -> 31 C0, записываем вместо ключа нули, собираем прошивку (не забыв, что у нас две копии этого файла и заменить нужно обе) и отправляем нашим ремонтникам на тестирование. Через 5 минут приходит сообщение: «запускается и работает, большое спасибо». Вот теперь можно снова садиться и писать статью про крутые вендорские защиты, которые сначала держатся полтора года, а потом снимаются за полтора часа.
Спасибо за внимание, удачных вам прошивок и модификаций.
Автор: CodeRush