Windows Prefetch files are one of the most consistently underestimated artefacts in Windows incident response. They were designed to speed up application loading by recording which files an executable accesses during startup, but as a side effect they create a persistent execution record that survives even after the executable itself has been deleted. An attacker who drops a tool, runs it once, and deletes it leaves a Prefetch file behind that proves it ran, records when it ran, and lists some of the files it accessed.
What Prefetch files contain
Each Prefetch file is named after the executable with an eight-character hash of the full path appended: MIMIKATZ.EXE-3A27C2B8.pf. The hash is derived from the directory path, not the file content, so the same binary run from two different locations produces two different Prefetch files. This is forensically useful: if you find two Prefetch files for the same executable name with different hashes, it means it was run from two different locations, which is characteristic of attacker tools that get copied to multiple staging directories.
On Windows 8 and later, each Prefetch file stores the last 8 execution timestamps for that binary. On Windows 7 it stores only the most recent. The run count is also stored. A run count of 1 combined with a single execution timestamp often indicates a one-shot tool that was used once and deleted. A run count of 47 over several weeks tells a different story.
The file access list embedded in each Prefetch file is particularly valuable. For credential dumping tools, lsass.exe appears in the referenced files list. The path of the output dump file often appears too, even after both the tool and the dump have been deleted.
Location and access
# Prefetch is enabled by default on Windows workstations
# Disabled by default on Windows Server editions
# Check whether it is enabled:
Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management\PrefetchParameters" |
Select-Object EnablePrefetcher
# 0 = disabled, 1 = application prefetching, 2 = boot prefetching, 3 = both
# List recent Prefetch files sorted by last execution time
Get-ChildItem C:\Windows\Prefetch\*.pf |
Select-Object Name, LastWriteTime, CreationTime, Length |
Sort-Object LastWriteTime -Descending |
Select-Object -First 30
# Flag known attacker tooling immediately
Get-ChildItem C:\Windows\Prefetch\*.pf |
Where-Object {
$_.Name -match "(?i)MIMIKATZ|PROCDUMP|RUBEUS|BLOODHOUND|SHARPHOUND|NANODUMP|COBALTSTRIKE"
} | Format-Table Name, LastWriteTime -AutoSize
Parsing with PECmd for full detail
# PECmd from Eric Zimmermann's toolkit provides complete parsing
# including all 8 timestamps, run count, and referenced file list
# Download: https://ericzimmerman.github.io/
PECmd.exe -d "C:\Windows\Prefetch" --csv C:\ir\ --csvf prefetch.csv -q
# The CSV output contains one row per execution timestamp per file
# Sort by RunTime to build a chronological execution timeline
Building an execution timeline from Prefetch
python3 << EOF
import csv
from datetime import datetime
# Define the incident window
incident_start = datetime(2025, 1, 15, 22, 0, 0)
incident_end = datetime(2025, 1, 16, 4, 0, 0)
# Known attacker tool names to flag automatically
attacker_tools = {
"MIMIKATZ", "PROCDUMP", "RUBEUS", "BLOODHOUND", "SHARPHOUND",
"NANODUMP", "SEATBELT", "WINPEAS", "LINPEAS", "PSEXEC", "WMIEXEC"
}
events = []
with open("prefetch.csv") as f:
for row in csv.DictReader(f):
exe_name = row.get("ExecutableName", "").upper().replace(".EXE", "")
for ts_field in ["LastRun", "PreviousRun1", "PreviousRun2",
"PreviousRun3", "PreviousRun4",
"PreviousRun5", "PreviousRun6", "PreviousRun7"]:
ts_str = row.get(ts_field, "").strip()
if not ts_str:
continue
try:
ts = datetime.strptime(ts_str, "%Y-%m-%d %H:%M:%S")
if incident_start <= ts <= incident_end:
flag = " <<< ATTACKER TOOL" if exe_name in attacker_tools else ""
events.append((ts, exe_name, row.get("RunCount","?"), flag))
except:
pass
events.sort(key=lambda x: x[0])
print(f"Execution timeline ({incident_start} to {incident_end}):\n")
for ts, name, count, flag in events:
print(f" {ts} {name:<30} (run count: {count}){flag}")
EOF
Cross-referencing Prefetch with other artefacts
Prefetch is most powerful when read alongside Amcache and Shimcache. Prefetch proves execution and gives timestamps. Amcache provides the SHA1 hash of the binary, which survives renaming and allows identification even after deletion. Shimcache records that a file was present at a given path. Together the three artefacts answer: what ran, when it ran, what it was called, where it was located, and what its hash was. An attacker who renames Mimikatz to svcupdate.exe and runs it once from C:\Windows\Temp\ will appear in Prefetch as SVCUPDATE.EXE, but Amcache will have the SHA1 that identifies it as Mimikatz regardless of the filename.