Capire il USN Journal di NTFS ($UsnJrnl:$J)

Un'introduzione pratica al Update Sequence Number Journal di NTFS: cos'è, com'è strutturato su disco e perché è così prezioso nella forense Windows.

Di 6 min di lettura

L'Update Sequence Number Journal di NTFS è l'artefatto a cui mi rivolgo per primo su qualsiasi host Windows quando devo sapere cosa ha toccato il disco e quando. È abilitato di default su ogni volume NTFS che Windows ha distribuito da Vista in poi, quasi nessuno lo disabilita e quasi nessuno lo legge prima che qualcosa vada storto. È questa asimmetria a renderlo utile.

Ogni creazione, eliminazione, rinomina, troncamento, cambio di attributo e chiusura di file sul volume appende un record a $UsnJrnl:$J. I record sono piccoli, qualche decina di byte ciascuno, e il file è un buffer circolare di dimensione fissa, quindi una macchina attiva trattiene grosso modo gli ultimi diversi milioni di operazioni. Su un desktop con l'allocazione predefinita di 32 MB sono pochi giorni. Su un file server dimensionato a 1 GB sono settimane.

Dove vive davvero il journal

Il journal non è un file regolare. È un flusso di dati alternativo chiamato $J, collegato al file di metadati \$Extend\$UsnJrnl alla root del volume. Due conseguenze che colpiscono chi non l'ha mai fatto:

  • Un drag-and-drop in Explorer, un xcopy o un robocopy copierà silenziosamente il flusso predefinito vuoto e ti ritroverai con un file da zero byte. Usa uno strumento che capisce gli ADS: FTK Imager, X-Ways, icat di The Sleuth Kit, RawCopy.exe, PowerForensics. Il post companion sull'estrazione di $J da un'immagine disco illustra ognuno.
  • Esiste un flusso fratello chiamato $Max sullo stesso file di metadati. Contiene solo metadati di dimensione e configurazione. Se per errore estrai $Max, non ottieni nulla di utile. Scegli sempre $J.

Il file stesso è sparso. NTFS riserva la dimensione massima configurata su disco ma lascia la regione iniziale inutilizzata a zero. Quando estrai $J da un'immagine, le prime centinaia di megabyte sono spesso tutte zeri prima di arrivare al primo record reale. Ogni parser ragionevole (usnrs, il USN-Journal-Parser di PoorBillionaire, MFTECmd, il parser WebAssembly di questa pagina) salta automaticamente gli zeri iniziali.

Struttura del record (USN_RECORD_V2)

Ogni record è una struttura USN_RECORD_V2. Microsoft documenta il layout in winioctl.h; il riepilogo campo per campo:

