If you spend any time reading USN journals, the reason bitmask becomes muscle memory the way EVTX EventIDs do. It is the 32-bit field NTFS sets on every USN_RECORD_V2 to summarise what just changed about a file. The bits are additive, not mutually exclusive, which is the first thing to internalise: a single record can carry FileCreate | DataExtend | Close and that combination means something different from any of those bits alone.
What follows is the field reference I keep on a sticky note next to my monitor.
The bits, in the order you will actually meet them
| Flag | Hex | What it means in practice |
|---|---|---|
DataOverwrite | 0x00000001 | A region of the main data stream was overwritten in place |
DataExtend | 0x00000002 | The main data stream grew |
DataTruncation | 0x00000004 | The main data stream shrank |
NamedDataOverwrite / Extend / Truncation | 0x10 / 0x20 / 0x40 | Same three, but on an alternate data stream |
FileCreate | 0x00000100 | A new file or directory was created |
FileDelete | 0x00000200 | The file was unlinked from the namespace |
EaChange | 0x00000400 | Extended attributes changed |
SecurityChange | 0x00000800 | ACLs/owner changed |
RenameOldName | 0x00001000 | The "before" half of a rename |
RenameNewName | 0x00002000 | The "after" half of a rename |
IndexableChange | 0x00004000 | Indexed flag toggled |
BasicInfoChange | 0x00008000 | Timestamps, attributes or compression flags rewritten |
HardLinkChange | 0x00010000 | A hard link was added or removed |
CompressionChange | 0x00020000 | NTFS compression toggled |
EncryptionChange | 0x00040000 | EFS state changed |
ObjectIdChange | 0x00080000 | Object ID set or cleared |
ReparsePointChange | 0x00100000 | Reparse point added/changed/removed |
StreamChange | 0x00200000 | An alternate data stream was added, renamed or deleted |
Close | 0x80000000 | The handle that produced the change has been released |
The Close bit deserves a separate paragraph because misreading it will throw off your event count by an order of magnitude. NTFS coalesces successive operations under the same handle and emits intermediate records as it flushes, then a final summary record with Close set once the handle is gone. The intermediate records are real, but they double-count. When you triage a parsed journal, filter for Close-bearing records first to dedupe operations to one row per handle. Pull the non-Close records only when you need sub-handle granularity.
Patterns that recur often enough to memorise
A handful of reason combinations show up so frequently across investigations that you stop reading them character by character and start reading them as words.
New file written by an app. FileCreate | DataExtend then more DataExtend | Close records as the file grows, capped by BasicInfoChange | Close. The last record is the file getting its mtime stamped on close.
Rename across directories. Two records sharing the same FileReferenceNumber: RenameOldName | Close then RenameNewName | Close. The parent reference differs between the two — that delta is how you reconstruct the move. If you only filter for one of the rename bits you lose half the story.
Atomic save-by-rename. Office, VS Code, Notepad++, vim with backup, almost every modern editor. You see a FileCreate on a temp file (~$report.docx, report.docx.tmp, .swp, etc.), one or more DataExtend records, a FileDelete | Close on the original, then RenameNewName | Close on the temp pointing at the original's name. The full sequence happens within a single wall-clock second.
Ransomware encrypt-then-rename. Sustained DataOverwrite records covering megabytes per file, followed by RenameOldName / RenameNewName pairs with a uniform suffix or fixed-length random extension. The DataOverwrite rate and the same-extension cluster on the renames are the two halves of the diagnostic. The ransomware detection post is the playbook.
Antivirus quarantine. A FileDelete | Close immediately following a recent FileCreate on the same MFT entry, often with a SecurityChange in between as the AV agent moves the file into a restricted directory. The journal preserves proof that AV touched the file even after the binary itself is gone.
Cloud-sync upload. RenameNewName | Close whose new parent is \Users\<u>\OneDrive\, \Dropbox\ or \Google Drive\ and whose old parent is the user profile. The agent itself produces DataExtend | Close records on files inside the sync folder for downloads — uploads tend to look like renames into the folder.
Timestomping. A bare BasicInfoChange | Close with no surrounding DataOverwrite, DataExtend, FileCreate or rename on the same FileReferenceNumber. A legitimate touch is paired with the write that triggered the timestamp update. The timestomping post is where this is worked out at length.
Stream injection / ADS abuse. StreamChange on its own usually indicates a Zone.Identifier from a browser download (paired with the original file's FileCreate). Repeated StreamChange records on the same FileReferenceNumber away from Downloads\, particularly on signed system binaries, are worth opening.
What the bits will never tell you
The reason field is about the file, not the actor. There is no user, no PID, no command line, no integrity level. Attaching an actor to an operation always means going somewhere else: Security.evtx 4663 (object access, only if SACLs were configured, which they usually were not), Sysmon event 11 (file create) and event 1 (process create with command line), or process execution evidence and Shimcache entries that put a binary on disk at the right moment.
The bits also do not tell you about content. The journal knows a region was overwritten, not what bytes used to be there. For that you need $LogFile before-images or a Volume Shadow Copy.
A reading recipe that holds up
When I open a parsed journal cold:
- Filter for
FileCreatefirst. What is genuinely new in the window? Look for paths in user-writable directories (\AppData\Local\Temp\,\Users\Public\,\ProgramData\outside known vendor subdirs,\PerfLogs\). - Switch to
RenameNewName | Closeand group by new extension or new parent. Save-by-rename, cloud-sync moves and ransomware suffixes all surface here. - Bucket
DataExtendper minute. Bulk writes — backups, encryption, exfil-archive growth — show as sustained spikes against an otherwise quiet baseline. - Read
Close-bearing records first. Treat intermediates as supporting evidence, not independent events. - Once you have a candidate file, group every record on the same
FileReferenceNumberand read the full lifecycle in chronological order. That sequence is almost always the part that goes into the report.
The parser on this page exposes reason filtering directly, so steps 1-3 are a few clicks. For step 5, sort by FileReferenceNumber then by USN.
Further reading
- Microsoft Learn — USN_RECORD_V2 reference covers every bit definition straight from the source.
- Eric Zimmerman's MFTECmd — its
$Jparser surfaces the reason bitmask in a flat CSV that holds up to spreadsheet pivots. - Airbus CERT's usnrs — the open-source Rust implementation; reading
src/reason.rsis the shortest way to internalise the bit layout.