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 file came into existence at this path.
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 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 can also be disabled through NtfsDisable8dot3NameCreation, so its absence tells you about system 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.
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.
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 neighbours that all carry noise, is worth a second look.
False positives. FAT and exFAT store modification times at two-second granularity, so anything from a USB stick or camera card is honestly zeroed. 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 holds a copy the MFT edit did not reach
Directory index entries carry their own copy of the $FN timestamps. Rewriting the MFT record does not rewrite the parent directory's index, and entries logically removed from the B-tree frequently survive in index slack. So the folder can preserve an earlier view of a file's timestamps after the record itself has been altered.
False positives. Index entries update on the same events that update $FN, so a rename or move refreshes them for honest reasons. And slack is unallocated space: correlate what you find there with the journal before treating it as the previous state.
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.
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 enables them on smaller volumes and not on larger ones. 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 including slack for the parent. 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 neighbours. 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