
Radiografía de latencia para hardware no documentado
Una herramienta exploratoria para medir tiempos de MMIO: mide el tiempo de cualquier dirección física y disecciona el hardware a partir de la latencia.

Sorprendentemente útil para ingeniería inversa de hardware, fingerprinting de hipervisores, actividad de dispositivos por canales laterales, caracterización de registros, calibración de activadores maliciosos y creación de instrucciones de máquina absurdamente largas.
El primer megabyte de memoria física es un ejemplo fácil para empezar, incluso si su estructura ya es bien conocida.

Los límites de latencia nos permiten delimitar el espacio de direcciones, sin depender de los diseños reportados.
sudo ./mmiotic --start-address 0x0 --end-address 0x100000 --stride 4 --count 20 --quiet-mmap0x9FFFF a ~380 ciclos, luego el hueco de video de 128 KB, que se divide en dos en 0xB0000. El cambio en la varianza en 0x20000 puede indicar un page walk diferente para la página nula.A0000–AFFFF: lento y con jitter (996 ciclos, desviación estándar 327), enrutado a la Radeon encendida pero inactiva. B0000–BFFFF: rápido y rígido (780 ciclos, desviación estándar 3.2, lee ffffffff), no reclamado por nadie. Datos de aspecto similar, pero una brecha de varianza de 100x.C0000–FFFFF: /proc/iomem dice "System ROM", pero el escaneo muestra latencia de DRAM, no velocidad de flash: la BIOS está sombreada en la DRAM.Escanee el espacio de configuración del root complex (00:00.0, 4 KB) en busca de valores atípicos de latencia frente a un piso plano de ~675 ciclos:

La latencia del buzón es significativamente mayor que la de los datos circundantes, y los picos duplicados y las varianzas equivalentes nos permiten emparejar registros funcionalmente similares.
sudo ./mmiotic --start-address 0xf8000000 --end-address 0xf8001000 --stride 4 --count 2000xe4 (1218 ciclos, valor 80e3110b) y 0xa4 (1199 ciclos, valor deadbeef). e4 es un doorbell documentado; a4 no está documentado.e4 es público.deadbeef se lee como un centinela de error/no inicializado: mismo buzón, a4 no implementado o alimentado con entrada inválida.Ciertas latencias cuidadosamente seleccionadas a veces pueden usarse en escenarios de explotación inusuales (MCHAMMER, smiiiiiiiiiiiiiiii):

Aquí, una Radeon inactiva y sin controlador convierte una lectura alineada de 4 bytes en una parada de ~100,000 ciclos.
sudo ./mmiotic --start-address 0x90e00000 --end-address 0x90e00100 --stride 4 --count 50 — barra la cabecera del BAR de la GPU (0x90e00000, 256 KB). 0x90e00008 cuesta 110,152 ciclos; 0x90e00018, 16 bytes más adelante, vuelve a ser rápido a 1,523 — por registro, no por página.sudo ./mmiotic --start-address 0x90e00000 --end-address 0x90e40000 --stride 4 --count 1 --binary — --binary sondea en orden de inversión de bits, por lo que la apertura completa de 256 KB se cubre uniformemente después de 1,012 sondas, mapeando un bloque contiguo de 48 KB donde todos los registros son lentos.Idealmente, leer una dirección física sería una operación relativamente segura, pero mmiotic es especialmente bueno encontrando las excepciones por accidente:

Por ejemplo, en la plataforma anterior, una lectura de 1 byte en 0xdc5003b0 — offset 0x3b0 de un BAR de registros de iGPU Zen 4 sin controlador (Región 5, 0xdc500000, 512 KB) — reinicia el sistema de forma fiable.
sudo ./mmiotic --address 0xdc5003b0 --size 1 --count 1. Sin salida; la máquina se reinicia.0x3ac y 0x3b4 a cada lado leen 00000000 a ~3,200 ciclos y son inofensivos.0x3a0–0x3fc encuentra 7 dwords venenosos intercalados con 17 seguros.sudo busybox devmem 0xdc5003b0 32 y sudo dd if=/dev/mem bs=1 count=1 skip=$((0xdc5003b0)) tumban la máquina si prefieres saltarte el intermediario.[!WARNING] Algunas plataformas se reiniciarán cuando
mmioticsondee regiones de direcciones específicas. Ten cuidado con dónde lo usas.
| Opción | Nombres |
|---|---|
-b/-d/-f/-r | PCI --bus/--device/--function/--register — decodificado contra la base ECAM |
-o, --offset <n> | offset desde la base MMIO (omite la decodificación B/D/F) |
-A, --address <n> | una dirección física; establece la base de la región implícitamente |
-a/-z | --start-address/--end-address — un rango físico; implica --scan |
La base y el tamaño ECAM se detectan automáticamente desde ACPI MCFG (/sys/firmware/acpi/tables/MCFG). Se puede sobrescribir con -M, --mmio-base / -Z, --mmio-size.
| Opción | Efecto |
|---|---|
-S, --scan | recorre todas las direcciones en la región objetivo |
-t, --stride <n> | paso entre direcciones (por defecto: 4) |
-n, --limit <n> | se detiene después de esta cantidad de direcciones |
--binary | sondea en orden de inversión de bits — después de k sondas el rango tiene cobertura uniforme a granularidad ~range/k (ver la advertencia anterior) |
-x, --limited | rango de registros limitado (0x00–0xff vs 0x000–0xfff) |
-e, --skip | omite funciones que leen 0xffffffff en el offset 0 (dispositivos ausentes) |
-I, --iomem | escanea toda región de nivel superior que no sea System-RAM en /proc/iomem |
-R, --ioregion <name> | escanea regiones de /proc/iomem cuya etiqueta coincida con <name>, a cualquier profundidad |
--find-target/--find-longest convierten el escaneo en búsqueda:
| Opción | Efecto |
|---|---|
-F, --find-target <s> | encuentra una dirección cuyo tiempo de acceso alcance s segundos, luego escala el ancho (4b no alineado → 8/16/32/64/512b) para empujarlo más alto |
-G, --find-longest | misma búsqueda, pero agota todos los candidatos en lugar de detenerse en el primer acierto |
-B, --fallbacks <n> | candidatos llevados a la fase de escalado (por defecto: 10) |