Reconstructing a user activity timeline from the USN journal

From three minutes of $UsnJrnl records you can usually reconstruct what a user was doing — Office, browser, downloads, code. How to read the journal as a behaviour log.

By 7 min read

A great deal of "what was happening on this computer between 14:00 and 16:00" can be answered straight out of the USN journal, without any other artefact. The records are not a syscall log and they are not tagged with a user, but they are something almost as useful: a high-frequency, per-operation record of every file the machine touched. With pattern knowledge that takes a few engagements to build, you can recover behaviour at the granularity of Office save, browser navigation, IDE rebuild, lunch break.

This post is the field guide for reading the journal as a behaviour timeline.

Why this works at all

Most user-visible actions on a Windows desktop produce a recognisable file-system signature. Click Save in Word and Word writes a temp file, atomically renames it, and BasicInfoChange | Closes the result. Open a new tab in Chrome and the disk cache appends. Run an npm install and a thousand package.json files spring into being in seconds.

These signatures are not documented in any Microsoft reference. They are learned by looking at journals from controlled-experiment hosts and matching them to known activity. Below is a starter set, ordered by how often I see each in casework.

Office in the wild

When a user saves a Word, Excel or PowerPoint file you see:

FileCreate | Close            ~$<filename>.docx     (lock file)
FileCreate | Close            <filename>.tmp        (atomic temp)
DataExtend | Close            <filename>.tmp        × N
RenameOldName | Close         <filename>.docx
FileDelete | Close            <filename>.docx
RenameNewName | Close         <filename>.docx       (was <filename>.tmp)
FileDelete | Close            ~$<filename>.docx
BasicInfoChange | Close       <filename>.docx

The ~$ lock file is Office's signal that a document is open. Its FileCreate and FileDelete bracket the open/close window precisely. The atomic rename .tmp to .docx is the save itself. The BasicInfoChange at the end is the new file getting its mtime stamped on close.

A pivot trick I use on every engagement: filter for FileCreate records whose filename starts with ~$. That gives you every Office document the user opened in the window. Subtract FileDelete of the same names and you have any documents still open at acquisition time, which on a live capture corresponds to documents the user had open when the machine was imaged. That last bit has gone into more than one report as the "what was the user actually doing the moment we walked in" answer.

Browser activity

Modern browsers maintain heavy file-system caches. Chrome and Edge use \Users\<u>\AppData\Local\<browser>\User Data\Default\Cache\Cache_Data\ (plus Code Cache\). Firefox uses \Users\<u>\AppData\Local\Mozilla\Firefox\Profiles\<id>\cache2\. During browsing:

  • A sustained low rate of FileCreate | Close and DataExtend | Close in the cache directory.
  • Occasional FileDelete | Close as the LRU evicts old entries.
  • Every few minutes, RenameNewName of cache files as the index is compacted.

Bursts of cache writes correlate with visiting media-heavy or single-page-application sites. Quiet periods correlate with the user being away or on a long-loading page. Active video playback produces a different shape — large sustained DataExtend on a single cache file rather than many small creates.

The cache filenames do not encode URLs. For that, pivot to the browser's own SQLite databases via the browser history forensics tooling. The journal's contribution is the cadence — that the user was actively browsing during a specific window — plus the explicit FileCreate events in \Users\<u>\Downloads\ for deliberate downloads.

Code activity

If the user is a developer, IDE and build-tool activity produces highly characteristic bursts:

  • Webpack, Vite, Turbopack — many FileCreate in node_modules\.cache\, node_modules\.vite\, .next\ and dist\. Hundreds per build.
  • Visual Studio C++FileCreate of .obj and .pdb files in Debug\ or Release\ subtrees.
  • Cargo (Rust) — incremental builds touch target\debug\incremental\ heavily; full builds also touch target\debug\deps\.
  • Go — short, intense bursts in $GOPATH\pkg\ and the build cache under \Users\<u>\AppData\Local\go-build\.
  • npm install and yarn install — thousands of FileCreate records under node_modules\ in tens of seconds. The single noisiest user activity in the journal.

A five-minute burst of FileCreate in node_modules\ followed by a DataExtend swarm on dist\bundle.js is "ran the build, then a dev server reloaded". Read the exclusion section below — these bursts will drown out everything else if you do not filter them out for non-development analysis.

Downloads and installations

