
Рентген задержек для недокументированного оборудования
Экспериментальный инструмент для MMIO-тайминга — измеряйте время обращения к любому физическому адресу и извлекайте информацию об оборудовании из задержек.

Неожиданно полезен для реверс-инжиниринга оборудования, снятия отпечатков гипервизоров, побочного анализа активности устройств, характеризации регистров, калибровки вредоносных триггеров и создания чудовищно длинных машинных инструкций.
Первый мегабайт физической памяти — простой пример для начала, даже если его структура уже хорошо известна.

Границы задержек позволяют разделить адресное пространство, не полагаясь на заявленную раскладку.
sudo ./mmiotic --start-address 0x0 --end-address 0x100000 --stride 4 --count 20 --quiet-mmap0x9FFFF при ~380 тактов, затем 128-КБ видео-дыра, которая раздваивается на 0xB0000. Изменение дисперсии на 0x20000 может указывать на другой обход страниц для нулевой страницы.A0000–AFFFF: медленный и дрожащий (996 тактов, stdev 327), направлен на включённый, но простаивающий Radeon. B0000–BFFFF: быстрый и стабильный (780 тактов, stdev 3.2, читается ffffffff), никем не занят. Похожие данные, но разрыв по дисперсии в 100 раз.C0000–FFFFF: /proc/iomem сообщает «System ROM», но сканирование показывает задержку DRAM, а не скорость флеша — BIOS затенён в DRAM.Просканируйте конфигурационное пространство корневого комплекса (00:00.0, 4 КБ) на предмет выбросов задержки на фоне ровного уровня ~675 тактов:

Задержка mailbox значительно выше окружающих данных, а повторяющиеся пики и эквивалентные дисперсии позволяют сопоставить функционально схожие регистры.
sudo ./mmiotic --start-address 0xf8000000 --end-address 0xf8001000 --stride 4 --count 2000xe4 (1218 тактов, значение 80e3110b) и 0xa4 (1199 тактов, значение deadbeef). e4 — документированный doorbell; a4 не документирован.e4.deadbeef читается как неинициализированный/ошибочный sentinel — тот же mailbox, a4 не реализован или получает недопустимые входные данные.Тщательно подобранные задержки иногда можно использовать в необычных сценариях эксплуатации (MCHAMMER, smiiiiiiiiiiiiiiii):

Здесь простаивающий Radeon без драйвера превращает одно выровненное 4-байтовое чтение в простой примерно на 100 000 тактов.
sudo ./mmiotic --start-address 0x90e00000 --end-address 0x90e00100 --stride 4 --count 50 — просканируйте начало GPU BAR (0x90e00000, 256 КБ). 0x90e00008 стоит 110 152 такта; 0x90e00018, на 16 байт дальше, снова быстрый — 1 523 такта — то есть задержка привязана к регистру, а не к странице.sudo ./mmiotic --start-address 0x90e00000 --end-address 0x90e40000 --stride 4 --count 1 --binary — --binary опрашивает в порядке обращения битов, поэтому всё 256-КБ окно равномерно покрывается после 1 012 проб, выявляя смежный блок 48 КБ, где каждый регистр медленный.В идеале чтение физического адреса должно быть относительно безопасной операцией, но mmiotic особенно хорош в случайном обнаружении исключений:

