Se passi tempo a leggere USN journal, la bitmask Reason diventa memoria muscolare come gli EventID EVTX. È il campo a 32 bit che NTFS imposta su ogni USN_RECORD_V2 per riassumere cosa è appena cambiato su un file. I bit sono additivi, non mutualmente esclusivi, che è la prima cosa da interiorizzare: un singolo record può portare FileCreate | DataExtend | Close e quella combinazione significa qualcosa di diverso da uno qualsiasi di quei bit da solo.
Quello che segue è la reference di campo che tengo su un post-it accanto al monitor.
I bit, nell'ordine in cui li incontrerai davvero
| Flag | Hex | Cosa significa in pratica |
|---|---|---|
DataOverwrite | 0x00000001 | Una regione del flusso dati principale è stata sovrascritta in loco |
DataExtend | 0x00000002 | Il flusso dati principale è cresciuto |
DataTruncation | 0x00000004 | Il flusso dati principale si è ridotto |
NamedDataOverwrite / Extend / Truncation | 0x10 / 0x20 / 0x40 | Stessi tre, ma su un flusso dati alternativo |
FileCreate | 0x00000100 | È stato creato un nuovo file o directory |
FileDelete | 0x00000200 | Il file è stato scollegato dal namespace |
EaChange | 0x00000400 | Attributi estesi cambiati |
SecurityChange | 0x00000800 | ACL/owner cambiati |
RenameOldName | 0x00001000 | La metà "prima" di una rinomina |
RenameNewName | 0x00002000 | La metà "dopo" di una rinomina |
IndexableChange | 0x00004000 | Flag indicizzabile commutato |
BasicInfoChange | 0x00008000 | Timestamp, attributi o flag di compressione riscritti |
HardLinkChange | 0x00010000 | Un hard link aggiunto o rimosso |
CompressionChange | 0x00020000 | Compressione NTFS commutata |
EncryptionChange | 0x00040000 | Stato EFS cambiato |
ObjectIdChange | 0x00080000 | Object ID impostato o cancellato |
ReparsePointChange | 0x00100000 | Reparse point aggiunto/cambiato/rimosso |
StreamChange | 0x00200000 | Un flusso dati alternativo aggiunto, rinominato o cancellato |
Close | 0x80000000 | L'handle che ha prodotto il cambio è stato rilasciato |
Il bit Close merita un paragrafo a parte perché leggerlo male sballa il tuo conteggio eventi di un ordine di grandezza. NTFS unisce operazioni successive sotto lo stesso handle ed emette record intermedi mentre fa flush, poi un record di riassunto finale con Close impostato una volta che l'handle è andato. I record intermedi sono reali, ma contano doppio. Quando triagi un journal parsato, filtra prima per i record con Close per deduplicare le operazioni a una riga per handle. Tira fuori i record senza Close solo quando serve granularità sub-handle.
Pattern che si ripetono abbastanza spesso da memorizzare
Una manciata di combinazioni Reason appare così frequentemente nelle indagini che smetti di leggerle carattere per carattere e inizi a leggerle come parole.
Nuovo file scritto da un'app. FileCreate | DataExtend poi altri record DataExtend | Close mentre il file cresce, chiuso da BasicInfoChange | Close. L'ultimo record è il file che si fa stampare l'mtime alla chiusura.
Rinomina tra directory. Due record che condividono lo stesso FileReferenceNumber: RenameOldName | Close poi RenameNewName | Close. Il riferimento padre differisce tra i due; quella differenza è come ricostruisci lo spostamento. Se filtri solo per uno dei bit di rename perdi metà della storia.
Save-by-rename atomico. Office, VS Code, Notepad++, vim con backup, quasi ogni editor moderno. Vedi un FileCreate su un file temporaneo (~$report.docx, report.docx.tmp, .swp, ecc.), uno o più record DataExtend, un FileDelete | Close sull'originale, poi RenameNewName | Close sul temporaneo che punta al nome dell'originale. La sequenza completa avviene in un singolo secondo da orologio da parete.
Encrypt-then-rename di ransomware. Record DataOverwrite sostenuti che coprono megabyte per file, seguiti da coppie RenameOldName / RenameNewName con un suffisso uniforme o un'estensione casuale a lunghezza fissa. Il tasso di DataOverwrite e il cluster di stessa estensione sulle rinomine sono le due metà della diagnosi. Il post di rilevamento ransomware è il playbook.
Quarantena antivirus. Un FileDelete | Close immediatamente dopo un recente FileCreate sulla stessa entry MFT, spesso con un SecurityChange in mezzo mentre l'agente AV sposta il file in una directory ristretta. Il journal conserva la prova che l'AV ha toccato il file anche dopo che il binario stesso è sparito.
Upload cloud-sync. RenameNewName | Close il cui nuovo padre è \Users\<u>\OneDrive\, \Dropbox\ o \Google Drive\ e il cui vecchio padre è il profilo utente. L'agente stesso produce record DataExtend | Close su file dentro la cartella di sync per i download — gli upload tendono ad apparire come rinomine dentro la cartella.
Timestomping. Un BasicInfoChange | Close nudo senza DataOverwrite, DataExtend, FileCreate o rename circostanti sullo stesso FileReferenceNumber. Un tocco legittimo è accompagnato dalla scrittura che ha innescato l'aggiornamento del timestamp. Il post sul timestomping è dove la cosa è elaborata per esteso.
Stream injection / abuso di ADS. StreamChange da solo di solito indica un Zone.Identifier da un download del browser (abbinato al FileCreate del file originale). Record StreamChange ripetuti sullo stesso FileReferenceNumber fuori da Downloads\, in particolare su binari di sistema firmati, meritano di essere aperti.
Cosa i bit non ti diranno mai
Il campo Reason riguarda il file, non l'attore. Niente utente, niente PID, niente riga di comando, niente livello di integrità. Attaccare un attore a un'operazione significa sempre andare altrove: Security.evtx 4663 (object access, solo se le SACL sono state configurate, cosa che di solito non è), Sysmon evento 11 (file create) ed evento 1 (process create con riga di comando), o prove di esecuzione processi e voci Shimcache che mettono un binario su disco al momento giusto.
I bit non ti dicono nemmeno del contenuto. Il journal sa che una regione è stata sovrascritta, non quali byte c'erano prima. Per quello servono le immagini "prima" di $LogFile o una Volume Shadow Copy.
Una ricetta di lettura che regge
Quando apro un journal parsato a freddo:
- Filtra prima per
FileCreate. Cos'è genuinamente nuovo nella finestra? Cerca percorsi in directory scrivibili dall'utente (\AppData\Local\Temp\,\Users\Public\,\ProgramData\fuori dai sotto-directory di vendor noti,\PerfLogs\). - Passa a
RenameNewName | Closee raggruppa per nuova estensione o nuovo padre. Save-by-rename, movimenti di cloud-sync e suffissi di ransomware emergono tutti qui. - Raggruppa
DataExtendper minuto. Scritture massive — backup, cifratura, crescita di archivi di esfiltrazione — appaiono come picchi sostenuti contro una base altrimenti tranquilla. - Leggi prima i record con
Close. Tratta gli intermedi come prove di supporto, non eventi indipendenti. - Una volta identificato un file candidato, raggruppa ogni record sullo stesso
FileReferenceNumbere leggi l'intero ciclo di vita in ordine cronologico. Quella sequenza è quasi sempre la parte che finisce nel report.
Il parser di questa pagina espone il filtro per Reason direttamente, quindi i passi 1-3 sono pochi clic. Per il passo 5, ordina per FileReferenceNumber poi per USN.
Letture aggiuntive
- Microsoft Learn — la reference USN_RECORD_V2 copre ogni definizione di bit direttamente dalla fonte.
- MFTECmd di Eric Zimmerman — il suo parser
$Jespone la bitmask Reason in un CSV piatto che regge a pivot su foglio di calcolo. - usnrs di Airbus CERT — l'implementazione Rust open source; leggere
src/reason.rsè la via più breve per interiorizzare il layout dei bit.