CampoByteSignificato
RecordLength4Dimensione totale del record incluso il nome file
Major / Minor version2 + 22.0 per tutto quello che vedrai in pratica
FileReferenceNumber8Entry MFT più sequenza del file
ParentFileReferenceNumber8Entry MFT più sequenza della directory padre
USN8Posizione di questo record nel journal
Timestamp8Windows FILETIME (tick da 100 ns dal 1601-01-01)
Reason4Bitmask che descrive cosa è cambiato
SourceInfo4Indizi (per es. DATA_MANAGEMENT per cambi guidati dall'OS)
SecurityId4Indice in $Secure:$SII
FileAttributes4Attributi NTFS standard
FilenameLength / Offset2 + 2Dove il nome UTF-16 vive nel record
FilenamenUTF-16 LE, senza terminatore nullo

La bitmask Reason è il campo che si guadagna il suo posto. È additiva, non esclusiva: un singolo record può portare FileCreate | DataExtend | Close insieme. Un ciclo di vita tipico di un file appare come FileCreate | DataExtendDataExtend | CloseBasicInfoChange | CloseFileDelete | Close. Ricostruire quella sequenza a posteriori è come capisci cos'è realmente accaduto a un file. Il post sui Reason code è la reference di campo per ogni bit.

Due campi che il lettore alle prime armi spesso sottovaluta:

  • FileReferenceNumber è la tua chiave di join. Raggruppa i record per questo e hai tutta la storia del file attraverso rinomine, cambi di attributo e cancellazione. Nota che i 16 bit alti sono un numero di sequenza: un file cancellato e un nuovo file allocato alla stessa entry MFT condivideranno l'entry ma differiranno nella sequenza.
  • ParentFileReferenceNumber è l'unica informazione di percorso nel record. Il nome del file è solo la foglia. Ti serve un $MFT parsato per risalire la catena dei padri fino a un percorso completo. Acquisisci entrambi, sempre.

Perché si guadagna il suo spazio nel DFIR

Due ragioni che tornano su quasi ogni incarico.

Primo, il journal sopravvive alla cancellazione. Una volta che un file è fuori dal namespace e la sua entry MFT è stata riutilizzata per qualcos'altro, il record della sua creazione, crescita, rinomina e cancellazione è ancora in $J finché il buffer circolare non ci passa sopra. Stesso discorso per i file svuotati dal cestino: i file $I del cestino vengono cancellati, ma il journal ricorda. Su una caccia al "questo file è mai esistito" il journal sopravviverà sia all'entry MFT sia ai metadati del cestino la maggior parte delle volte.

Secondo, è economico e auto-descrittivo. Un $J da 100 MB contiene tipicamente da cinque a dieci milioni di record che coprono giorni o settimane. Ogni record porta il proprio timestamp, Reason, riferimento file e padre. Puoi passarlo attraverso un parser in secondi e pivotare su qualsiasi campo. Non c'è un altro artefatto Windows a quel prezzo.

Quello che non ti dirà è chi. Nessun utente, nessun processo, nessuna riga di comando in nessun record. Per attaccare un attore, correla per timestamp con l'evento 4663 (object access, richiede SACL che quasi sicuramente non hai configurato) di Security.evtx, l'evento 11 di Sysmon (file create) o gli alberi di processo dell'evento 1 di Sysmon. Il journal risponde al cosa e al quando. Altri log rispondono al chi.

Dove si colloca nello stack degli artefatti

Il modello mentale che uso, ordinato da "stato attuale" a "storia passata":

  • $MFT — l'indice dello stato attuale. Ogni file e directory del volume, comprese alcune entry recentemente cancellate. Quattro timestamp per attributo, nessuna storia.
  • $UsnJrnl:$J — storia per-operazione, giorni-settimane. Quello di cui stai leggendo.
  • $LogFile — il log di transazioni per il crash recovery di NTFS. Minuti-ore di immagini grezze prima/dopo. Il post USN journal vs $MFT vs $LogFile dettaglia quando ricorrere a quale.

Affianca il journal a Prefetch, AmCache, Shimcache, file LNK e jump list e di solito puoi ricostruire al minuto cosa è girato, cosa è stato aperto e cosa ha toccato il disco.

Cosa viene dopo

Se non hai mai aperto un journal, il modo più rapido per costruirti l'intuizione è prenderne uno da una macchina di test e caricarlo nel parser in cima a questa pagina insieme alla $MFT corrispondente. Filtra per FileCreate e leggi il file come Windows l'ha scritto. I post che seguono in questa serie approfondiscono i flag Reason, la procedura di estrazione e i pattern che il journal espone per ransomware, esfiltrazione, timestomping e recupero di file cancellati.

Letture aggiuntive

  • Microsoft Learn — Change Journals e la reference USN_RECORD_V2.
  • usnrs di Airbus CERT — il parser open source di USN_RECORD_V2 più pulito in giro, e quello che alimenta il modulo WebAssembly di questo sito.
  • Brian Carrier, File System Forensic Analysis — il trattamento da lunghezza di libro sugli interni di NTFS che ancora non è stato sostituito.