Parsare lo ShimCache: il formato binario di Windows 10 e 11

5 min di lettura

TL;DR. Primo u32 = dimensione header, tipicamente da 0x30 a 0x36. A quell'offset ogni voce inizia con la magic di quattro byte 10ts, seguita da un header da 12 byte e un corpo a lunghezza variabile: percorso UTF-16LE (con prefisso di lunghezza, non terminato da null), mtime FILETIME, e un blob di dati finale. Il flag di esecuzione è stato rimosso in Windows 10.

Ogni release di Windows da XP in poi ha portato un layout AppCompatCache diverso. La variante Windows 10/11 è quella che gli investigatori vedono la maggior parte dei giorni, ed è la più indulgente una volta che sai dove sono le trappole. Questo è il percorso che avrei voluto avere quando ho provato per la prima volta a scriverne uno a mano.

Layout binario di una voce ShimCache Windows 10/11

Il blob che stai parsando

Inizia dal valore binario a:

HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\AppCompatCache\AppCompatCache

Estrailo dall'hive SYSTEM (vedi dove è memorizzato lo ShimCache). Quello che ottieni è un singolo blob, tipicamente da decine a centinaia di kilobyte. Header piccolo, poi una sequenza di voci, ognuna che inizia con una magic nota.

L'header

I primi quattro byte little-endian del blob sono l'offset della prima voce. Su Windows 10/11 è comunemente 0x30, 0x34, 0x35 o 0x36, a seconda della build. Alcuni hive portano 0x80 qui, il che segnala un layout Windows 8.x che usa ancora la firma di voce 10ts / 00ts.

Non hardcodare la dimensione dell'header. Leggi il primo u32 e valida che il byte a quell'offset sia l'inizio di una voce riconosciuta. Se né 10ts né 00ts compaiono all'offset dichiarato, scansiona in avanti per 10ts e riprendi da lì. Gli hive reali portano dati leggermente fuori spec, e un parser che molla al primo mismatch di byte è un parser che perde casi.

Una voce Windows 10/11, byte per byte

Ogni voce moderna inizia con la firma di quattro byte 10ts (0x31 0x30 0x74 0x73). Layout completo:

OffsetDimensioneCampo
0x004Magic, "10ts"
0x044Sequenza / sconosciuto, spesso ignorato
0x084Dimensione dati voce, byte che seguono questo campo
0x0C2Dimensione del percorso in byte (UTF-16LE)
0x0ENByte del percorso, UTF-16LE, senza terminatore
0x0E + N8Ora di ultima modifica, FILETIME (100-ns dal 1601-01-01 UTC)
0x16 + N4Dimensione dati finali
0x1A + NMDati finali, di solito vuoti o piccoli

La dimensione totale della voce è 0x0C + entry_data_size. La voce successiva inizia immediatamente dopo.

Due note pratiche:

  1. Il percorso è UTF-16LE e non è terminato da null. Usa il campo dimensione del percorso. Non leggere finché non incontri uno zero, ti mangi l'header della voce successiva.
  2. Il FILETIME è la mtime $STANDARD_INFORMATION del file. Non il tempo in cui il programma è girato. Non il tempo in cui la voce è stata inserita. La mtime del file, copiata direttamente da NTFS al momento dell'esame. Il post sulla prova di esecuzione copre perché conta.

Cosa è stato rimosso in Windows 10

Le versioni precedenti di Windows impacchettavano un flag Executed dentro ogni voce. Gli analisti Win7 e 8.x hanno imparato a leggerlo. Windows 10 lo ha rimosso. Windows 11 non lo ha rimesso. Non c'è alcun modo, all'interno della cache, di sapere se un binario sia stato effettivamente eseguito. Ti servono Prefetch, AmCache, o EVTX / Sysmon.

Per i layout legacy dei campi (da XP a 8.1), vedi formati legacy dello ShimCache.

Il parsing in pratica

Un parser che sopravvive alla revisione:

  1. Valida la magic di quattro byte all'offset dichiarato dall'header.
  2. Itera le voci leggendo il campo dimensione dati voce e avanzando.
  3. Decodifica il percorso come UTF-16LE usando il prefisso di lunghezza.
  4. Converte il FILETIME nel formato timestamp che il tuo tooling a valle preferisce. I millisecondi Unix sono comodi. ISO 8601 UTC è ciò che i report vogliono davvero.
  5. Espone l'etichetta del formato OS rilevato (Windows 10/11 vs 8.x vs 8.0) per il filtraggio a valle.
  6. Rigioca SYSTEM.LOG1 e SYSTEM.LOG2 prima di leggere il valore. Se il tuo parser non lo fa, sta leggendo un hive obsoleto.

Lo Shimcache Parser open source implementa i passi da 1 a 5 in Rust e li consegna al tuo browser come WebAssembly. Legge un singolo file di hive e non rigioca i log di transazione, quindi passa prima un hive dirty in rla.exe o Registry Explorer. Niente server, niente upload. Utile quando l'hive SYSTEM proviene da un ambiente regolamentato.

Letture aggiuntive

Articoli correlati