Например, на указанной платформе 1-байтовое чтение 0xdc5003b0 — смещение 0x3b0 BAR регистров неактивированного iGPU Zen 4 (Region 5, 0xdc500000, 512 КБ) — надёжно перезагружает систему.
sudo ./mmiotic --address 0xdc5003b0 --size 1 --count 1. Никакого вывода; машина перезагружается.0x3ac и 0x3b4 по обе стороны читаются как 00000000 при ~3 200 тактов и безвредны.0x3a0 до 0x3fc обнаруживает 7 ядовитых dword вперемешку с 17 безопасными.sudo busybox devmem 0xdc5003b0 32 и sudo dd if=/dev/mem bs=1 count=1 skip=$((0xdc5003b0)) вырубают машину, если вы предпочитаете без посредников.[!WARNING] Некоторые платформы перезагружаются при обращении
mmioticк определённым областям адресов. Будьте осторожны, где вы это используете.
База и размер ECAM автоматически определяются из ACPI MCFG (/sys/firmware/acpi/tables/MCFG). Переопределите с помощью -M, --mmio-base / -Z, --mmio-size.
--find-target/--find-longest превращают сканирование в поиск:
| Опция | Эффект |
|---|---|
-F, --find-target <s> | найти адрес, время обращения к которому достигает s секунд, затем увеличивать ширину (невыровненный 4б → 8/16/32/64/512б), чтобы поднять его выше |
-G, --find-longest |
b/d/f.o.l: 00:02:00.00.4 address: 00000000f0100000 min: 312 max: 480 stdev: 2.1 dynamic: 0 value: 8086abcd
-T добавляет к количеству тактов суффикс cy и вставляет масштабированную пару min/max времени (-N выводит их как сырые наносекунды):
b/d/f.o.l: 00:02:00.00.4 address: 00000000f0100000 min: 312 cy max: 480 cy ( 104 ns/ 160 ns) stdev: 2.1 dynamic: 0 value: 8086abcd
Сборка:
make
Запускается от root для доступа к /dev/mem.
Если вы видите: mmap ... Operation not permitted, попробуйте загрузиться с iomem=relaxed, чтобы снять CONFIG_STRICT_DEVMEM:
# /etc/default/grub
GRUB_CMDLINE_LINUX="iomem=relaxed nopat"
Затем выполните sudo update-grub и перезагрузитесь.
mmiotic использовался для продвижения разнообразных внутренних и внешних исследований, иногда неожиданными способами. Некоторые примеры применения mmiotic:
MCHAMMER: mmiotic калибрует отложенное исключение machine check для точной доставки сигналов MC# в защищённые среды.
smiiiiiiiiiiiiiiii:
mmiotic находит инструкцию с высокой задержкой для срыва SMM-рандеву.
The Assembly Hall of Shame:
mmiotic широко используется для важной проблемы деоптимизации производительности.
mmiotic — исследовательский проект Кристофера Домаса (@xoreaxeaxeax).
| Опция | Названия |
|---|
-b/-d/-f/-r | PCI --bus/--device/--function/--register — декодируется относительно базы ECAM |
-o, --offset <n> | смещение от базы MMIO (пропускает декодирование B/D/F) |
-A, --address <n> | один физический адрес; неявно задаёт базу области |
-a/-z | --start-address/--end-address — физический диапазон; подразумевает --scan |
| Опция | Эффект |
|---|
-S, --scan | пройти по всем адресам в целевой области |
-t, --stride <n> | шаг между адресами (по умолчанию: 4) |
-n, --limit <n> | остановиться после этого количества адресов |
--binary | опрос в порядке обращения битов — после k проб диапазон имеет равномерное покрытие с гранулярностью ~range/k (см. предупреждение выше) |
-x, --limited | ограниченный диапазон регистров (0x00–0xff вместо 0x000–0xfff) |
-e, --skip | пропускать функции, которые читают 0xffffffff по смещению 0 (отсутствующие устройства) |
-I, --iomem | сканировать каждую верхнеуровневую область, не являющуюся System-RAM, в /proc/iomem |
-R, --ioregion <name> | сканировать области /proc/iomem, чья метка соответствует <name>, любой глубины |
| тот же поиск, но перебирает всех кандидатов, а не останавливается на первом совпадении |
-B, --fallbacks <n> | кандидаты, переносимые в фазу эскалации (по умолчанию: 10) |
| Опция | Эффект |
|---|
-s, --size <n> | байт на обращение: 1 / 2 / 4 (по умолчанию) / 8 / 16 (XMM) / 32 (YMM) / 64 (ZMM) / 512 (fxrstor). Размеры > 4 выходят за рамки спецификаций, но работают на протестированном оборудовании; ширины AVX требуют AVX / AVX-512F |
-c, --count <n> | выборок на адрес (по умолчанию: 1); сообщается минимум |
-L, --lock | замер заблокированной RMW: lock xadd (размеры 1/2/4/8) или lock cmpxchg16b (размер 16). Записывает значение обратно |
-g, --gather | замер векторного gather; --size 16/32/64 выбирает XMM/YMM/ZMM (AVX-512) |
-w, --write-read | фиксирует задержку записи, измеряя чтение, которое должно увидеть предыдущую posted-запись — см. ниже |
-E, --enter (--enter-2, --enter-3) | выполнить серию до 30 обращений из одной инструкции enter $0, $31 — см. ниже |
-k, --continue | при сбое fxrstor отбросить эту выборку и продолжить сканирование |
| Опция | Эффект |
|---|
-C, --min-cycles <n> | выводить только результаты с min_cycles >= n |
-T, --time | показывать наносекунды вместе с тактами (определяет частоту TSC) |
-N, --nanosecond | с --time выводить сырые наносекунды без масштабирования единиц |
-P, --progress | живой прогресс сканирования в stderr |
-p, --processor <n> | привязка к логическому CPU n (по умолчанию: 0) — снижает шум таймера |
-u, --unbound | не привязываться к ядру |
--lazy-mmap | отображать окна по 2 ГБ по требованию; требуется для областей > 2 ГБ |
--quiet-mmap | подавлять сообщения «mmap failed» от недоступных областей |
| Поле | Значение |
|---|
b/d/f.o.l | шина/устройство/функция, смещение регистра, размер доступа |
offset | смещение от базы MMIO — заменяет b/d/f.o.l, когда цель задана через -o/-A/-a+-z |
address | полный 64-битный физический адрес |
min/max | самое быстрое и самое медленное обращение, в тактах |
stdev | стандартное отклонение замеренных количеств тактов |
dynamic | 1, если значение менялось в какой-либо момент между выборками |
value | прочитанное значение — для размеров более 8 байт первые 32 байта в виде qword, разделённых пробелами |
faults | добавляется, только если обращение вызвало сбой (fault) |