A direct download via Chrome:

FileCreate | Close             \Users\<u>\Downloads\<file>.crdownload
DataExtend | Close             \Users\<u>\Downloads\<file>.crdownload  × N
RenameNewName | Close          \Users\<u>\Downloads\<file>
BasicInfoChange | Close        \Users\<u>\Downloads\<file>

Firefox uses .part, Edge uses .download. The atomic rename at the end is what makes the file appear "complete" to Explorer and other readers.

Installation of a new program typically follows: FileCreate of an installer (.exe, .msi, .appx) in Downloads, then a burst of FileCreate records under \Program Files\, \Program Files (x86)\ or \Users\<u>\AppData\Local\Programs\. The per-minute histogram makes installs trivially identifiable. Cross-reference with AmCache and Prefetch for the execution side.

Putting it together: the daily timeline recipe

The recipe for a "what did the user do today" report:

  1. Pull the user's parsed journal for the day. Restrict to records whose resolved path starts with \Users\<u>\ (the target user). This eliminates Windows Update, system caches and OS noise that otherwise dominates.
  2. Compute, per 10-minute bucket: counts of FileCreate, DataExtend, BasicInfoChange.
  3. Plot the three series. The shape tells you the rough activity:
    • High creates plus extend. Writing or saving content.
    • High BasicInfoChange alone. Browsing, scrolling, light editing where Office or the editor is touching the file without significant writes.
    • High extend with low create. Large download or media playback.
    • Sustained creates in Cache\ paths. Browsing.
    • Sustained creates in node_modules\ or target\ or Debug\. Coding.
  4. Annotate spikes by inspecting the top five filenames in each bucket. Office filenames and ~$ lock files are great anchors. Cache filenames you can usually ignore.

The parser on this page exposes the per-minute histogram on its timeline component. Clicking a bar filters the table to that window, which is the workflow above without writing code.

For attribution — which user was at the keyboard — pair the journal timeline with Security.evtx 4624 interactive logons (LogonType=2) and 4647 user-initiated logoffs from the EVTX parser. That brackets the session boundaries and gives you a name to put on the activity.

Exclusions and pitfalls

Backup and AV scans produce volume-wide BasicInfoChange bursts that look superficially like user activity. They follow scheduled times and visit every directory rather than focusing on a subtree. Easy to identify by path coverage and to exclude.

Sync agents (OneDrive, Dropbox, Google Drive Backup and Sync) generate journal traffic that looks like user activity but is the agent's own work. Filter to records outside the sync folder for user-initiated activity, or look specifically inside the sync folder for the exfil-via-cloud pattern documented in the exfiltration post.

Background indexers. SearchIndexer.exe touches files in \Users\<u>\AppData\Roaming\Microsoft\Windows\Recent\ and \ProgramData\Microsoft\Search\. MsMpEng.exe (Defender) produces BasicInfoChange events when it scans. Both are easy to bucket as system noise.

Chrome Software Reporter, Edge updater, Windows Update. All produce predictable FileCreate and DataExtend bursts in \Program Files\ and \Windows\SoftwareDistribution\. Filter by path.

node_modules\ and other build outputs dominate any timeline that includes them. For non-development cases, exclude node_modules\, target\, Debug\, Release\, obj\, bin\, .next\, dist\, build\ by path prefix.

What the journal cannot reconstruct

  • The user's identity. Correlate timestamps with Security.evtx 4624 logons.
  • The URLs the user visited. Browser history databases hold those — the journal only knows the cache write cadence.
  • The contents of any file. Metadata only.
  • Anything outside the ring-buffer window. A 32 MB $J covers a few days on a desktop. Anything older is gone from this artefact; pull Shadow Copies and $LogFile if you need more reach.

For 80% of "what was the user doing between X and Y" questions, $J and $MFT together answer directly. The remaining 20% wants Sysmon, EVTX, SRUM for application-level network and CPU usage, and a proper super-timeline.

Further reading

  • Plaso (log2timeline) — the canonical super-timeline tool ingests parsed USN journals and merges them with every other Windows timeline artefact into one sortable view.
  • SANS DFIR — the Windows Forensic Analysis poster is the single-page reference for which artefacts answer which user-activity questions.
  • The Velociraptor artifact exchange — multiple community artefacts combine USN, MFT and EVTX correlations like the ones above.