Byte-level walkthrough of USN_RECORD_V2 (Win XP+) and USN_RECORD_V3 (Win 8+) records inside \$Extend\\$UsnJrnl:$J — the rolling change-log that records every file system event Windows observes.
\\$Extend\\$UsnJrnl:$J, where the kernel appends a USN_RECORD for every create, delete, rename, write, attribute change, security change, or stream operation it observes. Each record carries a monotonically-increasing USN (a 64-bit sequence number that doubles as the byte offset of the record inside :$J), a precise FILETIME, the file's MFT reference, and a Reason bitmask describing what changed.
DATA_OVERWRITE + FILE_DELETE. Staging directories show up as bursts of FILE_CREATE. Renames leave the classic two-record RENAME_OLD_NAME → RENAME_NEW_NAME pair that lets you reconstruct what a file was called before it became svchost.exe. Click the icon at any time to minimize or re-open this card.
Select any field in the map to reveal a deep forensic dive.
The MFT tells you what a file is. The USN Journal tells you what happened to it. Where the MFT exists at the volume's root as a normal system file, the USN Journal lives quietly inside \\$Extend\\$UsnJrnl as two streams: the small $Max stream (journal metadata — size limits, allocation deltas) and the much larger $J stream (the actual records). $J is allocated as a sparse file, so on disk it occupies only its "live window" — typically the last 32–512 MB — but Windows lies to NTFS about the rest of the byte range. Old records simply fall off the front as new ones append to the back.
Every USN_RECORD is variable-length: a fixed header (60 bytes in V2, 76 bytes in V3) followed by a UTF-16LE filename. The header is 64-bit-aligned and records the USN (which is both the record's identity and its byte offset inside $J), a FILETIME, the file's MFT reference, the parent directory's MFT reference, a Reason bitmask, a SourceInfo bitmask, the file's current attributes, and the security ID. Investigators read the journal from oldest to newest via the FSCTL_READ_USN_JOURNAL ioctl — the same call fsutil usn readjournal uses internally.
Crow-Eye's USN_Claw.py walks every record into the journal_events table, and records the gaps between unreadable USNs into a separate deleted_entries table — because each gap represents either records that fell off the front of the journal (volume too busy) or records that were intentionally erased by a low-level adversary.
The USN Journal is activated by the operating system, not by user-mode tools. Whenever the NTFS driver processes a write, rename, security change, or any other tracked operation, it composes a USN_RECORD on the stack, copies the file's name out of the file object, and appends the record to $UsnJrnl:$J through the NTFS log file system. The append is atomic with respect to the change itself — you cannot rename a file without producing the matching RENAME_OLD_NAME + RENAME_NEW_NAME pair (unless the journal is disabled or the kernel is patched).
Lifecycle of a single USN_RECORD:
WriteFile, MoveFile, SetFileSecurity, …).$J.$J. The reason bitmask accumulates as further operations on the same open handle occur.USN_REASON_CLOSE is appended — the close record carries the union of all reasons that occurred during the handle's lifetime.$J exceeds MaximumSize (typically 32 MB), the oldest range is deallocated as sparse zeros — the byte offsets stay valid, but reading them returns zeros. This is why the journal has a LowestValidUsn.Introduced in Windows 2000 alongside NTFS 3.0 and steadily refined since. The on-disk record shape has changed only once (V2 → V3 in Windows 8).
$REPARSE_POINT, $OBJECT_ID, EFS, and disk quotas. Available, but disabled by default. Records are USN_RECORD_V2: 60-byte header + variable UTF-16 filename. Activation requires fsutil usn createjournal as Administrator.C: — the single most valuable forensic windfall of the Vista era.DWORDLONG) to 16 bytes (FILE_ID_128) to accommodate ReFS and very large NTFS volumes. The record-header grows from 60 to 76 bytes. USN_REASON_TRANSACTED_CHANGE (0x00400000) and USN_REASON_INTEGRITY_CHANGE (0x00800000) are added for TxF and ReFS integrity streams. Win 8 also adds FSCTL_QUERY_USN_JOURNAL_V2.USN_RECORD_V4 variant is introduced for large-file range tracking (used by Storage Spaces dedup). V4 records always come in a batch followed by one V3 record. USN_REASON_DESIRED_STORAGE_CLASS_CHANGE (0x01000000) is added for tiered-storage events.USN_RECORD_V3 on the system volume, while removable drives and ReFS subvolumes can produce a mix of V2 and V3. Crow-Eye's parser handles both transparently.\$Extend\$UsnJrnl (MFT entry inside $Extend).$J.| Offset | Size | Field Name | Forensic Meaning & Value |
|---|
The Reason field is a 32-bit bitmask. A single record can carry several flags — the kernel ORs them together as operations accumulate on an open handle. Order matches Microsoft's USN_RECORD_V2 reference.
| Mask | Name | Forensic Meaning |
|---|---|---|
| 0x00000001 | USN_REASON_DATA_OVERWRITE | Bytes inside the default unnamed $DATA stream were overwritten. |
| 0x00000002 | USN_REASON_DATA_EXTEND | Default stream grew (file was appended to). |
| 0x00000004 | USN_REASON_DATA_TRUNCATION | Default stream shrank. |
| 0x00000010 | USN_REASON_NAMED_DATA_OVERWRITE | Bytes in a named stream (ADS) were overwritten. APT signature: payload-via-ADS modification. |
| 0x00000020 | USN_REASON_NAMED_DATA_EXTEND | Named stream grew. |
| 0x00000040 | USN_REASON_NAMED_DATA_TRUNCATION | Named stream shrank. |
| 0x00000100 | USN_REASON_FILE_CREATE | File or directory was created. The earliest definitive evidence a file existed. |
| 0x00000200 | USN_REASON_FILE_DELETE | File or directory was deleted — survives the file itself. |
| 0x00000400 | USN_REASON_EA_CHANGE | OS/2 extended attribute changed (rare on modern Windows; sometimes by IE). |
| 0x00000800 | USN_REASON_SECURITY_CHANGE | ACL/ACE/owner changed — flag when adversaries weaken permissions. |
| 0x00001000 | USN_REASON_RENAME_OLD_NAME | First half of a rename. Filename field carries the old name. |
| 0x00002000 | USN_REASON_RENAME_NEW_NAME | Second half of a rename. Filename field carries the new name. Pairs with the prior record for the same MFT entry. |
| 0x00004000 | USN_REASON_INDEXABLE_CHANGE | NOT_CONTENT_INDEXED attribute toggled. |
| 0x00008000 | USN_REASON_BASIC_INFO_CHANGE | A file attribute or any timestamp changed — this is the bit you watch for timestomping. |
| 0x00010000 | USN_REASON_HARD_LINK_CHANGE | Hard link added / removed. |
| 0x00020000 | USN_REASON_COMPRESSION_CHANGE | NTFS compression toggled. |
| 0x00040000 | USN_REASON_ENCRYPTION_CHANGE | EFS encryption toggled. |
| 0x00080000 | USN_REASON_OBJECT_ID_CHANGE | $OBJECT_ID attribute changed — rare; can indicate forensic-tool tampering. |
| 0x00100000 | USN_REASON_REPARSE_POINT_CHANGE | Reparse point added / removed / modified — symlink/junction creation evidence. |
| 0x00200000 | USN_REASON_STREAM_CHANGE | Named stream added, removed, or renamed. |
| 0x00400000 | USN_REASON_TRANSACTED_CHANGE | Change applied through a TxF transaction (rare; Win 8+). |
| 0x00800000 | USN_REASON_INTEGRITY_CHANGE | ReFS integrity-stream attribute toggled (ReFS only). |
| 0x01000000 | USN_REASON_DESIRED_STORAGE_CLASS_CHANGE | Tiered-storage class change (Storage Spaces Direct / dedup; Win 10+). |
| 0x80000000 | USN_REASON_CLOSE | Handle closed — carries the union of all reasons during its lifetime. |
The 6 highlighted rows are flags that Crow-Eye's REASON_MAP now decodes (added to the dictionary as part of the parser enhancement).
A 32-bit bitmask describing who or what caused the change. Useful for filtering synthetic events from real user activity.
| Mask | Name | Investigative Use |
|---|---|---|
| 0x00000001 | USN_SOURCE_DATA_MANAGEMENT | Hierarchical Storage Management / Remote Storage moved data — user didn't change anything. |
| 0x00000002 | USN_SOURCE_AUXILIARY_DATA | A private side-stream was added (AV checksums, indexing metadata) — not a real user write. |
| 0x00000004 | USN_SOURCE_REPLICATION_MANAGEMENT | DFSR or similar replication agent modified the file. |
| 0x00000008 | USN_SOURCE_CLIENT_REPLICATION_MANAGEMENT | OneDrive or similar cloud-sync client touched the file. |
| 0x00000000 | (none set) | USER / APPLICATION — a real user-mode write. The default case. |
Same encoding as the Win32 GetFileAttributes return value. Crow-Eye's parser decodes the bitmask to text using file_attributes_to_text() before storing.
| Mask | Name | Meaning |
|---|---|---|
| 0x00000001 | READONLY | File is read-only. |
| 0x00000002 | HIDDEN | Often abused by droppers in %APPDATA%. |
| 0x00000004 | SYSTEM | System file; combined with HIDDEN = classic hiding pattern. |
| 0x00000010 | DIRECTORY | Entry is a directory. |
| 0x00000020 | ARCHIVE | Set on writes; cleared by backups. |
| 0x00000400 | REPARSE_POINT | File carries a $REPARSE_POINT attribute — symlink / mount / dedup. |
| 0x00000800 | COMPRESSED | NTFS compression on the data stream. |
| 0x00004000 | ENCRYPTED | EFS-encrypted. |
| 0x00080000 | RECALL_ON_DATA_ACCESS | OneDrive / cloud-tiered file — data lives elsewhere. |
How sophisticated adversaries try to defeat the USN journal — and how the investigator catches each play:
| Technique | Indicator in the USN journal | Counter-detection |
|---|---|---|
| Journal deletion | fsutil usn deletejournal /D C: wipes \$Extend\$UsnJrnl:$J entirely. The next FSCTL_QUERY_USN_JOURNAL returns "journal not active". |
Crow-Eye logs the journal-not-active state per volume. Cross-reference against the MFT: a recently-modified \$Extend\$UsnJrnl entry with sequence-number bumps proves the journal was rebuilt. |
| Journal disabling | USN_REASON activity stops cold on a system that previously had it — e.g. system records continue but nothing else. | Check the time delta between the last visible USN_REASON_FILE_CREATE/DELETE and the present. A multi-hour or multi-day gap on an active workstation is highly suspicious. |
| Sparse-window manipulation | Adversary writes huge volumes of noise files to push real evidence out of the journal's live window through normal sparse eviction. | Look at deleted_entries: massive gap regions with millions of evicted USNs in a short timeframe is itself an IOC. Real volumes rarely churn that fast. |
| Rename obfuscation | Adversary renames a payload back-and-forth (evil.exe → winupdate.exe → svchost.exe). Final filename looks legitimate. |
The journal preserves the full chain of RENAME_OLD_NAME / RENAME_NEW_NAME pairs for the same MFT entry (same FRN). Group by FRN and order by USN — the historical names tell the real story. |
| SourceInfo abuse | Some malware uses FSCTL_MARK_HANDLE to set USN_SOURCE_DATA_MANAGEMENT on its writes, hoping defenders filter on "non-zero SourceInfo". |
The SourceInfo bit requires SE_MANAGE_VOLUME_PRIVILEGE; admin-level malware does have it. Don't blindly trust SourceInfo as "not user activity" — check the process that opened the file (via ETW or Sysmon). |
| Named-stream payload hiding | Payload written to a named ADS shows USN_REASON_NAMED_DATA_EXTEND (0x00000020) on an otherwise innocent host file. |
Now that Crow-Eye decodes flags 0x00000010, 0x00000020, 0x00000040 (the previously-unmapped NAMED_DATA reasons), every named-stream write surfaces as NAMED_DATA_EXTEND in the reason column — trivially queryable. |
| Timestomping | USN_REASON_BASIC_INFO_CHANGE (0x00008000) fires on every SetFileTime call — even when the attacker rolls timestamps backwards. |
Pivot on FRN: any file whose $MFT creation time predates its first USN FILE_CREATE, or has a BASIC_INFO_CHANGE with a backdated timestamp delta, has been timestomped. |
Crow-Eye parses $Extend\$UsnJrnl:$J into a second-by-second record of every create, rename, delete, and write — the change log that survives even when the files themselves are long gone.