
Raggi X della latenza per hardware non documentato
Uno strumento esplorativo per il timing di MMIO — cronometra qualsiasi indirizzo fisico e disseziona l' hardware dalla latenza.

Sorprendentemente utile per il reverse engineering dell'hardware, il fingerprinting degli hypervisor, l'analisi dell'attività dei dispositivi tramite canali laterali, la caratterizzazione dei registri, la calibrazione di trigger malevoli e la creazione di istruzioni macchina oscenamente lunghe.
Il primo megabyte di memoria fisica è un semplice esempio con cui iniziare, anche se la sua struttura è già ben nota.

I confini di latenza ci permettono di suddividere lo spazio degli indirizzi, senza fare affidamento sui layout riportati.
sudo ./mmiotic --start-address 0x0 --end-address 0x100000 --stride 4 --count 20 --quiet-mmap0x9FFFF a ~380 cy, poi
il buco video da 128 KB, che si divide in due a 0xB0000. Il cambiamento
di varianza a 0x20000 potrebbe indicare un page walk diverso per la pagina null.A0000–AFFFF: lenta e irregolare (996 cy, stdev 327), instradata verso la
Radeon accesa ma in idle. B0000–BFFFF: veloce e rigida (780 cy, stdev 3.2,
legge ffffffff), non reclamata da nessuno. Dati dall'aspetto simile, ma un
divario di varianza di 100x.C0000–FFFFF: /proc/iomem dice "System ROM", ma la scansione mostra latenza
DRAM, non velocità flash — il BIOS è shadowato nella DRAM.Scansiona uno spazio di configurazione del root complex (00:00.0, 4 KB) per individuare valori anomali di
latenza rispetto a un minimo piatto di ~675 cicli:

La latenza della mailbox è significativamente più alta rispetto ai dati circostanti, e i picchi duplicati e le varianze equivalenti ci permettono di associare registri funzionalmente simili.
sudo ./mmiotic --start-address 0xf8000000 --end-address 0xf8001000 --stride 4 --count 2000xe4 (1218 cy, valore 80e3110b) e
0xa4 (1199 cy, valore deadbeef). e4 è un doorbell documentato; a4 non è
documentato.e4 è pubblico.deadbeef viene letto come un sentinel non inizializzato/di errore — stessa
mailbox, a4 non implementato o alimentato con input non valido.Latenze accuratamente selezionate possono talvolta essere usate in insoliti scenari di exploitation (MCHAMMER, smiiiiiiiiiiiiiiii):

Qui una Radeon in idle e senza driver trasforma una lettura allineata da 4 byte in uno stallo di ~100.000 cicli.
sudo ./mmiotic --start-address 0x90e00000 --end-address 0x90e00100 --stride 4 --count 50 — spazza l'inizio della BAR della GPU (0x90e00000, 256 KB). 0x90e00008 costa 110.152 cy; 0x90e00018,
16 byte più avanti, è di nuovo veloce a 1.523 — per-registro, non per-pagina.sudo ./mmiotic --start-address 0x90e00000 --end-address 0x90e40000 --stride 4 --count 1 --binary — --binary sonda in ordine bit-reversal, quindi l'intera
apertura da 256 KB viene coperta uniformemente dopo 1.012 sonde, mappando un
blocco contiguo da 48 KB in cui ogni registro è lento.Idealmente, leggere un indirizzo fisico sarebbe un'operazione relativamente sicura, ma
mmiotic è particolarmente bravo a scoprire accidentalmente le eccezioni:

