The Windows DFIR Field Reference: Timestamps, Execution Evidence, and Lateral Movement
I keep coming back to two posters on my wall: SANS FOR500’s timestamp/artifact poster and FOR508’s “Hunt Evil” poster. Between them they cover most of what you need in the first two hours of a Windows intrusion case — but they’re a few years old in places, and a poster can’t hold a “here’s the gotcha” the way a page can. This is that page: the same core reference, corrected where Windows has moved on, with the 2025–2026 artifacts (Recall, PCA, the ShimCache research) that didn’t exist when those posters were printed.
Both original posters are below if you want to work from the source directly — everything after them is my own writeup.
SANS FOR500 — Windows Forensic Analysis poster
Open full size in a new tab →
SANS FOR508 — Hunt Evil poster
Open full size in a new tab →
Treat the reference below as a lookup table, not a tutorial. Ctrl+F it during a case.
1. Timestamps: $STANDARD_INFORMATION vs. $FILENAME
Every NTFS file has two independent sets of MACB timestamps: one in $STANDARD_INFORMATION (what dir, PowerShell, and most tools show you) and one in $FILENAME (harder to see, harder to fake). They behave differently depending on the operation, and that difference is one of the highest-value things you can pull out of an MFT.
| Operation | $STANDARD_INFORMATION | $FILENAME |
|---|---|---|
| Creation | M/A/C/B all = creation time | M/A/C/B all = creation time |
| Access (read/open) | Access = access time; rest unchanged | No change (FN never tracks access) |
| Modification (content write) | Modified & Metadata = write time; Access/Creation unchanged | No change |
| Rename | Metadata = rename time; rest unchanged | Metadata = rename time; rest unchanged |
| Copy (new file object) | Modified inherited from source; Access/Metadata/Creation = copy time | All four reset to copy time |
| Local move (same volume, Explorer or CLI) | No change at all | No change at all |
Cross-volume move via CLI (move) |
Modified & Metadata inherited; Access/Creation = move time | All four = move time |
| Cross-volume move via Explorer (cut/paste) | Modified & Metadata inherited; Access = cut/paste time; Creation inherited | All four = cut/paste time |
| Deletion | No change (until MFT record is reused) | No change |
Two things worth remembering on every case:
- A same-volume move is invisible in timestamps. If a file was dragged from
C:\Users\bob\DownloadstoC:\ProgramData\on the same volume, nothing in$SIor$FNrecords that it moved. Don’t over-interpret a “clean” timestamp set as proof a file has always lived where you found it. - Timestomping tells on itself in $FN. Tools like
SetFileTime/timestomprewrite$SI— the field every GUI shows — but leave$FNalone, because$FNlives in the index and requires a rename-class operation to touch. If$SIcreation is earlier than$FNcreation, or$SIhas second-level precision while everything else on the volume has the usual sub-millisecond NTFS jitter, that’s a manipulated timestamp, not a real one. Parse both withMFTECmdoranalyzeMFT— never trust$SIalone on a file that matters.
2. Evidence of program execution
No single artifact proves execution on its own — they answer slightly different questions, and several of them prove presence rather than execution. Use two or more together.
| Artifact | Location | What it actually tells you | Caveat |
|---|---|---|---|
| Prefetch | C:\Windows\Prefetch\*.pf |
Last 8 run times (Win8+; 1 on XP/7), run count, files/devices referenced | Disabled by default on server SKUs; capped at 1024 files (128 on XP/7) — will roll over |
| Amcache.hve | C:\Windows\AppCompat\Programs\Amcache.hve |
Full path, file size, compile time, SHA1 of the binary, first-seen time | Proves presence, not execution — some entries come from install/inventory scans, not runs |
| ShimCache / AppCompatCache | SYSTEM\CurrentControlSet\Control\Session Manager\AppCompatCache |
Path + last-modified time of the binary at time of caching | Order-dependent, wiped on reboot before Win10, and historically didn’t prove execution either — see the 2026 note below |
| UserAssist | NTUSER.DAT\...\Explorer\UserAssist\{GUID}\Count |
GUI execution count, last-run time, focus time — ROT13 encoded | GUI launches only; nothing from a shell or scheduled task |
| BAM / DAM | SYSTEM\CurrentControlSet\Services\bam\UserSettings\{SID} |
Full path + last execution time per user, Win10+ | Usually only ~1 week of retention |
| SRUM | C:\Windows\System32\SRU\SRUDB.dat |
30–60 days of app execution, per-user, plus network bytes sent/received per app per hour | ESE database — needs srum_dump or Eric Zimmerman’s SrumECmd, not a registry viewer |
| Jump Lists | %USERPROFILE%\AppData\Roaming\Microsoft\Windows\Recent\AutomaticDestinations |
First/last time an object was opened by an application | Doesn’t confirm the app itself executed vs. was already running |
| RecentApps | NTUSER.DAT\...\Search\RecentApps |
Last access time + launch count for GUI apps, Win10+ | Same GUI-only limitation as UserAssist |
2026 addition: the PCA launch dictionary
Windows 11 quietly added a new execution artifact via the Program Compatibility Assistant — a launch dictionary that records user-driven executable launches with timestamps, independent of Prefetch/Amcache/ShimCache. It doesn’t replace any of the above, but it’s cheap to check, easy to explain in a report, and analysts are still missing it by habit. Worth adding to your standard collection alongside the usual execution artifacts.
2026 correction: ShimCache and the “presence, not execution” rule
The long-standing rule of thumb — ShimCache shows presence, not execution — has needed refinement since 2025 research (Kaspersky, and independently confirmed by other DFIR researchers) found that the last 4 bytes of the ShimCache Data field can indicate actual execution status on modern builds, and that Amcache’s InventoryApplicationFile entries are written by the Microsoft Compatibility Appraiser scanning the disk — which means an Amcache entry can exist for a file that was never run, only scanned. If you’re citing either artifact as proof of execution in a report, corroborate it with Prefetch, SRUM, or an event log rather than resting on ShimCache/Amcache alone.
2026 addition: Windows Recall
On Copilot+ PCs (NPU-equipped, broadly rolled out through 2025), Windows Recall takes periodic screenshots of the desktop and stores them locally with OCR’d text, effectively building a forensic timeline of everything the user saw — not just what they executed. For an investigator this is a goldmine: visual proof of what was on-screen at a given minute, searchable by text. It also means it’s now a first-class target for attackers and a first-class disclosure risk for your own org — treat the Recall snapshot store as sensitive as browser history, and expect it to show up in scope discussions on any Copilot+ endpoint. Tooling is still maturing (RecallTimeline is the current open-source starting point), so budget extra parsing time versus a mature artifact like Prefetch.
3. Know normal before you hunt evil
Baseline the standard Windows process tree so anomalies actually stand out. The parent/child relationship matters more than the process name — malware loves to spawn from or masquerade as these.
| Process | Normal parent | Normal count | Account | Red flag |
|---|---|---|---|---|
System |
none | 1 | Local System | — |
smss.exe |
System | 1 master + 1 per session (children exit) | Local System | Lingering child instance |
wininit.exe |
orphan (smss child that exited) | 1 | Local System | Any parent other than smss/orphan |
csrss.exe |
orphan (smss child that exited) | 2+ | Local System | Parent set to anything else |
services.exe |
wininit.exe | 1 | Local System | More than one instance |
svchost.exe |
services.exe (mostly) | 10–50+ | System / Network Service / Local Service / logged-on user | No -k parameter, or parent isn’t services.exe |
lsass.exe |
wininit.exe | 1 | Local System | Any child process (EFS is the one legitimate exception); more than one instance |
lsaiso.exe |
wininit.exe | 0 or 1 | Local System | Running when Credential Guard/VBS is not enabled |
winlogon.exe |
orphan (smss child that exited) | 1+ | Local System | Unexpected parent |
explorer.exe |
orphan (userinit.exe child that exited) | 1+ per interactive user | logged-on user | Running from anywhere other than %SystemRoot%\explorer.exe |
RuntimeBroker.exe |
svchost.exe | 1 per UWP app | logged-on user | Parent isn’t svchost |
taskhostw.exe |
svchost.exe | 1+ | logged-on user / service accounts | Unusual command line or parent |
The two checks that catch the most malware fastest: wrong parent (svchost.exe not spawned by services.exe, lsass.exe with any child at all) and wrong path (explorer.exe or svchost.exe running from a user-writable directory instead of %SystemRoot%).
4. Lateral movement: what shows up where
Every technique below leaves evidence on the source (attacker’s foothold) and the destination (where they’re moving to). Check both — responders who only look at the destination miss the pivot point.
RDP
- Source:
NTUSER.DAT\Software\Microsoft\Terminal Server Client\Servers(per-user destination history); ShimCache/Amcache/BAM formstsc.exe; Jump List for the RDP AppID; bitmap cache underAppData\Local\Microsoft\Terminal Server Client\Cache. - Destination: Security log 4624 (Logon Type 10) and 4778/4779 (session connect/disconnect);
Microsoft-Windows-TerminalServices-RDPClient/Operational1024/1102;RemoteConnectionManager/Operational1149 (a blank username here can indicate Sticky Keys abuse); Prefetch forrdpclip.exe/tstheme.exe.
PsExec
- Source:
NTUSER.DAT\Software\SysInternals\PsExec\EulaAccepted; ShimCache/Amcache/BAM forpsexec.exe. - Destination: Security log 4624 (Type 3, or Type 2 with
-u), 4648, 4672, 5140 (ADMIN$ access); System log 7045 (service install —PSEXESVC); thepsexesvc.exebinary and any pushed payload landing inADMIN$(\Windows).
psexec.exe \\host -accepteula -d -c c:\temp\evil.exe
Scheduled tasks (at/schtasks)
- Source: ShimCache/Amcache/BAM for
at.exe/schtasks.exe. - Destination: Security log 4698/4699/4700-4702 (task created/deleted/enabled/disabled);
TaskScheduler/Operational106/140/141/200/201; job/XML files inC:\Windows\TasksandC:\Windows\System32\Tasks— check the Author tag in the XML for source hostname/username.
schtasks /CREATE /TN taskname /TR c:\temp\evil.exe /SC once /RU "SYSTEM" /ST 13:00 /S host /U username
Services (sc.exe)
- Source: ShimCache/Amcache/BAM for
sc.exe. - Destination: Security log 4697 (service install, if enabled — enable it, it’s cheap and gold); System log 7045; new key under
SYSTEM\CurrentControlSet\Services\; ShimCache for the dropped service binary.
sc \\host create servicename binpath= "c:\temp\evil.exe"
sc \\host start servicename
WMI / WMIC
- Source: ShimCache/Amcache/BAM for
wmic.exe. - Destination:
WMI-Activity/Operational5857 (provider DLL load path — watch for a malicious provider DLL); 5860/5861 (temporary/permanent event consumer registration — common persistence mechanism, also usable for remote exec);wmiprvse.exespawning the payload.
wmic /node:host process call create "C:\temp\evil.exe"
PowerShell Remoting
- Source: ShimCache/Amcache/BAM for
powershell.exe;ConsoleHost_history.txtunderAppData\Roaming\Microsoft\Windows\PowerShell\PSReadline(last 4096 commands, PSv5+). - Destination:
WinRM/Operational6/8/15/16/33/91/169;PowerShell/Operational4103/4104 (script block logging — enable this if it isn’t already);Windows PowerShell.evtx400/403/800;wsmprovhost.exeas the remoting host process.
Enter-PSSession -ComputerName host
Invoke-Command -ComputerName host -ScriptBlock {Start-Process c:\temp\evil.exe}
Net use / share mapping
- Destination: Security log 4624 (Type 3), 4672, 5140/5145 (share access — 5145 is noisy, filter it), 4776/4768/4769 depending on auth type; on servers, User Access Logging at
C:\Windows\System32\LogFiles\Sumretains source IP + first/last access even after other logs roll off.
net use z: \\host\c$ /user:domain\username <password>
5. Logon types and authentication event IDs
| Type | Meaning |
|---|---|
| 2 | Interactive (console) |
| 3 | Network |
| 4 | Batch |
| 5 | Service |
| 7 | Unlock |
| 8 | Network cleartext (credentials sent in the clear) |
| 9 | RunAs / NewCredentials |
| 10 | RemoteInteractive (RDP) |
| 11 | Cached credentials |
| 12 | Cached remote interactive |
| 13 | Cached unlock |
Core event IDs to alert on, in order of how often they matter:
4624successful logon /4625failed logon /4634+4647logoff4648logon with explicit credentials (RunAs, or any tool passing-u)4672logon with superuser rights — pair with 4624 to catch privilege at the moment of logon4768/4769/4771Kerberos TGT granted / service ticket requested / pre-auth failed4776NTLM authentication4720account created
Using this in an actual case
None of these artifacts is convincing alone. The workflow that actually works: pull the timestamp pair on the artifact of interest, cross-reference at least two execution artifacts before you write “executed” in a report, confirm the process tree against the baseline above before chasing a process name that just looks suspicious, and walk the lateral movement matrix from both ends — source and destination — because the pivot point is usually where the rest of the blast radius reveals itself.
Sources and further reading: SANS FOR500 (Windows Forensic Analysis) and FOR508 (Advanced Incident Response, Threat Hunting, and Digital Forensics) posters, Rob Lee / Mike Pilkington and the SANS DFIR faculty; Kaspersky’s 2025 Securelist research on Amcache and ShimCache; Andrea Fortuna’s write-up on the Windows 11 PCA artifact; the Windows Recall / RecallTimeline research community. If you’re building this out further, FOR508 and FOR572 (network forensics) are the natural next step.