The second question, and the one that separates people who have read about forensics from people who have done it. What do file timestamps actually mean, and how do you tell when someone has changed them.
I said in the previous article that this comes up almost as often as the deletion question. It also produces more confidently wrong answers, because the popular version of the tell is real but incomplete, and the incomplete version gets findings thrown out.
Technical assumptions
As before: Windows 7 and later, NTFS 3.1, and a working familiarity with NTFS attributes. If a term is unfamiliar, the references at the end are the books I keep going back to.
What the four timestamps are
Forensics calls them MACB, and the letters are not in the order most people expect.
M, modified
The content changed. This is the one users think of as "the" timestamp, because it is what Explorer shows by default.
A, accessed
The file was accessed, which in practice means a handle was opened to read it. Be careful how you phrase this in a report: it is not evidence that a person read the content. Antivirus, the search indexer, backup agents and Explorer generating a thumbnail all open handles, and any of them can move this value.
C, changed
The MFT record itself was modified: permissions, attributes, size, a rename. Not the content. This is the one people confuse with created, because both are commonly abbreviated to C.
B, birth
Created. The moment this file record came into existence on this volume. A move within the volume keeps it; a copy gets a new one.
How they are stored
Each timestamp is a FILETIME: an unsigned 64-bit integer counting 100-nanosecond intervals since 1 January 1601 UTC. Eight bytes, stored little-endian, so on disk the low-order byte comes first.
One second is 10,000,000 units. That is where the resolution comes from, and it is worth seeing at the byte level, because the fraction lives in the bytes a lazy tool never bothers to populate.
Not two sets. At least two, and usually three
Each of those four timestamps is stored more than once in the same MFT record.
$STANDARD_INFORMATION, written $SI, holds created, modified, MFT-changed and accessed. This is the set the Windows API exposes, the set Explorer displays, and the set every ordinary tool reports.
$FILE_NAME, written $FN, holds its own four. And a record carries one $FN per name. A file whose name is longer than the 8.3 limit, or contains characters 8.3 cannot express, gets a generated short name and therefore a second $FN. A file with hard links gets one more per link.
So on the system volume the usual case is not one comparison. It is one $SI against two or more $FN sets, and they do not have to agree with each other.
That matters practically. A tool that updates one $FN may leave the others alone. Short-name generation is also a per-volume setting, under NtfsDisable8dot3NameCreation. On a Windows 11 test host it was on for the system volume and off for a data volume, both at the default, so on data volumes the usual case is a single $FN. Its absence tells you about volume configuration rather than about the file.
$FN values are set when the name is created, and refreshed on rename, move within a volume, and hard link creation. Ordinary file activity does not touch them. Crucially, the documented Windows API for setting timestamps writes to $SI only.
How timestomping works, and how it has moved on
Nothing exotic is required. SetFileTime() is a documented Win32 call, and in PowerShell it is one line setting LastWriteTime on a file object. Both write $SI and leave $FN where it was.
They also leave a mark inside $SI. On a Windows 11 test host, SetFileTime moved the modified time to 2019 and pushed the MFT-changed time to the moment of the call, so the stomped file showed an MFT-changed time years after its modified time. That mark only catches the laziest tooling: SetFileInformationByHandle, also documented, then set the MFT-changed time to 2019 as well.
The important development is what modern tooling does instead of inventing a date. It copies the timestamps from a legitimate file on the same system, typically something in System32 that nobody will question. The values are then genuine: correct era, plausible relationships between the four, and a fully populated fraction, because they came from a real file.
Keep that in mind through the next section. Several of the classic tells only catch tooling that did not bother.
Finding it, and the false positive for each
No single one of these is a finding. Each has a benign explanation that occurs in ordinary systems, and an examiner who cannot state the benign explanation has not finished the analysis.
1. $SI earlier than $FN
Backdating through the API drags $SI into the past while $FN stays at the real creation time, and the pair becomes impossible: a file cannot have been modified before its name existed.
Note the direction. $SI later than $FN is entirely normal, and is what a file looks like after being edited.
False positives. A file copied into place carries $SI modified from its source while taking a fresh $FN created at the destination. Restores from backup, archive extraction, installers and sync clients all reproduce the pattern. And NTFS tunneling, described below, produces it legitimately.
That makes the sharpest pair $SI created against $FN created. A copy sets both to the moment of the copy, so a gap between those two needs an explanation other than a copy.
2. Sub-second precision that is absent
Real operations populate the fraction. A tool that converts a human-readable date leaves it at zero, so a $SI value ending in exactly .0000000, among neighbors that all carry noise, is worth a second look.
False positives. FAT stores modification times at two-second granularity, and Windows writes exFAT modification times the same way even though exFAT can hold 10 milliseconds. So anything written to a USB stick or camera card by Windows is honestly zeroed. Media written by a Mac can carry the 10 ms value. ZIP archives inherit DOS timestamps with the same two-second resolution, and other container formats allocate fewer bits to time than NTFS does, so zero padding after extraction is routine rather than suspicious.
And the false negative matters more. A tool that clones timestamps from a real file produces a perfectly populated fraction. This test catches carelessness, not capability.
3. MFT record order against timestamp order
Records are allocated broadly in sequence, so files created one after another tend to occupy adjacent record numbers with $FN birth times that increase in step. A file whose record number sits among a block created last Tuesday, but whose timestamps claim 2019, is out of place in a way its own attributes cannot explain.
False positives. Record numbers are reused. A record freed by a deletion is reallocated to an unrelated new file, which breaks the correlation legitimately and often. Check the sequence number before drawing anything from position, and treat the whole technique as directional rather than conclusive.
4. The USN journal disagrees
This is the strongest of them. If $UsnJrnl records a FILE_CREATE for the object, then the file was created at that moment, whatever its attributes now claim. The journal entry carries a timestamp written by the file system, and the reason flag BASIC_INFO_CHANGE (0x00008000) marks the moment the attributes were subsequently altered.
False positives. NTFS tunneling. When a file is deleted or renamed and a new file with the same name appears in the same directory within a short window, NTFS deliberately restores the original creation time and short name to the new file. The default window is fifteen seconds, governed by MaximumTunnelEntryAgeInSeconds. It exists so that save-as-temporary-then-rename, which is how a great many applications write files, does not reset the creation date. It also means a legitimately new file can hold a creation time older than itself, and this catches people out regularly.
5. $I30 slack can hold a view the stomp did not reach
The directory index keeps its own copy of each file's timestamps, so that a folder listing does not have to open every file. The copy is stored as a $FILE_NAME structure, which is why it is often read as a third $FN. Its timestamps are almost always kept in sync with $SI instead, and the stomp rewrites them. On a Windows 11 test host I set a file's M to 2019 through SetFileTime and then read the parent index straight from the disk: the live entry already held the 2019 value and the new $SI change time. So the live entry shows you the stomp, not the truth.
What can survive is slack. Entries logically removed from the B-tree when the index is reorganized often remain there, and one of them can preserve an earlier view of the file.
False positives. A slack entry is older by construction, so an earlier value there is expected after any honest edit, rename or move. Slack is also unallocated space: correlate what you find with the journal before treating it as the state before tampering.
6. Ordering in AppCompatCache and Amcache
AppCompatCache is a most-recently-used list, so an entry's position reflects the order in which the system encountered files, independent of anything stored in the file. An executable sitting among entries from the last few days, while claiming a modification date years old, is worth pursuing.
Be precise about what is independent here. The cache stores the file's $SI modified time, which is the stompable value. The ordering is independent evidence. The timestamp inside the cache is not.
Amcache is the better companion, because it records its own first-seen times and a SHA-1 of the binary, neither of which comes from the file's attributes.
False positives. A ShimCache entry does not prove execution on Windows 10 and later; the file may only have been enumerated. The cache is bounded and flushed to the registry at shutdown, so absence is not evidence, and a live system holds entries that are not yet on disk. If the file was stomped before the system ever saw it, the cache records the stomped value in good faith.
7. Everything outside the file system
Sysmon raises Event ID 2 specifically for a process changing a file creation time, which is as close to a direct observation as this gets. Beyond that: proxy or firewall logs showing when the file was downloaded, mail gateway records, EDR telemetry, the timestamps of the archive it arrived in, and the creation time of the folder that holds it.
False positives. Some installers and build tools set timestamps deliberately and will trigger Sysmon Event 2 in the course of doing their job. Establish what normally does this in the environment before treating an occurrence as adversarial.
What none of it shows
$FN agreement is not innocence
Tools that write $FN exist, working through direct volume access or by driving the file system into updating $FN itself. $SI matching $FN means the simple attack was not used. It does not mean no attack was used.
A changed clock defeats every comparison
Every tell above compares values the file system wrote at different moments. An attacker with administrative rights can set the system clock back, create or change the file, and set the clock forward again. $SI, $FN, the index and the USN journal then all record the false time together, and none of the tells fires.
The clock change itself is recorded. The System log’s Kernel-General event 1 names the old time, the new time and the reason for each change, and Security event 4616 records it where that audit is enabled. Most Kernel-General entries are routine time sync with a delta of a few milliseconds; the one to look for is a large jump and its return.
Last access is unreliable, and you must say so
Last-access updates were disabled by default from Windows Vista. Windows 10 1803 changed the default to System Managed, which decides at boot. As documented at the time, it enabled them when the system volume was 128 GiB or smaller and not on larger ones. It does not describe every current build: on a Windows 11 25H2 host with a 928 GB system volume, System Managed had them enabled. And even when they are enabled, NTFS delays the update by up to an hour, so repeated reads inside that hour leave one value. So the value may be current, or years stale, and which is true depends on a setting nobody thought to record at the time.
Check it rather than assume: fsutil behavior query disablelastaccess, and the corresponding value under NtfsDisableLastAccessUpdate. Put the answer in the report, because an examiner who reports on access times without establishing this has reported on nothing.
Putting it together
The order I would work it, and the order I would defend it in: parse the $MFT with $SI and every $FN side by side, comparing direction and precision rather than equality. Pull $I30 slack for the parent; the live entries almost always follow $SI and will agree with it. Check the USN journal for the creation event and for BASIC_INFO_CHANGE, and rule tunneling in or out explicitly. Check $LogFile if the window is recent enough. Look at record-number position against neighbors. Then step outside the file system entirely, to Sysmon, Amcache and whatever recorded the file arriving.
And write the limits into the finding. The tampering was performed through the documented API, or the tooling was capable of writing $FN and the comparison would not reveal it, or the timestamps were cloned from a legitimate file and the precision test would not fire. All three are defensible sentences. "The timestamps were altered" on its own is not.
Conclusion
The popular answer to this question is one comparison, and it is a good comparison. The complete answer is knowing the benign explanation for every tell, and knowing which tells stopped working once tooling started copying timestamps from real files instead of inventing them.
In an interview, that second half is the part that gets remembered.
Next in this series: what actually proves a program ran, and what each of Prefetch, Amcache and ShimCache does not prove.
References
- NTFS internals Brian Carrier, File System Forensic Analysis amazon.com/dp/0321268172
- NTFS internals Forensic Examination of Windows Supported File Systems amazon.com/dp/1497358353
- API SetFileTime, and the FILETIME structure it writes learn.microsoft.com/windows/win32/api/fileapi/nf-fileapi-setfiletime
- USN journal USN_RECORD reason flags, including USN_REASON_BASIC_INFO_CHANGE learn.microsoft.com/windows/win32/api/winioctl/ns-winioctl-usn_record_v2
- Tunneling Windows file system tunneling, and the registry values that govern it support.microsoft.com/kb/172190
- Last access NtfsDisableLastAccessUpdate, and the default change in Windows 10 1803 learn.microsoft.com/windows-server/administration/windows-commands/fsutil-behavior
- Detection Sysmon Event ID 2: a process changed a file creation time learn.microsoft.com/sysinternals/downloads/sysmon
- Tooling MFTECmd and AmcacheParser, which report $SI and every $FN side by side ericzimmerman.github.io