Ad esempio, sulla piattaforma di cui sopra, una lettura da 1 byte di 0xdc5003b0 — offset
0x3b0 di una BAR di registri della iGPU Zen 4 senza driver (Region 5, 0xdc500000, 512 KB)
— resetta il sistema in modo affidabile.
sudo ./mmiotic --address 0xdc5003b0 --size 1 --count 1. Nessun output; la macchina
si riavvia.0x3ac e 0x3b4 ai lati leggono 00000000 a ~3.200
cicli e sono innocui.0x3a0–0x3fc trova 7 dword velenose intervallate da 17
sicure.sudo busybox devmem 0xdc5003b0 32 e sudo dd if=/dev/mem bs=1 count=1 skip=$((0xdc5003b0))
mandano giù la macchina se preferisci saltare il passaggio intermedio.[!WARNING] Alcune piattaforme si resettano quando specifiche regioni di indirizzi vengono sondate da
mmiotic. Fai attenzione a dove lo usi.
La base e la dimensione ECAM vengono rilevate automaticamente da ACPI MCFG
(/sys/firmware/acpi/tables/MCFG). Puoi sovrascriverle con -M, --mmio-base /
-Z, --mmio-size.
--find-target/--find-longest trasformano la scansione in ricerca:
| Opzione | Effetto |
|---|---|
-F, --find-target <s> | trova un indirizzo il cui tempo di accesso raggiunge s secondi, quindi aumenta la larghezza (4b non allineati → 8/16/32/64/512b) per spingerlo più in alto |
b/d/f.o.l: 00:02:00.00.4 address: 00000000f0100000 min: 312 max: 480 stdev: 2.1 dynamic: 0 value: 8086abcd
-T aggiunge il suffisso cy ai conteggi di cicli e inserisce una coppia min/max di tempi scalati
(-N li stampa come nanosecondi grezzi):
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
Compilazione:
make
Viene eseguito come root per ottenere accesso a /dev/mem.
Se vedi: mmap ... Operation not permitted, prova ad avviare con iomem=relaxed per rimuovere CONFIG_STRICT_DEVMEM:
# /etc/default/grub
GRUB_CMDLINE_LINUX="iomem=relaxed nopat"
Poi esegui sudo update-grub e riavvia.
mmiotic è stato usato per portare avanti una varietà di ricerche interne ed esterne,
a volte in modi inaspettati. Alcuni esempi di applicazioni di mmiotic:
MCHAMMER: mmiotic calibra un'eccezione di machine check
ritardata per la consegna di precisione dei segnali MC# in ambienti
protetti.
smiiiiiiiiiiiiiiii:
mmiotic individua l'istruzione ad alta latenza per interrompere il
rendezvous SMM.
The Assembly Hall of Shame:
mmiotic è ampiamente usato per l'importante problema della deottimizzazione
delle prestazioni.
mmiotic è un progetto di ricerca di Christopher Domas (@xoreaxeaxeax).
| Opzione | Nomi |
|---|
-b/-d/-f/-r | PCI --bus/--device/--function/--register — decodificati rispetto alla base ECAM |
-o, --offset <n> | offset dalla base MMIO (salta la decodifica B/D/F) |
-A, --address <n> | un singolo indirizzo fisico; imposta implicitamente la base della regione |
-a/-z | --start-address/--end-address — un intervallo fisico; implica --scan |
| Opzione | Effetto |
|---|
-S, --scan | percorre tutti gli indirizzi nella regione target |
-t, --stride <n> | passo tra gli indirizzi (predefinito: 4) |
-n, --limit <n> | si ferma dopo questo numero di indirizzi |
--binary | sonda in ordine bit-reversal — dopo k sonde l'intervallo ha una copertura uniforme con granularità ~range/k (vedi l'avviso sopra) |
-x, --limited | intervallo di registri limitato (0x00–0xff vs 0x000–0xfff) |
-e, --skip | salta le funzioni che leggono 0xffffffff all'offset 0 (dispositivi assenti) |
-I, --iomem | scansiona ogni regione di primo livello non–System-RAM in /proc/iomem |
-R, --ioregion <name> | scansiona le regioni di /proc/iomem la cui etichetta corrisponde a <name>, a qualsiasi profondità |
-G, --find-longest| stessa ricerca, ma esaurisce ogni candidato invece di fermarsi al primo risultato |
-B, --fallbacks <n> | candidati portati nella fase di escalation (predefinito: 10) |
| Opzione | Effetto |
|---|
-s, --size <n> | byte per accesso: 1 / 2 / 4 (predefinito) / 8 / 16 (XMM) / 32 (YMM) / 64 (ZMM) / 512 (fxrstor). Le dimensioni > 4 sono fuori specifica ma funzionano sull'hardware testato; le larghezze AVX richiedono AVX / AVX-512F |
-c, --count <n> | campioni per indirizzo (predefinito: 1); viene riportato il minimo |
-L, --lock | misura il tempo di un RMW con lock: lock xadd (dimensioni 1/2/4/8) o lock cmpxchg16b (dimensione 16). Riscrive il valore |
-g, --gather | misura il tempo di una gather vettorizzata; --size 16/32/64 seleziona XMM/YMM/ZMM (AVX-512) |
-w, --write-read | cattura la latenza di scrittura misurando il tempo di una lettura che deve osservare una scrittura posted precedente — vedi sotto |
-E, --enter (--enter-2, --enter-3) | genera una raffica di fino a 30 accessi da un singolo enter $0, $31 — vedi sotto |
-k, --continue | in caso di fault fxrstor, scarta quel campione e continua la scansione |
| Opzione | Effetto |
|---|
-C, --min-cycles <n> | stampa solo i risultati con min_cycles >= n |
-T, --time | mostra i nanosecondi accanto ai cicli (rileva la frequenza TSC) |
-N, --nanosecond | con --time, stampa i nanosecondi grezzi, senza scala automatica delle unità |
-P, --progress | avanzamento della scansione in tempo reale su stderr |
-p, --processor <n> | fissa alla CPU logica n (predefinito: 0) — riduce il rumore del timer |
-u, --unbound | non fissare a un core |
--lazy-mmap | mappa in finestre da 2 GB su richiesta; richiesto per regioni > 2 GB |
--quiet-mmap | sopprime il rumore "mmap failed" dalle regioni inaccessibili |
| Campo | Significato |
|---|
b/d/f.o.l | bus/device/function, offset del registro, dimensione dell'accesso |
offset | offset dalla base MMIO — sostituisce b/d/f.o.l quando il target proviene da -o/-A/-a+-z |
address | indirizzo fisico completo a 64 bit |
min/max | accesso più veloce e più lento osservato, in cicli |
stdev | deviazione standard dei conteggi di cicli campionati |
dynamic | 1 se il valore è cambiato in qualsiasi punto tra i campioni |
value | valore letto — per dimensioni oltre 8 byte, i primi 32 byte come qword separate da spazi |
faults | aggiunto solo se un accesso ha generato un fault |