40 min read

> Where you are: Part III, Chapter 21 of 40. The last seven chapters handed you a growing pile of dated facts: file-system MACB times (Chapter 14, Chapter 4), live-triage captures (Chapter 15), registry and .evtx and execution artifacts (Chapter...

Chapter 21: Timeline Analysis — Reconstructing Events from Timestamps Across Multiple Sources

Where you are: Part III, Chapter 21 of 40. The last seven chapters handed you a growing pile of dated facts: file-system MACB times (Chapter 14, Chapter 4), live-triage captures (Chapter 15), registry and .evtx and execution artifacts (Chapter 16), macOS/Linux logs (Chapter 17), browser history (Chapter 18), email and chat (Chapter 19), and document/photo metadata (Chapter 20). Each was a fragment with a clock attached. This chapter fuses them into one chronological narrative — the super-timeline — and teaches you to read it, to normalize it to a single time standard, and to detect when someone has lied to the clock. This is the home chapter for the MACB model and for timestomping detection, and it is where anchor case #2 — the departing engineer — finally resolves: the timeline that reconciles altered $STANDARD_INFORMATION` times against the truthful `$FILE_NAME times and proves the sequence of the theft.

Learning paths: This chapter is the analytical heart of the 🔍 Forensic Examiner track — a case is rarely won by a single artifact and almost always won by the order of many. 🛡️ Incident Response lives here too: an intrusion is a story in time (initial access → execution → persistence → lateral movement → exfiltration), and the timeline is how you tell it and bound the blast radius. 📜 Legal/eDiscovery practitioners should read the normalization and timestomping sections closely — the timeline is the spine of the forensic report, and every line of it must survive cross-examination. 💾 Data Recovery technicians get a quieter but real payoff: modification times tell you which recovered version is newest, and shadow-copy timestamps tell you which snapshot to restore.


The timeline is the spine of the investigation

By the time you reach this chapter you can answer dozens of small questions. Was a USB device connected? Yes — Chapter 16's USBSTOR. Did a program run? Yes — Chapter 16's prefetch. Was a file opened from a removable drive? Yes — the LNK and Jump List. What does the browser history show? Chapter 18. What did the email headers say? Chapter 19. Each of those is true and each is, by itself, nearly useless. A juror does not convict on "a device was connected." A juror — and before the juror, a judge weighing admissibility, and before the judge, opposing counsel hunting for a hole — responds to a sequence: at 18:51 the device was connected, at 18:55 the secret folder was browsed, at 18:58 a local copy appeared, at 19:04 the file was opened from the device, at 19:33 the device left the building, and the next morning a cleaning tool ran and the local copy went into the Recycle Bin. That sequence is an argument. The individual facts are only its words.

Assembling that sequence is timeline analysis, and the artifact it produces — every dated event from every source, merged and sorted into one chronological list — is the forensic super-timeline. The term "super-timeline" was popularized by the SANS DFIR community and by the tool that made it practical, log2timeline/plaso, but the idea is older than any tool: take everything that carries a time, put it on one axis, and read what the machine did to itself and what people did to the machine. The super-timeline is simultaneously the most powerful and the most dangerous deliverable in forensics. Powerful, because correlation across independent sources is how a lead becomes a finding — when the registry, a LNK file, a prefetch entry, and an event-log record all agree on a time, no single tool's bug and no single artifact's ambiguity can shake the conclusion. Dangerous, because a raw super-timeline of a busy workstation contains millions of lines, the overwhelming majority of them noise, and an examiner who cannot filter, pivot, and interpret will either drown or — worse — pluck a coincidence out of the flood and call it a fact.

This chapter is about doing it well: understanding how machines represent time (so you can convert any timestamp to a common standard), normalizing time zones and accounting for clock skew (so events from different sources line up honestly), mastering the MACB model and its NTFS subtlety of two timestamp sets (so you know what each time actually means), detecting manipulation (so a backdated file does not corrupt your sequence), building the timeline with the standard tools (bodyfile/mactime and plaso), and reading it like an investigator rather than a spreadsheet.

Why This Matters. Almost every other chapter in Part III produces inputs to this one. Windows artifacts, browser records, email headers, document metadata — they are raw material, and timeline analysis is the refinery. If you learn one synthesis skill in digital forensics, learn this one, because it is the skill that turns a folder full of CSV exports into a report a court can act on. It is also the skill that most directly embodies one of this book's recurring themes: every action leaves a trace, and the absence of a trace is itself a trace. On a timeline, a wiped log is not a blank — it is a conspicuous, dated gap, and the gap is evidence.

A map of temporal artifacts

Before the mechanics, fix in your mind how many independent clocks feed a single timeline. The richness is the point: redundancy is what makes a timeline robust.

SOURCES THAT FEED A SUPER-TIMELINE  (each carries its own clock + epoch)
┌──────────────────────────────────────────────────────────────────────────┐
│ FILE SYSTEM (the backbone)                                                 │
│   NTFS  $STANDARD_INFORMATION  M A C B   (100-ns, UTC)  ← user-forgeable   │
│   NTFS  $FILE_NAME             M A C B   (100-ns, UTC)  ← kernel, truthful │
│   $UsnJrnl:$J change journal, $LogFile   (file ops, with reasons + times)  │
│   ext4 inode atime/ctime/mtime/crtime (ns, UTC) · APFS (ns) · FAT (2 s,    │
│   LOCAL time) · exFAT (with UTC offset)                                    │
├──────────────────────────────────────────────────────────────────────────┤
│ WINDOWS ARTIFACTS (Ch.16)                                                  │
│   Registry key last-write (FILETIME) · .evtx event records (FILETIME)      │
│   Prefetch run times · Amcache · LNK · Jump Lists · $Recycle.Bin $I        │
│   BAM/UserAssist · ShellBags · SRUM · scheduled tasks · ActivitiesCache    │
├──────────────────────────────────────────────────────────────────────────┤
│ APPLICATION & INTERNET (Ch.17–20)                                          │
│   Browser history/cookies/cache (WebKit µs-1601 / Firefox PRTime µs-1970)  │
│   Email Date + Received headers · chat DBs · macOS unified log / FSEvents  │
│   Office/PDF metadata · EXIF date-taken + GPS (Ch.20)                      │
├──────────────────────────────────────────────────────────────────────────┤
│ HISTORICAL DEPTH                                                           │
│   Volume Shadow Copies (point-in-time snapshots) → the SAME timeline,      │
│   replayed at earlier states; backups; cloud version history (Ch.31)      │
└──────────────────────────────────────────────────────────────────────────┘
            ▼  normalize every clock to ONE standard (UTC)  ▼
        ┌───────────────────────────────────────────────┐
        │   THE SUPER-TIMELINE  (sorted, deduped, sourced)│
        └───────────────────────────────────────────────┘

The skill is not memorizing every source. It is internalizing two operations that every source must pass through before it can sit on the same axis as the others: convert its clock to a common epoch and time standard, and label it with its source so you can weigh its reliability. The next two major sections handle exactly those two operations.


How computers tell time: epochs, resolution, and the Rosetta-stone problem

A timestamp is a number plus a convention. The number is meaningless until you know two things about the convention: the epoch (the zero point the count starts from) and the resolution (the size of each tick). Get either wrong and your event lands in the wrong decade or with false precision. The hardest part of timeline analysis is not finding timestamps — tools find them by the thousand — it is that the timestamps arrive in a half-dozen incompatible formats, and you must translate them all into one. Think of it as a Rosetta stone problem: the same instant in time is written in FILETIME on NTFS, in Unix seconds in a Linux log, in WebKit microseconds in a Chrome database, and in DOS-packed local time on a FAT thumb drive, and your job is to prove they are the same instant.

The FILETIME and the Unix epoch — the two you will meet most

Windows runs on the FILETIME: a 64-bit count of 100-nanosecond intervals since midnight, 1 January 1601 UTC. It is everywhere in Windows forensics — NTFS $SI`/`$FN attributes, registry key last-write times, .evtx records, LNK files, $Recycle.Bin` `$I metadata. The choice of 1601 is not arbitrary: it is the start of the 400-year Gregorian cycle that contained the date Windows NT was designed in, which makes leap-year arithmetic clean. You met the conversion in Chapter 16; here it becomes a daily reflex.

The Unix (POSIX) world runs on a simpler clock: a signed count of seconds since midnight, 1 January 1970 UTC. Linux file systems (ext4, with nanosecond extensions), syslog, journald, the Sleuth Kit's bodyfile format, and countless application databases store Unix time. The constant that bridges the two epochs is worth committing to memory, because you will type it more than any other number in this field:

116,444,736,000,000,000   = number of 100-ns intervals between 1601-01-01 and 1970-01-01
                            (i.e., FILETIME of the Unix epoch)
   FILETIME → Unix seconds : (filetime - 116444736000000000) / 10,000,000
   Unix seconds → FILETIME : (unix * 10,000,000) + 116444736000000000

Resolution: why 100 ns and 2 seconds are both true

Resolution determines how finely you can sequence two close events — and, as you will see, a change in resolution is one of the loudest tells of tampering. The major file systems differ by orders of magnitude:

FILE-SYSTEM TIMESTAMP RESOLUTION  (sequence-ability)
┌─────────┬───────────────┬───────────────┬───────────────────────────────┐
│ FS      │ Resolution    │ Time standard │ Notes                         │
├─────────┼───────────────┼───────────────┼───────────────────────────────┤
│ NTFS    │ 100 ns        │ UTC           │ FILETIME; sub-second is gold   │
│ FAT32   │ 2 s (write)   │ LOCAL time    │ create has +10 ms field;       │
│         │ 1 day (access)│ (NO tz info!) │ access = date only             │
│ exFAT   │ 10 ms         │ UTC + offset  │ stores a UTC-offset byte       │
│ ext4    │ 1 ns          │ UTC           │ needs 256-byte inode + extra   │
│ APFS    │ 1 ns          │ UTC           │ nanoseconds since 1970         │
│ HFS+    │ 1 s           │ UTC (1904 ep.)│ legacy macOS                   │
└─────────┴───────────────┴───────────────┴───────────────────────────────┘

Two consequences fall straight out of this table. First, on FAT you cannot sequence two writes less than two seconds apart, and the timestamp is in the local time of whatever machine wrote it — with no record of which time zone that was. A thumb drive formatted FAT32 and carried between a laptop in California and a desktop in New York records times that silently mean different absolute instants. This is the single most common normalization error new examiners make, and we devote the next section to it. Second, NTFS's 100-ns resolution is a forensic gift the forger usually forgets to fake. Real file operations on NTFS stamp times with non-zero sub-second fractions; the cheap timestomping tools and the documented Win32 SetFileTime API frequently write whole-second values, zeroing the 100-ns fraction. A column of NTFS timestamps that all end in exactly .0000000 is not impossible by chance, but it is suspicious by design — and it is the first thing we will check in the worked example.

Limitation. Fine resolution is not the same as fine accuracy. NTFS stores 100-ns precision, but Windows does not update every timestamp on every operation with that precision — last-access updates are disabled by default (covered below), and the system file-time granularity for some operations is coarser than the storage granularity. Never report "the file was modified at 18:58:13.4928130, therefore 0.4 seconds after X" as though the fraction were a stopwatch. Treat sub-second precision as a consistency check (is it zeroed or real?), not as a high-precision clock.

The epoch zoo: a conversion table

Beyond file systems, applications invent their own epochs, and a super-timeline crosses all of them. Memorize the shape of this table; keep the exact constants in a script.

EPOCH / FORMAT REFERENCE  ("Rosetta stone" of forensic time)
┌────────────────────────┬───────────┬──────────┬──────────────────────────┐
│ Format                 │ Epoch     │ Unit     │ Where you meet it         │
├────────────────────────┼───────────┼──────────┼──────────────────────────┤
│ Windows FILETIME       │ 1601-01-01│ 100 ns   │ NTFS, registry, .evtx,    │
│                        │  UTC      │          │ LNK, $Recycle.Bin $I      │
│ Unix / POSIX time      │ 1970-01-01│ 1 s      │ ext4, syslog, journald,   │
│                        │  UTC      │          │ bodyfile, SQLite epoch    │
│ WebKit / Chrome time   │ 1601-01-01│ 1 µs     │ Chrome/Edge History,      │
│                        │  UTC      │          │ cookies (= FILETIME / 10) │
│ Firefox PRTime         │ 1970-01-01│ 1 µs     │ Firefox places.sqlite     │
│ Cocoa / Mac Absolute   │ 2001-01-01│ 1 s      │ Safari History.db, many   │
│  (CFAbsoluteTime)      │  UTC      │ (+frac)  │ macOS plists              │
│ HFS+ time              │ 1904-01-01│ 1 s      │ legacy macOS volumes      │
│ DOS / FAT date+time    │ 1980-01-01│ 2 s      │ FAT/exFAT (LOCAL time!)   │
└────────────────────────┴───────────┴──────────┴──────────────────────────┘

Two relationships are worth internalizing because they save real time. WebKit time is simply FILETIME divided by ten (microseconds instead of 100-ns ticks, same 1601 epoch), so a Chrome history value and an NTFS timestamp share a base. And Cocoa time hides a 31-year offset from Unix (978,307,200 seconds between 1970 and 2001) that, if you forget it, lands a 2024 Safari visit in 1993. The browser-specific details belong to Chapter 18 — Browser and Internet Forensics and the macOS specifics to Chapter 17 — macOS and Linux Forensics; what belongs here is the discipline of converting all of them to one standard before you let them share an axis.

# Illustrative epoch-conversion toolkit. Tools do this for you; understand it anyway.
# Returns timezone-aware UTC datetimes so everything is directly comparable.
import datetime as dt

UTC         = dt.timezone.utc
WIN_EPOCH   = dt.datetime(1601, 1, 1, tzinfo=UTC)   # FILETIME / WebKit
UNIX_EPOCH  = dt.datetime(1970, 1, 1, tzinfo=UTC)   # POSIX / Firefox PRTime
COCOA_EPOCH = dt.datetime(2001, 1, 1, tzinfo=UTC)   # Apple CFAbsoluteTime

def from_filetime(ticks):    return WIN_EPOCH  + dt.timedelta(microseconds=ticks / 10)   # 100-ns
def from_unix(seconds):      return UNIX_EPOCH + dt.timedelta(seconds=seconds)
def from_webkit(micros):     return WIN_EPOCH  + dt.timedelta(microseconds=micros)
def from_prtime(micros):     return UNIX_EPOCH + dt.timedelta(microseconds=micros)
def from_cocoa(seconds):     return COCOA_EPOCH+ dt.timedelta(seconds=seconds)

def from_dos(date_word, time_word):           # FAT: packed 16-bit + 16-bit, LOCAL time
    day    =  date_word        & 0x1F
    month  = (date_word >> 5)  & 0x0F
    year   = ((date_word >> 9) & 0x7F) + 1980
    second = ( time_word        & 0x1F) * 2   # 2-second resolution
    minute = ( time_word >> 5)  & 0x3F
    hour   = ( time_word >> 11) & 0x1F
    # NOTE: returned NAIVE on purpose — FAT carries no time zone. You MUST attach
    # the correct local zone (from context) before comparing to UTC sources.
    return dt.datetime(year, month, day, hour, minute, second)

# Worked check: the truthful $FN time from the anchor case.
print(from_filetime(0x01DA770ABB334302))   # -> 2024-03-15 18:58:13.492812+00:00

Tool Tip. Eric Zimmerman's TimeApp (and the conversion panes built into most suites) will turn any of these formats into a human date instantly — paste the raw value, pick the format, read the answer. Keep one such converter open at all times during analysis. But understand the math behind it: the day you are handed a value in a format the GUI does not list (an obscure embedded device, a custom application log — see Chapter 34 — IoT, Vehicle, and Embedded Device Forensics), you will have to identify the epoch yourself by reasoning from a known event, and the reasoning above is how.


Time zones, normalization, and clock skew

A timeline is a claim about when, and "when" is only meaningful relative to a standard. The professional standard is UTC: convert every source to UTC, build and analyze in UTC, and translate to a local zone only at the very end, for the reader's convenience, with the zone stated explicitly. Mixing zones — even accidentally — is how examiners produce timelines where a "later" event appears earlier, and it is a gift to a cross-examining attorney.

Normalizing to UTC

The good news is that the richest Windows sources already store UTC. NTFS $SI`/`$FN FILETIMEs are UTC; registry last-write FILETIMEs are UTC; .evtx event records store UTC. The trap is the sources that do not. FAT and exFAT (the latter only partially) store local time, and a FAT thumb drive is exactly the kind of device that shows up in an exfiltration case. A photo's EXIF DateTimeOriginal (Chapter 20) is typically the camera's local time with no zone. Some application logs write local time. For every such source you must determine the local zone that was in effect and apply the offset.

On a Windows image the system's configured zone lives in the registry:

SYSTEM\CurrentControlSet\Control\TimeZoneInformation
   TimeZoneKeyName          REG_SZ    "Pacific Standard Time"
   Bias                     REG_DWORD  480   (minutes; UTC = local + Bias)
   ActiveTimeBias           REG_DWORD  420   (current effective bias incl. DST)
   DaylightBias             REG_DWORD  -60
   DynamicDaylightTimeDisabled REG_DWORD 0

Bias is the base offset in minutes such that UTC = local_time + Bias; a Bias of 480 is UTC−8 (Pacific Standard Time). ActiveTimeBias folds in daylight saving — 420 minutes (UTC−7) when Pacific Daylight Time is in effect. Read these from the SYSTEM hive (per Chapter 16 — Windows Forensics) and you know how to convert this machine's local-time artifacts to UTC, and how to translate your final UTC timeline back into the zone the reader expects.

# Illustrative: read the evidence machine's configured time zone from an EXTRACTED
# SYSTEM hive (load read-only under a temporary key — never the live evidence drive).
reg load HKLM\EVID_SYS "E:\work\SYSTEM"
$tz = Get-ItemProperty 'HKLM:\EVID_SYS\ControlSet001\Control\TimeZoneInformation'
"{0}  Bias={1}min  ActiveTimeBias={2}min" -f $tz.TimeZoneKeyName, $tz.Bias, $tz.ActiveTimeBias
reg unload HKLM\EVID_SYS
# UTC = local + ActiveTimeBias(minutes). Pacific Daylight: ActiveTimeBias=420 -> UTC-7.

Chain of Custody. Your report must state, in one unambiguous sentence, the time standard of your timeline and how you derived it — for example: "All times are UTC. Local-time sources (the FAT-formatted USB volume and EXIF metadata) were converted using the evidence system's configured zone, Pacific (Bias 480, ActiveTimeBias 420 during DST), read from SYSTEM\ControlSet001\Control\TimeZoneInformation." That single sentence is the difference between a timeline a court can rely on and one that opposing counsel can muddy by asking, "Whose time is that, exactly?" The chain-of-custody and report fundamentals are in Chapter 5 — The Forensic Process and Chapter 26 — The Forensic Report.

Clock skew: when the machine's clock is wrong — or was changed

UTC normalization assumes the source clock was correct. It often was not. A workstation's CMOS battery dies and the clock drifts; a server is set to the wrong zone for years; a laptop is taken offline and its clock wanders; and — the case that matters most — a suspect deliberately rolls the clock backward to backdate everything they touch. Clock skew is the difference between the time a machine recorded and true wall-clock time, and you must measure it before you trust the timeline, then document and apply the offset.

You measure skew by comparing a machine-recorded event to the same event timed by a trusted external clock. The classic external anchors:

  • Email Received headers. When the suspect's machine sent or received mail, the corporate or provider mail server stamped its own Received header with a well-synchronized clock. The gap between the client-side timestamp and the server Received time is a direct skew measurement (email header analysis is Chapter 19 — Email, Chat, and Social Media Forensics).
  • Server-side logs of an action the workstation took — an authentication to a domain controller, a file written to a network share, a DHCP lease. The server's clock is your reference.
  • Windows event ID 4616, "The system time was changed." This event records that the clock was altered, by whom, and the old and new values. A 4616 (especially one not attributable to the Windows Time service w32time) is a flashing indicator of deliberate clock manipulation — and the old/new values let you reconstruct what the timestamps written during the altered window really mean.
DETERMINING CLOCK SKEW  (then apply a single documented offset)
  trusted external time (mail server Received)   2024-03-15 18:51:09 UTC
  workstation-recorded time for same action      2024-03-15 18:51:07
  ──────────────────────────────────────────────────────────────────
  measured skew  = workstation is  ~2 s SLOW     → add +2 s to machine times
  (verify with a SECOND external anchor; if 4616 events exist, the offset is
   not constant — segment the timeline at each clock change.)

Why This Matters. Skew is not a rounding nuisance; it can invert a sequence. If a suspect's machine is twelve minutes fast and a server is correct, an action that truly happened after a server event will appear before it on a naïve merge — and your whole narrative collapses on cross. Conversely, a clean, documented skew measurement is a credibility asset: "I verified the evidence clock against two independent external references and applied a constant +2-second correction" tells a court you understood the problem and handled it. This is theme #4 in practice — technology changes, principles don't: the clocks and formats vary endlessly, but the method (measure against an external reference, document, apply) is constant.


MACB: the four timestamps (and the eight)

Every modern file system records, for each file, a small set of times describing what happened to it. The forensic shorthand for that set is MACB, and it is the vocabulary the timeline tools speak. Learn the letters precisely, because their meanings are not what their everyday names suggest.

MACB — the four file-system timestamps
  M = Modified  → content last written (the data changed)
  A = Accessed  → last read/opened       (often UNRELIABLE — see below)
  C = Changed   → metadata last changed   (MFT/inode entry altered: rename,
                   permissions, attributes — NOT the file content, NOT creation)
  B = Born      → created                  (the file came into existence here)

The letter that trips up everyone is C. On both NTFS and Unix-family file systems, the "C" time is not creation — it is the time the file's metadata record (its MFT entry or its inode) was last changed. Creation is B, "Born." On a traditional Unix stat, the three classic times are mtime (M), atime (A), and ctime (C, the inode-change time); the creation/birth time (crtime/btime) was a later addition (ext4, APFS, NTFS always had it) and is the B. The Sleuth Kit's mactime tool fixed the ordering as m, a, c, b and gave the model its name.

MAPPING: file-system field  →  MACB letter  (what 'mactime' reports)
  NTFS $SI / $FN Creation      → b   (Born)
  NTFS $SI / $FN File-Modified → m   (content Modified)
  NTFS $SI / $FN MFT-Modified  → c   (metadata Changed)
  NTFS $SI / $FN Accessed      → a   (Accessed)
  ext4 i_crtime → b · i_mtime → m · i_ctime → c · i_atime → a

NTFS keeps two sets: $STANDARD_INFORMATION` and `$FILE_NAME

Here is the fact that makes NTFS timeline analysis uniquely powerful, and the fact on which anchor case #2 turns. An NTFS file does not have four timestamps. It has eight — a full MACB set in the $STANDARD_INFORMATION` attribute (type `0x10`) and another full MACB set in the `$FILE_NAME attribute (type 0x30). (Strictly, a file can carry more than one $FILE_NAME — one for its long name and one for the 8.3 short name — so the count can exceed eight; the canonical teaching pair is the two sets.) The structure of these attributes is defined in Chapter 4 — File Systems and tabulated in Appendix G — File System Reference; what matters for the timeline is who writes each set and how easily it can be forged.

WHY NTFS HAS TWO TIMESTAMP SETS  (and why one of them is honest)
┌──────────────────────────┬───────────────────────────────────────────────┐
│ $STANDARD_INFORMATION     │ Updated by normal file operations. Shown by   │
│  (attribute 0x10)         │ Explorer, dir, PowerShell. SETTABLE from user │
│                           │ space via SetFileTime() / timestomp tools.    │
│                           │  ⇒ FORGEABLE. This is what an attacker edits.  │
├──────────────────────────┼───────────────────────────────────────────────┤
│ $FILE_NAME                │ Set by the KERNEL when the name is created/    │
│  (attribute 0x30)         │ moved/renamed (and on $SI updates it mirrors). │
│                           │ No documented user-space API writes it.        │
│                           │  ⇒ HARD TO FORGE. This is the truthful clock.  │
└──────────────────────────┴───────────────────────────────────────────────┘

The practical rule is short enough to recite on the stand: Explorer shows $STANDARD_INFORMATION`, which any user can change; `$FILE_NAME is maintained by the kernel and is the timestamp to trust when the two disagree. That single asymmetry is what defeats the most common anti-forensic move in corporate cases — backdating a file so it looks old and innocuous.

What operations do to timestamps — the behavior rules

To read a timeline you must know what normal looks like, because timestomping is detected as a deviation from normal, and benign operations produce patterns that look alarming to the untrained eye. The behavior of NTFS $SI times under common operations (Windows 10/11 defaults):

OPERATION                 M (mod)   A (acc)   C (MFT)   B (born)
─────────────────────────────────────────────────────────────────
Create a new file          set       set       set       set      (all = now)
Modify content             update     —*        update    —        (B unchanged)
Rename / move (same vol)   —          —*        update    —        (B unchanged)
Copy file → new location   inherit    set       set       SET=now  ← B > M is NORMAL
Download from web          inherit    set       set       SET=now  ← B > M is NORMAL
Read / open (no write)     —          —*        —          —
─────────────────────────────────────────────────────────────────
* A (last access) updates are DISABLED BY DEFAULT since Vista
  (SYSTEM\...\FileSystem\NtfsDisableLastAccessUpdate). Do not rely on A.

Two rows in that table prevent a career-limiting mistake. First, a copied or downloaded file normally has a Born time later than its Modified time, because copying creates a brand-new file (new B = now) whose content was last written earlier (M inherited from the source). A new examiner who sees Created 2024-03-15, Modified 2024-03-10 and shouts "timestomping!" is wrong: that is the signature of a perfectly ordinary copy or download. Stomping is suspected from other contradictions, below — not from B-after-M alone. Second, last-access time is unreliable because Windows stopped updating it by default; an unchanged A time tells you almost nothing, and you must not present a stale access time as "the file was last opened on this date."

Recovery vs. Forensics. MACB times serve both disciplines, read with different intent. A 💾 recovery technician leans on the Modified time to triage: when a reformat or corruption leaves several recoverable versions of a document, the newest mtime is the version the client actually wants, and shadow-copy creation times tell you which snapshot to restore first. A 🔍 forensic examiner leans on Born and Changed to sequence events and to attribute them — when did this file come into existence, in what order relative to the USB connection, and does its $FN` agree with its `$SI? Same four numbers; one reads them to restore the newest, the other to prove the order.


Detecting timestamp manipulation (timestomping)

Timestomping is the deliberate alteration of file timestamps to mislead an investigator — to make a freshly created exfiltration archive look like an old system file, or, as in our anchor, to make a just-copied trade-secret file look two years old so it blends into a long history of legitimate work. The technique is old and the tools are cheap (the Metasploit timestomp command, SetMACE, nTimestomp, and a one-line PowerShell call to [IO.File]::SetCreationTimeUtc). Detecting it is one of the most reliable wins in forensics, because the cheap tools almost all share the same blind spots.

The principle: forge $SI`, but the kernel keeps `$FN honest

The core detection principle follows directly from the two-timestamp-set design above. The documented Windows API and the common tools write only $STANDARD_INFORMATION`. They do *not* write `$FILE_NAME, because no documented user-space call exposes it — it is the kernel's bookkeeping. So when a file's $SI` says 2022 and its `$FN says 2024, the file was created in 2024 and someone changed $SI to lie about it. You compare the two sets, and the disagreement is the evidence.

The tells: a checklist of contradictions

No single tell is conclusive (each has an innocent explanation in some corner case), but they corroborate, and together they are decisive:

  1. $SI` earlier than `$FN. A file's $STANDARD_INFORMATION` creation predates its `$FILE_NAME creation — the file claims to have been "created" before the kernel recorded its name existing. This is the headline tell.
  2. Zeroed sub-second precision. Genuine NTFS operations stamp non-zero 100-ns fractions. Many stomping tools and the high-level APIs write whole seconds, so every forged $SI time ends in .0000000. A cluster of perfectly round timestamps among neighbors with messy fractions is a strong signal.
  3. $SI` Modified earlier than `$SI Created (M < B) on a file that was not copied/downloaded. Distinguish from the benign copy pattern by checking whether $FN` agrees; on a real copy, `$FN will corroborate the recent birth.
  4. Times that predate the OS install or the volume's creation. A file "created" before the file system existed is impossible; the forger picked a date out of thin air.
  5. $SI` and `$FN diverging by an implausible span (years), or a whole directory of files sharing one identical, suspiciously round timestamp.

The annotated MFT record — $SI` lying, `$FN telling the truth

Here is the centerpiece. Below is the relevant slice of the MFT entry for the engineer's local copy, C:\Users\jrivera\Desktop\TurbineHousing_v7.sldprt, as it appears in the verified image WS-ENG-04.E01. The entry begins with the FILE signature; we show the two timestamp-bearing attributes, $STANDARD_INFORMATION` (type `0x10`) and `$FILE_NAME (type 0x30), with their FILETIME bytes decoded.

MFT ENTRY (excerpt) — TurbineHousing_v7.sldprt          [image WS-ENG-04.E01]
Offset    Bytes (little-endian)                          Decoded

  0x000   46 49 4C 45 ...                                "FILE" — MFT entry header

  --- $STANDARD_INFORMATION  (attribute type 0x10) ----------------------------
  +0x00   00 10 98 21 2E 1F D8 01   B (Created)  = 2022-02-11 10:00:00.0000000  ← forged
  +0x08   00 10 98 21 2E 1F D8 01   M (Modified) = 2022-02-11 10:00:00.0000000  ← forged
  +0x10   00 10 98 21 2E 1F D8 01   C (MFT-chg)  = 2022-02-11 10:00:00.0000000  ← forged
  +0x18   00 10 98 21 2E 1F D8 01   A (Accessed) = 2022-02-11 10:00:00.0000000  ← forged
                       ▲ note: every value identical, sub-seconds ALL .0000000

  --- $FILE_NAME  (attribute type 0x30) ---------------------------------------
  +0x08   02 43 33 BB 0A 77 DA 01   B (Created)  = 2024-03-15 18:58:13.4928130  ← KERNEL
  +0x10   02 43 33 BB 0A 77 DA 01   M (Modified) = 2024-03-15 18:58:13.4928130  ← KERNEL
  +0x18   02 43 33 BB 0A 77 DA 01   C (MFT-chg)  = 2024-03-15 18:58:13.4928130  ← KERNEL
  +0x20   02 43 33 BB 0A 77 DA 01   A (Accessed) = 2024-03-15 18:58:13.4928130  ← KERNEL
                       ▲ messy real sub-seconds; matches USB last-arrival window

Read the contradiction. The $SI` set claims the file was born, modified, and last touched at 10:00:00 on 11 February 2022 — and every one of those four values is *byte-for-byte identical* with a *zeroed* 100-ns fraction, the signature of a tool that wrote whole seconds. The kernel-maintained `$FN set says the file came into existence at 18:58:13.4928130 UTC on 15 March 2024, with a realistic non-zero fraction — and that instant falls squarely inside the window during which the SanDisk device was connected (18:51–19:33, from Chapter 16's USBSTOR analysis). The $SI times are not merely different; they are earlier than the file's own kernel birth, which is impossible without intervention. This is timestomping, proven from the file system itself.

# Illustrative $SI-vs-$FN inconsistency check. Inputs are decoded UTC datetimes.
def stomping_flags(si, fn):     # si, fn: {'b','m','c','a'} -> datetime (UTC)
    flags = []
    if si['b'] < fn['b']:
        flags.append("$SI Born EARLIER than $FN Born  (file 'created' before kernel recorded it)")
    if si['m'] < si['b']:
        flags.append("$SI Modified before $SI Born     (impossible in normal single-file use)")
    if all(t.microsecond == 0 for t in si.values()):
        flags.append("$SI sub-seconds all zeroed (.0000000)  (whole-second API/tool tell)")
    if abs((si['b'] - fn['b']).days) > 7:
        flags.append(f"$SI/$FN Born differ by {abs((si['b']-fn['b']).days)} days")
    return flags
# For TurbineHousing_v7.sldprt this returns all four flags -> stomping, high confidence.

Corroborating clocks: the USN journal and $LogFile

$FN is your strongest single tell, but a professional never rests a conclusion on one artifact when others are available. NTFS keeps two transaction records that independently time file operations and are even harder for casual tampering to reach:

  • **$Extend\$UsnJrnl:$J`** — the **USN change journal** — logs, for each change, the affected file reference, a *reason* flag (`FILE_CREATE`, `DATA_OVERWRITE`, `RENAME_NEW_NAME`, `FILE_DELETE`, …), and a timestamp. A `USN_REASON_FILE_CREATE` record for `TurbineHousing_v7.sldprt` dated 2024-03-15 18:58 corroborates `$FN and contradicts the forged $SI — a second, independent witness to the true birth time.
  • $LogFile — NTFS's metadata transaction log — records recent low-level operations and can place a create/rename in time even when other artifacts are ambiguous.

Parse the USN journal with The Sleuth Kit or with EZ tools (MFTECmd reads $J`), and treat agreement between `$FN and $J as the gold standard for proving a true creation time. The deep treatment of anti-forensic techniques and their detection — wiping, log clearing, secure deletion, and timestomping at the more sophisticated end — is Chapter 30 — Anti-Forensics; here you have the timeline-relevant core.

Limitation. $FN` is strong, not sacred. A sufficiently capable adversary using a kernel-level tool (some versions of `SetMACE` can write `$FN, and a raw-disk editor can rewrite both attribute sets and even the $J` records) can forge `$FN too. The defense is the same as always: corroborate across independent clocks. When $SI`, `$FN, $UsnJrnl`, `$LogFile, an event-log entry, and a LNK volume serial all point at one time, no single forgery explains the convergence. And know the innocent exceptions: file-system tunneling, restore-from-backup operations, and certain installer behaviors can legitimately produce $SI` older than `$FN, so investigate the pattern and corroboration, never a lone attribute. State your confidence honestly — "consistent with timestamp manipulation, corroborated by the USN journal" is defensible; "the file was definitely stomped" on $FN alone is not.


Building the super-timeline: the tools

You now know what timestamps mean and how to spot lies in them. Time to assemble the axis. Two workflows dominate the field: the bodyfile/mactime pipeline (file-system timeline only, fast, surgical) and log2timeline/plaso (everything, comprehensive, voluminous). Use both; they answer different questions. As always, you run them against your verified image or extracted copies — never the original (theme #2, the original is sacred), and you record hashes of every working file.

The file-system timeline: bodyfile + mactime (The Sleuth Kit)

The Sleuth Kit's fls walks the file system and emits a bodyfile — a pipe-delimited intermediate format with one line per file and all of its MACB times in Unix epoch. mactime then sorts those into a chronological, human-readable timeline, optionally filtered to a date range and a target time zone.

# 1) Generate a bodyfile from the NTFS partition (offset from `mmls`), recursively,
#    INCLUDING deleted entries still in the MFT (-r recurse). Work from the IMAGE.
fls -m "C:" -r -o 2048 WS-ENG-04.E01 > WS-ENG-04.bodyfile
sha256sum WS-ENG-04.bodyfile >> chain-of-custody.txt

# 2) Render a timeline for the window of interest, normalized to UTC.
mactime -b WS-ENG-04.bodyfile -d -z UTC 03/15/2024-03/16/2024 > timeline_fs.csv
#   -b bodyfile   -d comma-delimited   -z time zone   DATE-RANGE = MM/DD/YYYY

The bodyfile format itself is worth knowing by heart, because every timeline tool that ingests file-system times speaks it:

BODYFILE FORMAT (TSK 3.x) — 11 pipe-delimited fields, times in Unix epoch
  MD5 | name | inode | mode_as_string | UID | GID | size | atime | mtime | ctime | crtime
   │     │       │         │                            │       │       │       │
   │     │       │         └ rwx string                 A       M       C       B
   │     │       └ MFT record number (NTFS) / inode
   │     └ full path (+ " (deleted)" for deleted entries — note #1: deleted ≠ gone)
   └ optional hash (0 if not computed)

And the mactime output reads like the spine of a report. Note the MACB column: each row shows which of the four times fired at that instant (b... = only Born; macb = all four at once, the signature of a fresh creation; m.c. = Modified plus metadata-Changed; etc.), so you see at a glance whether a line is a creation, a content change, or a metadata touch. Because fls/mactime report $STANDARD_INFORMATION by default, the forged file lands at its fake 2022 date — conspicuously outside the Friday-evening cluster where its own LNK, the USB connection, and the ShellBag all sit:

mactime -d -z UTC excerpt  (WS-ENG-04)   [default view = $SI times]
Date/Time (UTC)        Size      MACB   Meta    File Name
2022-02-11 10:00:00    1990656   macb   74511   C:/Users/jrivera/Desktop/TurbineHousing_v7.sldprt  ← $SI (forged, displaced)
2024-03-15 19:04:00     169      macb   75002   C:/Users/jrivera/AppData/Roaming/.../Recent/TurbineHousing_v7.lnk
2024-03-15 19:05:13     412      m.c.   75040   C:/Users/jrivera/AppData/.../AutomaticDestinations/<AppID>.automaticDestinations-ms

That displacement is the tell: a file whose LNK proves it was opened from a USB drive on Friday 2024 carries a $SI` creation date of 2022 and does not appear in the Friday cluster at all. To recover its *true* time you read the `$FILE_NAME set, which mactime's default view omits. MFTECmd.exe -f $MFT --csv .` emits *both* `$SI and $FN columns side by side — the fastest way to scan an entire volume for divergence — and it places this file's true birth at 2024-03-15 18:58:13, back inside the cluster where the corroborating artifacts live.

Recovery vs. Forensics. The bodyfile pipeline serves both trades. A 🔍 examiner builds it to sequence an incident. A 💾 recovery technician builds the very same bodyfile to inventory a damaged volume — fls -r enumerates every file the MFT still references, deleted ones included (theme #1, deleted ≠ destroyed), and sorting by mtime surfaces the client's most recent work first, so you carve and restore in order of value rather than blindly. One command, two missions.

The everything-timeline: log2timeline / plaso

fls/mactime see the file system and nothing else. plaso (the engine; log2timeline.py is its collector) sees everything: it runs a hundred-plus parsers across an image and extracts every timestamp it can find — file-system MACB (both $SI` and `$FN), registry key last-write and decoded values, .evtx records, prefetch, LNK, Jump Lists, $Recycle.Bin, browser histories, email, and more — into a single .plaso storage file. psort.py then sorts, filters, deduplicates, and renders it.

# 1) Collect EVERYTHING into one storage file (run against the image; this is slow).
log2timeline.py --storage-file WS-ENG-04.plaso WS-ENG-04.E01

# 2) Render the full super-timeline to CSV (l2tcsv = the classic 17-column format).
psort.py -o l2tcsv -w supertimeline.csv WS-ENG-04.plaso

# 3) In practice you NEVER read the whole thing. Filter to the window + sources.
psort.py -o dynamic -w focused.csv WS-ENG-04.plaso \
   "date > '2024-03-15 00:00:00' AND date < '2024-03-16 23:59:59'"

The output is enormous — a few months of one workstation easily exceeds a million events — which is the central challenge of super-timelines and the reason filtering is a core skill, not an afterthought. plaso's value is that it labels every event with its source and a timestamp_desc telling you which clock fired, so a focused, dynamic-format slice reads cleanly:

psort.py -o dynamic (focused slice, UTC)  — anchor window
datetime              timestamp_desc          source   message / display_name
2024-03-15 18:51:07   Last Connection         REG      USBSTOR SanDisk Cruzer Glide  SN 4C53...0123
2024-03-15 18:55:32   Last Written            REG      Shellbag  E:\ProjectArchive\TurbineHousing
2024-03-15 18:58:13   Creation Time ($FN)     FILE     C:\Users\jrivera\Desktop\TurbineHousing_v7.sldprt
2024-03-15 19:04:00   Last Access ($SI)/Open  LNK      TurbineHousing_v7.lnk  -> E:\...  vol SN 9C2A-77F1
2024-03-15 19:33:18   Last Connection (rem.)  REG      USBSTOR last-removal
2024-03-16 09:12:41   Deletion Time           RECBIN   $I... TurbineHousing_v7.sldprt
2024-03-16 09:14:22   Last Run Time           PREF     CCLEANER64.EXE-A1B2C3D4.pf   run count 3
2022-02-11 10:00:00   Creation Time ($SI)     FILE     C:\Users\jrivera\Desktop\TurbineHousing_v7.sldprt

The last line — the forged $SI` creation landing in 2022 while the same file's `$FN creation sits at 2024-03-15 18:58 — is exactly the divergence plaso surfaces by parsing both MFT timestamp sets. The super-timeline does not hide the lie; it places the lie and the truth on the same axis, where the contradiction is obvious.

Viewing and triaging: Autopsy and Timeline Explorer

For analysts who prefer a GUI, Autopsy builds a timeline from its ingested data sources (file-system times plus parsed web, registry, and message artifacts) and presents two views: a histogram of event counts over time — invaluable for spotting bursts of activity, like the flurry the Friday evening of our case — and a detail list you can filter by type and zoom into a window. The wider tool landscape (Autopsy/TSK, FTK, EnCase, X-Ways, Magnet AXIOM, plaso) is surveyed in Chapter 36 — The Forensic Toolkit, and command syntax lives in Appendix H — Command-Line Reference.

For working the CSV that plaso, mactime, or the EZ tools produce, Timeline Explorer (Eric Zimmerman) is the field standard: load multimillion-row CSVs, filter and full-text-search instantly, tag and color rows, and pin the handful of events that become your report's exhibits. The pattern most examiners settle into is: collect with plaso, slice with psort.py filters, and read/triage the slice in Timeline Explorer.

Tool Tip. Build the super-timeline, but never read the super-timeline raw. The professional workflow is anchor → window → expand: find one event you are confident about (a USB connection, a logon, a known file creation), filter to a tight window around it (say ±10 minutes), read what clustered, then widen as the story demands. An examiner who scrolls a million-line CSV top to bottom will miss the signal and waste days; an examiner who pivots from a known anchor finds the story in minutes. Correlate at least one load-bearing line across two independent tools (e.g., plaso and mactime, or plaso and Autopsy) so a single parser's quirk can never carry your conclusion — a recurring Daubert expectation discussed in Chapter 27 — Expert Testimony.


Reading a timeline: pivoting, windows, and corroboration

Tools build the timeline; judgment reads it. Three habits separate an analyst from a spreadsheet-scroller.

Pivot from anchors, do not read linearly. An anchor is an event you trust and that matters: a logon (.evtx 4624), a device connection (USBSTOR), a known-bad binary's prefetch, a creation time you have corroborated. From each anchor, expand a temporal window and ask what clustered around it. Activity is bursty — humans and malware both work in concentrated sessions separated by long idle gaps — so the events that matter tend to arrive in tight clumps, and an anchor drops you into the right clump.

Weigh corroboration; distinguish a finding from a coincidence. A single line on a timeline is a lead. Multiple independent sources agreeing on a time is a finding. When the registry says a device connected at 18:51, the per-user MountPoints2 last-write agrees, a ShellBag shows that volume browsed at 18:55, a LNK carries the volume's serial, and a Jump List shows the file opened at 19:04, you have a corroborated sequence that no single artifact's ambiguity can break. Conversely, two events near each other in time are not necessarily related — temporal proximity suggests a hypothesis; it does not prove causation. State correlations as correlations.

Read the gaps. A timeline's silences speak. Browser history that simply stops mid-session, a Security log with a 1102 "log cleared" event and nothing after it, a run of MRU keys whose values are empty but whose last-write times all share one minute — these are not absences of evidence; they are evidence of an action that removed evidence, and the action has a timestamp. This is theme #3 at its sharpest: the absence of a trace is itself a trace, and on a timeline the gap has coordinates.

ANCHOR → WINDOW → EXPAND  (how to actually read a million-line timeline)
   [pick anchor]  USBSTOR connect 18:51  ───►  filter 18:41–19:43
        │                                          ├ 18:55 ShellBag browse  (corroborates)
        │                                          ├ 18:58 $FN create        (corroborates)
        │                                          ├ 19:04 LNK/JumpList open  (corroborates)
        │                                          └ 19:33 USBSTOR remove     (bounds session)
   [read the gap] browser history ends 09:14  ───►  same minute as CCleaner run
                                                     = the gap is dated evidence

Worked example: the timeline that proves the theft

Return to anchor case #2 one last time and finish it. From Chapter 16 — Windows Forensics you extracted, parsed, and hashed the Windows artifacts for user jrivera on the verified image WS-ENG-04.E01; from Chapter 18 and Chapter 19 you pulled the browser and email records. You have a dozen CSV exports. Counsel's question is unchanged: did jrivera copy proprietary turbine-housing files to a personal device before resigning? The artifacts answered fragments; the timeline answers the question.

Step 1 — Normalize and verify the clock. Before merging anything, you establish the time standard. NTFS, registry, and .evtx sources are already UTC. You read SYSTEM\ControlSet001\Control\TimeZoneInformation and confirm the workstation's local zone (Pacific) for translating the report's reader-facing column later, and for converting the one local-time source in the case — a FAT-formatted other thumb drive that proves irrelevant. Crucially, you measure clock skew: comparing the workstation's send time for an email jrivera sent Friday evening against the corporate mail server's Received header shows the machine clock was within ~2 seconds of true UTC, and there are no event ID 4616 "system time changed" records on the timeline. The recorded times can be trusted as-is; you document that finding. (Had a 4616 existed, you would segment the timeline at the clock change and re-anchor — but it does not.)

Step 2 — Build and merge. You run log2timeline.py against the image for the comprehensive view and fls/mactime for a clean file-system spine, then load both into Timeline Explorer alongside the EZ-tool CSVs from Chapter 16. You filter to the anchor window — Friday evening through Saturday morning — and pivot from the strongest anchor, the USBSTOR connection.

Step 3 — Resolve the timestomping in the timeline itself. MFTECmd on $MFT` gives you `$SI and $FN` side by side for `TurbineHousing_v7.sldprt`. The contradiction from the hex dump above resolves cleanly on the merged axis: the forged `$SI (2022-02-11 10:00:00.0000000, zeroed fractions, four identical values) is demoted to a flagged artifact-of-manipulation, and the kernel $FN` (2024-03-15 18:58:13.4928130), corroborated by a `USN_REASON_FILE_CREATE` record in `$UsnJrnl:$J at the same instant, anchors the true creation. You do not delete the forged time from the timeline — you keep it and label it, because the act of forging it is itself a dated event in the story.

Step 4 — Read the merged super-timeline. Every line below is sourced to a specific artifact from a hashed image, normalized to UTC, and — where it carries the conclusion — corroborated by at least two independent sources:

WS-ENG-04  MASTER TIMELINE (UTC)  — user jrivera          [image SHA-256 on file]
TIME (UTC)            EVENT                                          SOURCE(S)
2024-03-15 18:51:07   SanDisk Cruzer Glide (HW SN 4C53...0123)       USBSTOR + MountedDevices
                      connected → drive E:, by user jrivera          + NTUSER MountPoints2 (last-write)
2024-03-15 18:55:32   Browsed  E:\ProjectArchive\TurbineHousing\     UsrClass ShellBags (SBECmd)
2024-03-15 18:58:13   C:\...\Desktop\TurbineHousing_v7.sldprt        MFT $FILE_NAME (kernel)
                      CREATED  (true birth)                          + $UsnJrnl:$J FILE_CREATE  ✔corroborated
2024-03-15 19:04:00   Opened  TurbineHousing_v7.sldprt  FROM E:      LNK (vol SN 9C2A-77F1, removable)
                      (+ 3 sibling .sldprt opened from E:)           + SolidWorks Jump List (DestList)
2024-03-15 19:20:11   Visited personal cloud-storage upload page     Browser history (Ch.18, WebKit µs)
2024-03-15 19:25:44   Email to personal account, .sldprt attached    Webmail/Sent + Received hdr (Ch.19)
2024-03-15 19:33:18   Device E: removed                              USBSTOR last-removal property
─────────────────────────────────────────────────────────────────────────────────────────────
2024-03-16 09:12:41   TurbineHousing_v7.sldprt → Recycle Bin         $Recycle.Bin\\ $I (RBCmd)
2024-03-16 09:12:44   BearingAssembly_rev3.sldprt → Recycle Bin      $Recycle.Bin\\ $I
2024-03-16 09:14:22   CCleaner run #3 ("wipe free space" enabled)    Prefetch (PECmd) + UserAssist + Amcache
2024-03-16 09:14:2x   Browser history ENDS; several MRU lists empty  GAP — value-less keys, last-write 09:14
                      but last-written 09:14 (wiped, not unused)     = dated absence-as-trace
─────────────────────────────────────────────────────────────────────────────────────────────
NOTE  Local C: copy shows Explorer "Modified 2022-02-11" — this is $SI, FORGED
      (zeroed sub-seconds, $SI Born < $FN Born). True birth 2024-03-15 18:58:13
      per $FN + USN journal.  Timestomping flagged; truth anchors the sequence.

Step 5 — Read it as a story. The timeline narrates itself. Friday at 18:51 the personal SanDisk goes in; four minutes later the secret project folder on that drive is browsed; at 18:58 a local copy of the lead turbine-housing file is created (kernel time, corroborated by the change journal); at 19:04 that file and three siblings are opened from the removable drive; in the next twenty minutes a cloud-upload page is visited and the file is emailed to a personal account; at 19:33 the device leaves. The next morning the local copies are sent to the Recycle Bin, CCleaner runs with free-space wiping on, and in that same minute the browser history stops and several MRU lists are emptied — the gaps dated to the cleanup. Threaded through it all is the backdated $SI`, exposed by the truthful `$FN and the USN journal. The forging, the copying, the exfiltration channels, and the cleanup are not four suspicions; they are one corroborated, time-anchored sequence.

War Story. In a real version of this matter, the defense did not dispute that the files were on the drive — they argued the dates were unreliable, hoping to suggest the activity predated the employee's resignation and was part of routine work. The argument died on the $FILE_NAME` attribute and the USN journal. The examiner showed, side by side, the Explorer-visible `$SI date the defense was relying on and the kernel $FN date plus the change-journal FILE_CREATE record — two independent, hard-to-forge sources agreeing to the second. Once the panel understood that the "old" date was the one the suspect had set and the "new" date was the one the operating system had recorded, the timeline stood. The case turned not on finding the files but on proving when — which is to say, it turned on this chapter.

Ethics Note. A timeline this precise is also this consequential, which returns us to theme #6 — the human cost is real. Behind WS-ENG-04 is a person whose career and reputation hinge on your sequence, and a company whose work may have been taken. Your duty is to the artifacts and to their limits. "The file was created locally at 18:58:13 on 2024-03-15 per $FN and the USN journal, and opened from removable volume SN 9C2A-77F1 at 19:04" is a finding the data supports. "He planned the theft for weeks" is a motive the timeline cannot show and you must not assert. Report the sequence; let others argue intent. Keeping findings and inferences rigorously separate is the subject of Chapter 26 — The Forensic Report and a professional obligation revisited in Chapter 28 — Ethics.


Common mistakes

  • Mixing time zones on one timeline. The cardinal sin. Build in UTC, label every column, and convert to local only at the end for the reader. A timeline that silently blends a UTC .evtx time with a local-time FAT timestamp can invert a sequence and will be torn apart on cross.
  • **Trusting Explorer's dates (the $SI` trap).** Explorer, `dir`, and `Get-ChildItem` show `$STANDARD_INFORMATION, which any user can forge. When timing matters, compare $SI` against `$FN, the USN journal, and the event logs. Inconsistency is not noise — it is the finding.
  • Crying "timestomping" at a benign copy. A Created-after-Modified pattern is the normal signature of a copied or downloaded file (new birth, inherited modified time). Confirm with $FN`: a real copy's `$FN corroborates the recent birth; a stomp shows $SI` older than `$FN.
  • Relying on last-access time. Windows disables $SI access-time updates by default (and many Linux mounts use relatime/noatime). A stale A time does not mean "never opened." Never present access time as a reliable "last opened" without confirming the system actually updated it.
  • Ignoring clock skew and ID 4616. If you never verify the evidence clock against an external reference, you do not actually know what your timestamps mean. Always check for "system time changed" events and measure skew against a trusted source before you trust the axis.
  • Reading the super-timeline linearly. A million-line plaso CSV scrolled top to bottom buries the signal. Anchor on a trusted event, window tightly, expand deliberately. Use Timeline Explorer's filters; do not eyeball.
  • Treating proximity as causation. Two events one second apart may be cause-and-effect or pure coincidence. State correlations as correlations; reserve causal language for sequences with mechanism and corroboration.
  • Forgetting epochs. A WebKit value read as Unix lands in the wrong century; a Cocoa value read as Unix is off by 31 years. Identify each source's epoch before you place it on the axis, and convert with the right constant.
  • Working on the original. Building a bodyfile or running plaso against the evidence drive instead of a verified image violates theme #2 and taints everything downstream. Image first, hash, work the copy.

Limitations: knowing when to stop

A timeline is an inference engine, and like every inference engine it has a domain outside which it must not be pushed. State its limits as plainly as its findings — theme #5.

Timestamps record events, not intentions, and sometimes not even the event you think. A creation time tells you a file came into existence; it does not tell you who willed it or why. The "C" (MFT/inode-change) time fires for metadata operations you may not anticipate. Last-access times are usually disabled. Resolution is not accuracy. Every line on a timeline is a fact about the storage medium, and the leap from that fact to a human action is an inference you must justify, not assume.

Manipulation can run deeper than $SI`.** Most timestomping is shallow and exposed by `$FN, but a capable adversary with kernel-level tools or raw-disk access can forge $FN`, edit the USN journal, or wipe `$LogFile. Your defense is convergence: when many independent clocks agree, forgery becomes implausible; when they disagree, you report the disagreement rather than picking a favorite. Note, too, the innocent causes of $SI`/`$FN divergence — file-system tunneling, restores from backup, certain installers — so you investigate patterns, not lone attributes.

Sources can simply be absent or destroyed. Prefetch may be disabled; the USN journal has a finite size and wraps, discarding old records; logs roll over or are cleared; FAT media carry no time zone and coarse resolution; an anti-forensically disciplined subject who used full-disk encryption you cannot break (Chapter 29), a live OS that wrote nothing to the system disk, or physical destruction can leave you with a timeline full of holes. Volume Shadow Copies and backups sometimes fill the gaps; sometimes nothing does.

When the artifacts will not support a sequence, the professional answer is the one a beginner is most tempted to avoid: "the available temporal evidence is insufficient to establish the order of events" — or "the timeline establishes that X occurred before Y, but cannot establish whether the same person was responsible for both." A defensible non-conclusion is worth more than a confident overreach that collapses on cross-examination. Build the most complete, corroborated, honestly-bounded timeline the evidence allows — and stop exactly there.


Progressive project: build the master timeline for the case

This is the chapter where your Forensic Case File (begun in Chapter 5, acquired in Chapters 14–15, and populated artifact-by-artifact since) becomes a single coherent narrative. You will fuse every dated source you have collected into one master timeline.

  1. Establish the time standard. Read the evidence system's TimeZoneInformation from the SYSTEM hive and record the local zone and bias. Measure clock skew against at least one external reference (a mail-server Received header, a server-side authentication log, or — if present — event ID 4616). Document the standard in one sentence: "All times UTC; skew verified ±N s against [reference]; no system-time-change events / clock changed at [time]."
  2. Build two timelines. Generate a file-system spine with fls -m -rmactime -z UTC, and a comprehensive super-timeline with log2timeline.pypsort.py. Hash both intermediate files into your chain-of-custody worksheet (Appendix F). Pull the $SI` *and* `$FN MACB sets with MFTECmd so divergences are visible.
  3. Hunt for timestomping. Scan the $MFT` export for the tells: `$SI Born earlier than $FN` Born, zeroed sub-second fractions, dates predating the OS install. For every flagged file, corroborate the true time with `$UsnJrnl:$J. Record each manipulated file and its forged-vs-true times — the manipulation is itself a finding.
  4. Merge, slice, and pivot. Load everything into Timeline Explorer. Filter to your incident window. Pivot from your strongest anchor and read the cluster. Tag the handful of events that carry your conclusions, and confirm each load-bearing line with a second independent source and (where possible) a second tool.
  5. Write the master timeline exhibit. Produce a clean, UTC, source-annotated timeline table — one line per significant event, each sourced to an artifact, manipulated times flagged and explained. This exhibit becomes the spine of your forensic report in Chapter 26 and a centerpiece of the capstone in Chapter 38. Note beside any line whose limits you must state (proximity-not-causation, access-time-unreliable, single-source) so future-you writes it honestly.

Save the bodyfile, the .plaso, the psort slices, the MFTECmd export, and the master-timeline exhibit into the case-file folder. Do not let the sequence live only in your head; the timeline is the deliverable.


Summary

This chapter taught you to fuse a pile of dated fragments into one chronological argument — the forensic super-timeline — and to read it like an investigator. You learned that a timestamp is a number plus a convention, and that timeline work begins with the Rosetta-stone problem: converting every source's clock to one standard. You mastered the epochs you will meet daily — the Windows FILETIME (100-ns ticks since 1601 UTC) and Unix time (seconds since 1970 UTC), bridged by the constant 116,444,736,000,000,000 — and the wider epoch zoo of WebKit, Firefox PRTime, Cocoa, HFS+, and DOS/FAT formats, each with its own zero point and resolution. You learned that resolution varies from NTFS's 100 ns to FAT's two seconds, that FAT stores local time with no zone while NTFS, the registry, and .evtx store UTC, and that normalizing everything to UTC — using the evidence machine's TimeZoneInformation — is non-negotiable. You learned to measure clock skew against external references and to read event ID 4616 as the fingerprint of deliberate clock manipulation. You internalized the MACB model (Modified, Accessed, Changed-as-in-metadata, Born-as-in-created) and the NTFS subtlety that every file carries two MACB sets — a forgeable $STANDARD_INFORMATION` and a kernel-maintained, truthful `$FILE_NAME — and you turned that asymmetry into reliable timestomping detection: $SI` earlier than `$FN, zeroed sub-seconds, impossible orderings, all corroborated by the $UsnJrnl:$J change journal. You built timelines two ways — the surgical fls/mactime bodyfile spine and the comprehensive log2timeline/plaso super-timeline — and triaged them in Autopsy and Timeline Explorer with the anchor → window → expand method, weighing corroboration over coincidence and reading the dated gaps that wiping leaves behind. Finally, you closed anchor case #2: the master timeline that demoted a forged 2022 $SI` date, anchored the truth on the kernel's `$FN and the change journal, and laid the connection, the copy, the exfiltration, and the cleanup on a single corroborated axis. The formats and tools will keep changing; the method will not — convert to one standard, account for skew, trust the kernel's clock over the user's, corroborate across independent sources, and state every conclusion with its limits.

You can now: - Identify a timestamp's epoch and resolution and convert any common format (FILETIME, Unix, WebKit, PRTime, Cocoa, DOS/FAT) to a single UTC standard. - Normalize a mixed-source timeline to UTC using the evidence system's configured time zone, and measure, document, and apply clock skew (including detecting ID 4616 clock changes). - Explain the MACB model precisely — including that "C" is metadata-change and "B" is creation — and map file-system fields to MACB letters for NTFS, ext4, and APFS. - Detect timestamp manipulation by comparing $STANDARD_INFORMATION` against the kernel-maintained `$FILE_NAME and corroborating with the USN journal, and distinguish stomping from the benign copy/download pattern. - Build a super-timeline with both fls/mactime and log2timeline/plaso, filter and pivot it in Timeline Explorer/Autopsy, and read dated gaps as evidence. - Assemble a corroborated, UTC-normalized, source-annotated master timeline that serves as the spine of a court-admissible forensic report — and recognize when the temporal evidence is insufficient to support a sequence.

What's next. Chapter 22 — Memory Forensics — moves from the timestamps on disk to the volatile world of RAM, where running processes, network connections, injected code, and encryption keys live only until power is lost. You will capture memory, analyze it with Volatility, and add a whole new layer of now to the then you just learned to reconstruct — proof again that technology changes, principles don't.


Practice in exercises.md, test yourself with the quiz, apply it in the case studies, review the key takeaways, and go deeper with further reading.