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.
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:
| Offset | Dimensione | Campo |
|---|---|---|
| 0x00 | 4 | Magic, "10ts" |
| 0x04 | 4 | Sequenza / sconosciuto, spesso ignorato |
| 0x08 | 4 | Dimensione dati voce, byte che seguono questo campo |
| 0x0C | 2 | Dimensione del percorso in byte (UTF-16LE) |
| 0x0E | N | Byte del percorso, UTF-16LE, senza terminatore |
| 0x0E + N | 8 | Ora di ultima modifica, FILETIME (100-ns dal 1601-01-01 UTC) |
| 0x16 + N | 4 | Dimensione dati finali |
| 0x1A + N | M | Dati finali, di solito vuoti o piccoli |
La dimensione totale della voce è 0x0C + entry_data_size. La voce successiva inizia immediatamente dopo.
Due note pratiche:
- 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.
- Il
FILETIMEè la mtime$STANDARD_INFORMATIONdel 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:
- Valida la magic di quattro byte all'offset dichiarato dall'header.
- Itera le voci leggendo il campo dimensione dati voce e avanzando.
- Decodifica il percorso come UTF-16LE usando il prefisso di lunghezza.
- Converte il
FILETIMEnel formato timestamp che il tuo tooling a valle preferisce. I millisecondi Unix sono comodi. ISO 8601 UTC è ciò che i report vogliono davvero. - Espone l'etichetta del formato OS rilevato (Windows 10/11 vs 8.x vs 8.0) per il filtraggio a valle.
- Rigioca
SYSTEM.LOG1eSYSTEM.LOG2prima 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
- AppCompatCacheParser di Eric Zimmerman. implementazione C# gold standard che copre ogni variante Windows.
- Shim Cache Parser di Mandiant. proof-of-concept Python originale, utile come referenza incrociata di formato.
- Velociraptor
Windows.Registry.AppCompatCache. artefatto VQL per raccolta live. - Windows 10/11 AppCompatCache deep dive su Ø Security. il write-up pubblico più approfondito del layout moderno.