Данная статья посвящена тестированию возможности использования технологии Intel Processor Trace Technology (Intel PT) для записи трассировки в режиме System Management Mode (SMM).
Работа выполнена в рамках Summer Of Hack 2019. Автор работы: @sysenter_eip .
Большинство используемых инструментов были написаны другими людьми (в частности @d_olex , @aionescu ).
Результатом является всего лишь сочетание доступных инструментов с целью получения трассировки выполнения кода в режиме SMM для один конкретный материнская плата.
Однако материал может быть интересен тем, кто хочет повторить это для своей платформы или просто интересуется работой SMM.
Режим управления системой
SMM — это особый, привилегированный режим работы процессора x86, который доступен во время работы операционной системы, но совершенно невидим для нее.Он предназначен для низкоуровневого взаимодействия с оборудованием, управления питанием, эмуляции устаревших устройств, перехода в спящий режим (S3), доступа к TPM и многого другого.
Работает полностью изолированно от ОС.
Во время работы SMM операционная система полностью остановлена.
Программный код, исполняемый в этом режиме, хранится во SPI-Flash-памяти материнской платы и является частью прошивки UEFI BIOS. Переход в режим SMM осуществляется с помощью специальных прерываний SMI (System Management Interrupt).
Для использования в нулевом кольце (т.е.
из ядра ОС) доступен один из вариантов этого прерывания — прерывание прикладного уровня SMI (Software SMI).
Далее мы поговорим об этих прерываниях.
Благодаря своей высокой привилегированности SMM представляет особый интерес для исследований в области безопасности.
Компрометация SMM приводит к серьезным нарушениям целостности и конфиденциальности всей системы, а также в большинстве случаев позволяет внедрить в прошивку UEFI BIOS вредоносный код, который не может быть удален или обнаружен операционной системой.
Трассировка процессора Intel
Одним из подводных камней процесса отладки различных высоконагруженных приложений являются накладные расходы — стоимость средств отладки.Их можно уменьшить с помощью аппаратного решения.
Пятое поколение процессоров Intel (Broadwell) подарило миру такую технологию, как Intel Processor Trace. Насколько это полезно? Intel PT позволяет получить полный поток выполнения (Control Flow) отлаживаемого приложения с минимальными накладными расходами (<5%).
However, it supports multi-threading and can help identify race condition errors thanks to timestamps when recording the application trace. Undoubtedly, Intel PT technology opens up great opportunities for writing tools for searching for vulnerabilities in applications. Сегодня эта технология используется в различных инструментах трассировки, отладки и оценки покрытия кода — как в пользовательских приложениях, так и в приложениях уровня ядра.
Примеры инструментов можно посмотреть на сайте Интел .
Вариант фаззера AFL, использующий преимущества Intel PT, доступен в репозитории.
PTfuzzer .
Из последних проектов стоит обратить внимание на иптанализатор .
Однако мы не видели работ, посвященных использованию Intel PT в режиме SMM. Поскольку ничто не мешает нам использовать Intel PT в этом контексте, мы решили посмотреть, можно ли его использовать для отслеживания кода режима управления системой.
Подготовка к работе
От Руководство разработчика Intel Отсюда следует, что активировать трассировку Intel PT в SMM извне штатными средствами невозможно.Если он был активен при срабатывании SMI, процессор отключит его перед передачей управления точке входа обработчика SMI. Единственный способ активации — это добровольное включение обработчика SMI самим кодом.
Даже если обработчик изначально не предоставляет такую возможность, мы можем перехватить ее и активировать Intel PT вручную.
Однако нужно как-то определить, что система готова к записи трассировки (установлен адрес выходного буфера), а также отключить трассировку по окончании выполнения обработчика (выполнения инструкции RSM).
В противном случае процессор отключит всю систему.
Прежде всего, вам необходимо получить доступ к SMRAM (область оперативной памяти, в которой находится код, исполняемый в режиме SMM).
Поскольку эта область оперативной памяти защищена, мы не можем получить к ней доступ из операционной системы (даже через DMA это сделать невозможно).
Есть несколько вариантов развития событий:
- использовать известную уязвимость в SMM и получить от нее примитив чтения/записи.
Это может быть либо программная ошибка (уязвимость в самом обработчике SMI; как правило, в SMM, добавленном OEM-производителем, достаточно кода, поэтому уязвимости нередки), либо уязвимая конфигурация платформы (разблокировка/перемещение СМРАМ);
- пропатчим образ UEFI таким образом, что у нас будет интерфейс чтения и записи по произвольным адресам — бэкдор.
Для реализации этого варианта вам необходимо найти материнскую плату, на которой отключен Intel Boot Guard или имеются уязвимости, позволяющие его обойти.
Внедрение вашего кода в прошивку
Несмотря на то, что SMM-уязвимости встречаются в коде различных производителей время от время , будет лучше, если мы не будем на них полагаться.Нам интереснее отслеживать код на новых прошивках и соответственно пытаться найти в них уязвимости.
У нас уже была материнская плата GIGABYTE GA-Q270M-D3H с отключенным Intel Boot Guard, поэтому нам просто нужно было добавить бэкдор в SMM.
Рисунок 1. Испытательный стенд Уже существует фреймворк для «заражения» SMM и работы с бэкдором .
Он состоит из трёх компонентов: драйвера UEFI на C, «инфектора» и клиентского скрипта на Python. Чтобы это работало, вам необходимо извлечь собственный драйвер DXE (вы можете сделать это, используя UEFITool ) и обработайте его возбудителем.
Оригинальный модуль был заменен на «улучшенный», а прошивка залита в SPI-память (для удобства прошивки SPI-флешка была отпаяна от платы).
Рисунок 2. Микросхема памяти SPI-Flash Система успешно загрузилась, и теперь у нас есть полный доступ к SMRAM из Python (пример использования включен в бэкдор).
Поскольку клиентский скрипт бэкдора основан на ЧИПСЕК , нужно дать ему доступ в режим ядра (мы использовали драйвер RWEverything, кому-то будет удобно использовать собственный драйвер CHIPSEC с отключенной в системе проверкой подписи).
Проверить работу бэкдора можно, запросив дамп SMRAM.
После выполнения этой команды будет создан файл SMRAM_dump_cb000000_cb7fffff.bin, содержащий текущее состояние SMRAM. Значения cb000000 и cb7fffff — это соответственно физические адреса начала и конца SMRAM.$ python SmmBackdoor.py -d
Работа с дампом SMRAM
Дамп SMRAM можно загрузить в дизассемблер или передать скрипту для анализа.smram_parse.py , который доставит нам много полезной информации.
Самым важным для нас будут адреса точек входа SMI. Это адреса функций, которым будет передаваться управление при срабатывании SMI. Каждый процессор имеет свою собственную точку входа.
Рисунок 3. Вывод скрипта smram_parse Давайте посмотрим на их код. Поскольку SMM начинает свое выполнение в 16-битном реальном режиме (при этом первые 4 ГБ ОЗУ отражаются в виртуальном пространстве), первое, что делает код, — это переключается в 64-битный режим.
В этом случае вся SMRAM доступна с правами на запись и выполнение, поскольку был создан только один сегмент (есть ли производители, которые делают по-другому?).
Мы не хотим писать 16-битный код или готовить все необходимое для перехода в 64-битный режим самостоятельно, поэтому поместим наш перехватчик непосредственно перед вызовом функции SMI-менеджера (эта функция определяет, какой SMM-модуль нужно делегировать выполнение в зависимости от того, какая служба была вызвана или какое событие произошло).
Рисунок 4. Где разместить крючок Самый простой способ перехватить управление — подменить адрес диспетчера на наш.
Все точки входа имеют одинаковый код, поэтому патч необходимо повторить для каждой.
Примечание: По поводу расположения кода перехватчика.
Поскольку структура SMRAM нам до конца не известна, мы выбрали случайный кусок обнулённой памяти рядом с одной из точек входа, куда поместили код перехватчика.
Лучшим вариантом будет добавить в прошивку собственный SMM-модуль, который UEFI легально поместит в SMRAM, чтобы не волноваться, что что-то важное будет перезаписано нашим кодом.
Реализация перехватчика SMI Manager
Укажем, что именно мы хотим делать внутри нашего перехватчика.
Сначала нам нужно определить, включен ли Intel PT, прежде чем переходить к SMM. Из документации Intel известно, что каждый процессор имеет свою базу SMBASE (MSR 0x9E) и собственное пространство для хранения состояния процессора (область SMM Save State) на момент перехода на SMM.
Рисунок 5. Схема компоновки SMBASE
Определение состояния Intel PT
Состояние сохранения SMM должно сохранять значение регистра MSR IA32_RTIT_CTL, который отвечает за управление трассировкой Intel PT. К сожалению, в руководстве Intel не указано, где процессор хранит состояние бита IA32_RTIT_CTL.TraceEn на момент перехода в SMM (включена ли трассировка, бит нулевой).Однако мы можем определить это сами, дважды сбросив SMM Save State: с включенной трассировкой и без.
Мы использовали инструмент ВинИПТ чтобы активировать трассировку процесса интерпретатора Python (pid 1337 ), выделив 2^ 12 (4096) байт в буфер трассировки, а затем выполнил скрипт SmmBackdoor.py внутри интерпретатора (аргумент 0 - это флаги, для нас они не важны, так как в SMM вам все равно придется принудительно ставить настройки трассировки).
$ ipttool.exe --start 1337 12 0
Сравнивая снимки SMRAM, мы определили расположение регистра IA32_RTIT_CTL в структуре SMM Save State. Он хранится по смещению SMBASE+0xFE3C. Состояние бита IA32_RTIT_CTL.TraceEn является основным условием повторной активации Intel PT в SMM. Поле с этим смещением помечено как «Зарезервировано» в Руководстве разработчика Intel.
Рисунок 6. Отметьте, что поля зарезервированы
Написание шеллкода
Мы не хотели самостоятельно настраивать Intel PT внутри SMM, так как это усложнило бы наш шеллкод (например, находясь в SMM, было бы сложно выделить большой кусок оперативной памяти, чтобы она не использовалась самой операционной системой ).Поэтому мы решили использовать уже настроенный трассировщик и просто «пропустить» его внутрь SMM, тем более, что в нем уже есть функция сохранения трассировки в файл.
Поскольку для этих целей мы использовали WinIPT, который на тот момент не поддерживал трассировку кода ядра (CPL == 0), то было очевидно, что даже если трассировка будет включена в SMM, в журнале ничего не появится, так как код SMM был выполняется при CPL=0 .
Нам необходимо доработать некоторые фильтры, чтобы трассировщик мог работать на протяжении всего времени нахождения в SMM. Перечислим все, что необходимо проверить и установить:
- Трассировка с CPL=0 должна быть включена.
- Трассировка при CPL> 0 должна быть включена (необязательно).
- Диапазоны IP-адресов, разрешенные для записи событий, необходимо отключить.
- IA32_RTIT_STATUS.PacketByteCnt необходимо сбросить.
- Фильтрация CR3 должна быть отключена.
Нам необходимо обнулить этот счетчик, иначе при обработке трассировки момент входа в SMM будет пропущен и трассировка начнется со случайного места при естественной генерации PSB. Ниже приведен шеллкод, который мы использовали: sub rsp, 0x18 ; this will align stack at 16 byte boundary (in case SMM
; code uses align dependent instructions)
mov qword ptr ss:[rsp+0x10], rcx ; need to save rcx for SMI_Dispatcher
mov ecx, 0x9E ; MSR_IA32_SMBASE
rdmsr
test byte ptr ds:[rax+0xFE3C], 0x1 ; Save State area contains saved
; IA32_RTIT_CTL.TraceEn
je short @NoTrace
call @Trace_Enable
mov rcx, qword ptr ss:[rsp+0x10] ; SMI_Dispatcher is __fastcall
; (first argument in rcx)
mov eax, 0xCB7DDAA4 ; original SMI_Dispatcher !!!!!!!!!!!!!!!!!!!!!
call rax
call @Trace_Disable
add rsp, 0x18
ret
@NoTrace:
mov rcx, qword ptr ss:[rsp+0x10] ; SMI_Dispatcher is __fastcall
mov eax, 0xCB7DDAA4 ; original SMI_Dispatcher !!!!!!!!!!!!!!!!!!!!!
call rax
add rsp, 0x18
ret
@Trace_Disable:
mov ecx, 0x570 ; IA32_RTIT_CTL
rdmsr
mov rax, qword ptr ss:[rsp+0x10] ; restore IA32_RTIT_STATUS
wrmsr
mov ecx, 0x571 ; IA32_RTIT_STATUS
rdmsr
mov rax, qword ptr ss:[rsp+0x8] ; restore IA32_RTIT_CTL
wrmsr
ret
@Trace_Enable:
mov ecx, 0x571 ; IA32_RTIT_STATUS
rdmsr
mov qword ptr ss:[rsp+0x8], rax ; save IA32_RTIT_STATUS
and edx, 0xFFFF0000 ; IA32_RTIT_STATUS.PacketByteCnt = 0
wrmsr
mov ecx, 0x570 ; IA32_RTIT_CTL
rdmsr
mov qword ptr ss:[rsp+0x10], rax ; save IA32_RTIT_CTL
and eax, 0xFFFFFFBF ; IA32_RTIT_CTL.CR3Filter = 0
or eax, 0x5 ; IA32_RTIT_CTL.OS = 1; IA32_RTIT_CTL.User = 1;
and edx, 0xFFFF0000 ; IA32_RTIT_CTL.ADDRx_CFG = 0
wrmsr
ret
Этот код необходимо поместить в SMRAM, а переход на SMI-менеджер пропатчить для перехода на наш код. Все это делается с помощью SmmBackdoor.
Работа с маршрутом
Перехватчик SMI Manager позволил нам записать первую трассировку кода от SMM. Следующая команда может запросить ВинИПТ сохраните маршрут в файл: $ ipttool.exe --trace 1337 trace_file_name
Отключите трассировку процесса:
$ ipttool.exe --stop 1337
Можно попробовать разобрать трейс с помощью утилиты свалка от либипт .
$ ptdump.exe --no-pad .
/examples/trace_smm_handler_33 > .
/examples/trace_smm_handler_33_pt_dump.txt
Пример вывода:
Рисунок 7. След первых инструкций SMM Мы можем видеть некоторые адреса, но использовать эту информацию крайне сложно, так как она очень низкого уровня.
Для получения более читабельного вида существует утилита ptxed (из libipt), который преобразует трассировку в журнал выполненных ассемблерных инструкций.
Разумеется, нам придется предоставить утилите дамп памяти SMRAM, поскольку журнал IPT не содержит информации о значениях ячеек памяти или о том, какие инструкции были выполнены; он содержит только информацию о том, какие изменения произошли в потоке управления.
$ ptxed.exe --pt tracesmm_12 --raw SMRAM_dump_cb000000_cb7fffff.bin:0xcb000000 > tracesmm_12_ptasm
Рисунок 8. Листинг сборки, соответствующий журналу IPT Так выглядит гораздо лучше, но если код содержит цикл, вывод будет забит теми же инструкциями.
Определение покрытия кода с помощью трассировки
Для визуализации покрытия мы выбрали плагин Маяк для IDA Pro, использующего формат drcov. Готовых инструментов не нашлось, поэтому доработали ptxed так что он генерирует файл покрытия во время работы.Исправлено ptxed доступно в репозитории .
Посмотрите историю коммитов, чтобы определить, что именно было добавлено.
После завершения выполнения ptxed появится файл SMRAM_dump_cb000000_cb7fffff.bin.log, который будет содержать информацию о покрытии в формате drcov. Примечание: Есть небольшая проблема с синхронизацией дизассемблера на первом PSB. По не совсем понятной причине, если PSB генерируется раньше PGE (счетчик обнуляется до повторной активации трассировки), то ptxed не могу с ним синхронизироваться.
Чтобы обойти эту проблему, мы сделали небольшой патч.
Не ясно, является ли это проблемой само по себе ptxed , или мы делаем что-то не так, сбрасывая IA32_RTIT_STATUS.PacketByteCnt.
Рисунок 9. Патч, позволяющий использовать блок PSB, расположенный непосредственно перед PGE Сгенерированные файлы покрытия можно загрузить в IDA Pro и получить красивую подсветку, а также статистику процента покрытия для каждой функции.
Рисунок 10. Плагин IDA Pro Lighthouse с информацией о покрытии кода.
Примечание: Плагин Lighthouse немного странно работает на не полностью проанализированных базах данных (не размечается исполняемый код, не создаются функции).
Мы связали эту «проблему» с функцией get_instructions_slice в \lighthouse\metadata.py, которая возвращает 0 инструкций даже для адреса, где функция была создана вручную.
Кажется, плагин использует кеш и игнорирует вновь определенный код. Это можно обойти, вызвав Reanalyze в программе и повторно открыв IDB. Только после этого плагин сможет увидеть новый код и начать его учитывать.
Поскольку эта проблема очень раздражает в случае дампов SMRAM (которые при первой загрузке почти полностью представляют собой неопределенный код), мы внесли одно небольшое изменение в код Lighthouse, чтобы можно было быстрее определять новый код вручную.
Рис.
11. Добавлено сообщение журнала, помогающее определить новый код.
Поддержка Linux
Поскольку все наши тесты проводились на Windows 10 x64 (нам нужен был ipt.sys, который появился в Windows October Creators Update 2018), скажем несколько слов о возможности реализации чего-то подобного в Linux.- Есть модуль перформанс Ядро Linux, которое может выполнять действия, аналогичные WinIPT (ipt.sys), включая возможность трассировки кода в режиме ядра.
- Поскольку интерфейс бэкдора SMM основан на кроссплатформенной платформе CHIPSEC, наш патч будет работать в системе Linux без каких-либо модификаций.
Заключение
Мы успешно выполнили задачу по получению трассировки кода, исполняемого в SMM, с использованием технологии Intel Processor Trace. Аналогичного результата можно было бы добиться, используя дорогостоящее оборудование и программное обеспечение, которое продается не каждому.Нам было достаточно иметь под рукой одну материнскую плату и SPI-программатор.
Скорость прослеживания маршрута действительно впечатляет, а к точности результата претензий нет. Мы надеемся, что эта статья поможет другим воспользоваться преимуществами технологии Intel PT для исследования и поиска уязвимостей в коде SMM. Адаптация нашей работы к другим материнским платам не должна вызвать затруднений (не забываем про Intel Boot Guard).
Главное — полностью понимать, как это работает. Самое сложное — определить, как перехватить диспетчер SMI, и написать шеллкод для перехватчика.
В нашей версии использовались «зашитые» адреса, поэтому следует аккуратно переносить шеллкод в другую систему.
Все используемые инструменты и скрипты доступны в репозитории на GitHub .
Теги: #информационная безопасность #обратный инжиниринг #smm #intel pt #трассировка процессора #UEFI BIOS #ipt #ipttool #режим управления системой
-
Netgraph Против Файловых Серверов Plan9
19 Oct, 24 -
Sisoft Сандра Своими Руками
19 Oct, 24 -
Microsoft Открыла Аккаунт На Github
19 Oct, 24 -
Выбор Javascript И Css
19 Oct, 24