ReferenceCONSTRUCTED
Forensic timeline cheat sheet: which clock, whose time zone, and what each timestamp really records
A timeline is only as good as your understanding of what each timestamp means. The artifacts that carry time on Windows, what event each one actually records, the zone it is stored in, and the commands to build and cut a super timeline.
version 1.0checked 2026-09-21windows · linux6 min read
The three rules
- Everything in UTC, always. Convert at ingestion, keep the original value beside the converted one, and write "UTC" in the column header. The timeline merge tool does exactly this.
- Know what event a timestamp records. "Modified" on a Prefetch file is a run time. The "modified" time in Shimcache is the executable's own last-modified time and says nothing about when it ran.
- One clock is an assertion. Two independent clocks are a finding. A file's creation time agreeing with a USN journal record and a process creation event is a fact. A creation time on its own is something the file's owner could have set.
NTFS: two sets of four
Every file has timestamps in two attributes of its MFT record, each with four values: Modified, Accessed, Changed (the MFT record) and Born. Stored as UTC FILETIME values with 100-nanosecond resolution.
| Attribute | Updated by | Can a user set it? |
|---|---|---|
$STANDARD_INFORMATION (0x10) | Normal file operations. What Explorer and dir show | Yes, through a documented API. This is what timestomping tools change |
$FILE_NAME (0x30) | The kernel, on create, rename and move | Not through the ordinary API. It usually keeps the true creation time |
Timestomping indicators, none conclusive on its own:
$STANDARD_INFORMATIONBorn earlier than$FILE_NAMEBorn.- Sub-second fraction of exactly zero in 0x10 while neighbours have full precision. Many tools set whole seconds.
- A Born time that predates the volume, the operating system install or the parent directory.
- The USN journal records a
BasicInfoChangefor the file at the moment the times were altered. The journal is the clock they usually forget; see the operator set the clock, and set only one of them.
What ordinary operations do to 0x10
| Operation | Born | Modified | Reading it |
|---|---|---|---|
| Create | Now | Now | |
| Edit | Unchanged | Now | |
| Copy (same or different volume) | Now | Inherited from the source | Modified earlier than Born means the file was copied here. The most useful single rule on this page |
| Move within a volume | Unchanged | Unchanged | Only the MFT Changed time and the 0x30 attribute move |
| Move across volumes | Behaves as copy then delete | Inherited | |
| Extract from an archive | Now | Usually the time stored in the archive | The Modified time can be years old and legitimate |
Last Accessed is not reliable. Updates have been disabled or deferred by default
on most Windows versions since Vista, and the setting varies with volume size on
recent builds. Do not build an argument on it without checking
NtfsDisableLastAccessUpdate for that host.
FAT and exFAT volumes (USB sticks, SD cards) store local time with no zone and a two-second resolution on modification times. A USB timeline is in whatever zone the writing machine was set to.
The artifacts that carry time
| Artifact | The time it records | Zone | Trap |
|---|---|---|---|
$MFT | Two sets of MACB per file | UTC | See above. 0x10 is settable |
$UsnJrnl:$J | Every change to every file, with a reason code, in order | UTC | Rolls by size. On a busy volume the window is hours to days |
$LogFile | NTFS transaction log | UTC | Shorter window than the USN journal |
| Event logs (.evtx) | When the event was written | UTC in the file. Event Viewer displays local time | Exports from Event Viewer are in the viewer's zone, not the host's |
| Prefetch (.pf) | Last run, and up to eight previous run times on Windows 8 and later. File creation is roughly the first run | UTC | Written about ten seconds after launch. Disabled on servers by default and on some SSD configurations |
| Amcache.hve | First seen by the compatibility inventory, plus the SHA-1 | UTC | Presence is not proof of execution on newer builds |
| Shimcache (AppCompatCache) | The file's last-modified time | UTC | Not an execution time. Order in the cache is meaningful; the timestamp is not what it looks like. Written to the registry only at shutdown |
| UserAssist | Last run and run count for GUI programs launched by the user | UTC | Per user, Explorer launches only |
| Registry key LastWrite | When any value under the key last changed | UTC | One time per key, not per value. A Run key with five values has one timestamp |
| LNK and Jump Lists | Target file's MACB at the time of opening, plus the LNK's own | UTC | Two different sets of times in one artifact. Know which you are quoting |
| SRUM (SRUDB.dat) | Application and network usage | UTC | Flushed roughly hourly, so times are bucket boundaries rather than event times |
| Browser history | Visit time | Chrome and Edge: WebKit microseconds since 1601. Firefox: Unix microseconds | The epoch. A timestamp off by 369 years is a WebKit time read as Unix |
Recycle Bin $I files | Deletion time and original path | UTC | |
| Linux ext4 | atime, mtime, ctime, and crtime | UTC | ctime is inode change, not creation. crtime exists but stat on older systems does not show it |
Building a super timeline
log2timeline.py --storage-file case.plaso /evidence/host01.E01
psort.py -o dynamic -w host01-window.csv case.plaso \
"date > '2026-03-01 00:00:00' AND date < '2026-03-04 00:00:00'"
Plaso on a full image takes hours and produces millions of rows. For triage, build a narrow timeline from the artifacts that matter, then widen:
MFTECmd.exe -f 'C:\case\$MFT' --csv C:\case\out --csvf mft.csv
MFTECmd.exe -f 'C:\case\$J' --csv C:\case\out --csvf usn.csv
EvtxECmd.exe -d 'C:\case\winevt\Logs' --csv C:\case\out --csvf evtx.csv
PECmd.exe -d 'C:\case\Prefetch' --csv C:\case\out --csvf prefetch.csv
fls -r -m C: -o 2048 host01.dd > body.txt
mactime -b body.txt -d -z UTC 2026-03-01..2026-03-04 > fs-timeline.csv
Then merge, in UTC, keeping each source's original value:
python3 timeline_merge.py \
--input evtx.csv:TimeCreated:evtx \
--input mft.csv:Created0x10:mft-born \
--input proxy.csv:timestamp:proxy:+01:00 \
--output timeline.csv
Working the timeline
- Start from a pivot and work outward. A known-bad process start, a ransom note's Born time, an alert. Thirty seconds either side first, then five minutes, then the hour.
- Annotate as you go, in a column, with the artifact each line rests on. The timeline in your report is this column.
- Separate observed from assessed. "u.exe Born 09:14:07 (MFT 0x30), first executed 09:14:11 (Prefetch)" is observed. "The operator dropped and ran the tool" is assessed from it.
- Check the host's clock. Event 4616 for time changes, and the offset between the host and your log collector. A five-minute skew between two hosts reorders a lateral movement sequence.
- State the window you could see. The oldest USN record, the oldest Security event. An absence before that is not an absence.
sources
- Plaso (log2timeline) documentation
- Microsoft Learn: File Times (FILETIME, UTC storage, FAT and NTFS resolution)
- Eric Zimmerman's tools: MFTECmd, EvtxECmd, PECmd, Timeline Explorer
- The Sleuth Kit: fls and mactime
- NIST SP 800-86: Guide to Integrating Forensic Techniques into Incident Response
- MITRE ATT&CK T1070.006, Indicator Removal: Timestomp