Eye Describe Anatomy

NTFS USN Journal — Total Binary Dissection

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.

USN_RECORD_V2 (Win XP+)
USN_RECORD_V3 (Win 8+, 128-bit FRN)
USN_RECORD_V4 (Win 10+, range tracking)
USN_RECORD_V3 (Win 8+, 128-bit FILE_ID_128 FRN) — 112 byte total
Live Byte Selection

Select any field in the map to reveal a deep forensic dive.

The Change Log — NTFS's Tape Recorder

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.

How the USN Journal Works — Kernel-Level Bookkeeping

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:

  1. Triggering event. User-mode code calls a Win32 API (WriteFile, MoveFile, SetFileSecurity, …).
  2. Kernel intercept. The NTFS driver translates the request, but before completing it, it allocates a USN_RECORD inside the next 8-byte-aligned slot of $J.
  3. Append. The record is written with a fresh, monotonically-increasing USN equal to its own byte offset in $J. The reason bitmask accumulates as further operations on the same open handle occur.
  4. Close. When the file handle is closed, a final record with USN_REASON_CLOSE is appended — the close record carries the union of all reasons that occurred during the handle's lifetime.
  5. Sparse eviction. When $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.

USN Journal History

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).

FEBRUARY 2000
Windows 2000 — NTFS 3.0 introduces the USN Journal
Microsoft adds the change-journal feature alongside $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.
JANUARY 2007
Windows Vista — USN Journal enabled by default
Microsoft turns the journal on by default on the system volume so the search indexer and the Volume Shadow Copy service can use it. From this point forward, every default Windows install has a populated USN journal on C: — the single most valuable forensic windfall of the Vista era.
OCTOBER 2012
Windows 8 — USN_RECORD_V3 ships
The file reference fields grow from 8 bytes (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.
JULY 2015
Windows 10 — USN_RECORD_V4 + range tracking
A new 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.
2026 — STILL THE STANDARD
Windows 11 — V2 and V3 records, same encoding
No format change since Win 10. On modern Windows 11, normal file activity produces 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.

USN Journal Key Facts

Storage Location
$UsnJrnl:$J
Sparse ADS on the system file \$Extend\$UsnJrnl (MFT entry inside $Extend).
Default Max Size
~32 MB
The "live window". Older bytes are sparse-zeroed; the file looks petabytes long.
V2 header size
60 B
+ UTF-16 filename + padding to 8-byte alignment.
V3 header size
76 B
Adds two 128-bit FILE_ID_128 fields instead of 64-bit DWORDLONGs.
Reason flags
24
Bitmask covering every kind of NTFS / ReFS change event.
SourceInfo flags
4
Filters synthetic events (replication, AV scans, HSM moves).
USN = byte offset
8 B
The 64-bit USN doubles as the record's literal byte offset in $J.
Enable manually
fsutil usn createjournal
Off by default on non-system NTFS volumes. Admin only.

Full Logical Dissection Reference

Offset Size Field Name Forensic Meaning & Value

All 24 USN_REASON_* Flags

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.

MaskNameForensic Meaning
0x00000001USN_REASON_DATA_OVERWRITEBytes inside the default unnamed $DATA stream were overwritten.
0x00000002USN_REASON_DATA_EXTENDDefault stream grew (file was appended to).
0x00000004USN_REASON_DATA_TRUNCATIONDefault stream shrank.
0x00000010USN_REASON_NAMED_DATA_OVERWRITEBytes in a named stream (ADS) were overwritten. APT signature: payload-via-ADS modification.
0x00000020USN_REASON_NAMED_DATA_EXTENDNamed stream grew.
0x00000040USN_REASON_NAMED_DATA_TRUNCATIONNamed stream shrank.
0x00000100USN_REASON_FILE_CREATEFile or directory was created. The earliest definitive evidence a file existed.
0x00000200USN_REASON_FILE_DELETEFile or directory was deleted — survives the file itself.
0x00000400USN_REASON_EA_CHANGEOS/2 extended attribute changed (rare on modern Windows; sometimes by IE).
0x00000800USN_REASON_SECURITY_CHANGEACL/ACE/owner changed — flag when adversaries weaken permissions.
0x00001000USN_REASON_RENAME_OLD_NAMEFirst half of a rename. Filename field carries the old name.
0x00002000USN_REASON_RENAME_NEW_NAMESecond half of a rename. Filename field carries the new name. Pairs with the prior record for the same MFT entry.
0x00004000USN_REASON_INDEXABLE_CHANGENOT_CONTENT_INDEXED attribute toggled.
0x00008000USN_REASON_BASIC_INFO_CHANGEA file attribute or any timestamp changed — this is the bit you watch for timestomping.
0x00010000USN_REASON_HARD_LINK_CHANGEHard link added / removed.
0x00020000USN_REASON_COMPRESSION_CHANGENTFS compression toggled.
0x00040000USN_REASON_ENCRYPTION_CHANGEEFS encryption toggled.
0x00080000USN_REASON_OBJECT_ID_CHANGE$OBJECT_ID attribute changed — rare; can indicate forensic-tool tampering.
0x00100000USN_REASON_REPARSE_POINT_CHANGEReparse point added / removed / modified — symlink/junction creation evidence.
0x00200000USN_REASON_STREAM_CHANGENamed stream added, removed, or renamed.
0x00400000USN_REASON_TRANSACTED_CHANGEChange applied through a TxF transaction (rare; Win 8+).
0x00800000USN_REASON_INTEGRITY_CHANGEReFS integrity-stream attribute toggled (ReFS only).
0x01000000USN_REASON_DESIRED_STORAGE_CLASS_CHANGETiered-storage class change (Storage Spaces Direct / dedup; Win 10+).
0x80000000USN_REASON_CLOSEHandle 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).

SourceInfo Flags

A 32-bit bitmask describing who or what caused the change. Useful for filtering synthetic events from real user activity.

MaskNameInvestigative Use
0x00000001USN_SOURCE_DATA_MANAGEMENTHierarchical Storage Management / Remote Storage moved data — user didn't change anything.
0x00000002USN_SOURCE_AUXILIARY_DATAA private side-stream was added (AV checksums, indexing metadata) — not a real user write.
0x00000004USN_SOURCE_REPLICATION_MANAGEMENTDFSR or similar replication agent modified the file.
0x00000008USN_SOURCE_CLIENT_REPLICATION_MANAGEMENTOneDrive or similar cloud-sync client touched the file.
0x00000000(none set)USER / APPLICATION — a real user-mode write. The default case.

FileAttributes — Common Bits

Same encoding as the Win32 GetFileAttributes return value. Crow-Eye's parser decodes the bitmask to text using file_attributes_to_text() before storing.

MaskNameMeaning
0x00000001READONLYFile is read-only.
0x00000002HIDDENOften abused by droppers in %APPDATA%.
0x00000004SYSTEMSystem file; combined with HIDDEN = classic hiding pattern.
0x00000010DIRECTORYEntry is a directory.
0x00000020ARCHIVESet on writes; cleared by backups.
0x00000400REPARSE_POINTFile carries a $REPARSE_POINT attribute — symlink / mount / dedup.
0x00000800COMPRESSEDNTFS compression on the data stream.
0x00004000ENCRYPTEDEFS-encrypted.
0x00080000RECALL_ON_DATA_ACCESSOneDrive / cloud-tiered file — data lives elsewhere.

Anti-Forensics & APT Techniques

How sophisticated adversaries try to defeat the USN journal — and how the investigator catches each play:

TechniqueIndicator in the USN journalCounter-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.exewinupdate.exesvchost.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.
From reading to doing

Replay the file-system timeline with Crow-Eye

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.

Download Crow-Eye