Prefetch Analysis
Windows Prefetch records every executable that runs on the system — filename, path, run count, run timestamps, and the DLLs and files it accessed. For forensics, it's a chronological execution log that survives file deletion.
An attacker ran Mimikatz from a temp directory as svchost32.exe (renamed to blend in), deleted it immediately after execution, and cleaned up the temp folder. Three days later, you're investigating. The file is gone. The MFT record shows the deleted entry but the attacker also timestomped it. But Prefetch doesn't lie: SVCHOST32.EXE-A3F2B819.pf exists in C:\Windows\Prefetch\. PECmd parses it: run count 1, last run 2026-09-17 03:42:11, file path \TEMP\SVCHOST32.EXE, DLLs loaded include WDIGEST.DLL (cleartext credential extraction). The attacker can delete their tool but they can't easily delete its Prefetch entry.
What Prefetch Records
The Windows Prefetch service monitors program startup to speed up future launches. As a side effect, it creates a forensic execution log for every program that runs.
| Data recorded in each .pf file | Forensic value |
|---|---|
| Executable name and hash of the path | Identifies what ran — the hash means two executables with the same name but different paths get different .pf files |
| Last run time (up to 8 most recent) | Windows 8+: stores up to 8 run timestamps. Shows exactly when the program ran. |
| Run count | Total number of times the program ran |
| Volume information | Drive serial number and volume path — helps correlate to specific disks |
| Files and directories accessed on first run | Which DLLs were loaded, which config files read, which directories accessed — evidence of what the program did |
| File path | Full path the executable ran from — reveals suspicious locations (temp dir, user profile, AppData) |
Prefetch Location and Limitations
Prefetch Facts
═══════════════════════════════════════════════════════════════════
Location: C:\Windows\Prefetch\
Format: EXECUTABLENAME-PATHHASH.pf
Max files: ~1024 (older entries overwritten when limit reached)
Retention: Survives reboot; entries persist until limit or deletion
Enabled by default:
Workstations: YES (Superfetch/SysMain service)
Servers: NO — Prefetch disabled by default on Server OS
Check: HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\
Memory Management\PrefetchParameters → EnablePrefetcher
Windows versions and timestamps stored:
Windows 7/8: 1 run timestamp per .pf file
Windows 10/11: Up to 8 run timestamps per .pf file (huge forensic value)
Attackers delete Prefetch? Yes — requires SYSTEM or admin rights.
Indicator: gap in Prefetch sequence numbers (they're not sequential
but mass deletion leaves the directory unusually sparse).
More often attackers leave Prefetch alone (don't know about it)
or rename the executable before running (gets its own .pf file).
Using PECmd to Parse Prefetch
:: Parse all Prefetch files in a directory
:: PECmd.exe from Eric Zimmermann Tools
:: Parse all .pf files, output CSV
PECmd.exe -d "D:\evidence\C\Windows\Prefetch" --csv "D:\analysis\prefetch" --csvf prefetch.csv -q
:: CSV output columns:
:: SourceFilename — the .pf filename
:: SourceCreated — when the .pf file was created (not execution time)
:: SourceModified — when the .pf was last updated (correlates to last run)
:: ExecutableName — name of the executable
:: Hash — path hash (used to disambiguate same-name executables)
:: Size — prefetch file size
:: Version — Windows version
:: RunCount — total times run
:: LastRun — most recent execution timestamp
:: PreviousRun0..6 — up to 7 prior run timestamps (Windows 10+)
:: Directories — directories accessed on first run
:: FilesLoaded — files (DLLs etc) accessed on first run
:: Parse a single .pf file with full detail (loaded files list)
PECmd.exe -f "D:\evidence\C\Windows\Prefetch\MIMIKATZ.EXE-A1B2C3D4.pf" -q
# Hunt for suspicious executables in Prefetch CSV output
$pf = Import-Csv "D:\analysis\prefetch\prefetch.csv"
# Executables that ran from suspicious locations
$suspiciousLocations = @(
"\\TEMP\\",
"\\APPDATA\\LOCAL\\TEMP\\",
"\\USERS\\PUBLIC\\",
"\\PROGRAMDATA\\",
"\\RECYCLE",
"\\WINDOWS\\TASKS\\" # scripts in Tasks dir
)
foreach ($loc in $suspiciousLocations) {
$pf | Where-Object { $_.ExecutableName -match [regex]::Escape($loc) } |
Select-Object ExecutableName, RunCount, LastRun |
ForEach-Object {
Write-Host "SUSPICIOUS LOCATION: $($_.ExecutableName)"
Write-Host " Runs: $($_.RunCount), Last: $($_.LastRun)"
}
}
# Known attacker tool names
$toolNames = @(
"MIMIKATZ", "MIMI", "PROCDUMP", "PWDUMP",
"PSEXEC", "PSEXESVC", "COBALT", "COBALTSTRIKE",
"METERPRETER", "BEACON", "RUBEUS", "BLOODHOUND",
"SHARPHOUND", "LAZAGNE", "WCEWIN", "FGDUMP",
"GSECDUMP", "GETSYSTEM", "GETPASS"
)
foreach ($tool in $toolNames) {
$pf | Where-Object { $_.ExecutableName -match $tool } |
Select-Object ExecutableName, RunCount, LastRun
}
# Executables with unusual names — single char names, numeric names
$pf | Where-Object {
$name = ($_.ExecutableName -split "\\")[-1] -replace "\.EXE$",""
$name -match "^[a-z0-9]{1,3}$" # 1-3 char names are unusual
} | Select-Object ExecutableName, RunCount, LastRun
Building an Execution Timeline from Prefetch
# Build a chronological execution timeline from Prefetch data
# Combines all run timestamps (up to 8 per entry) into a flat timeline
$pf = Import-Csv "D:\analysis\prefetch\prefetch.csv"
$timeline = @()
foreach ($entry in $pf) {
# Add LastRun
if ($entry.LastRun -and $entry.LastRun -ne "") {
$timeline += [PSCustomObject]@{
Timestamp = [datetime]$entry.LastRun
Executable = ($entry.ExecutableName -split "\\")[-1]
FullPath = $entry.ExecutableName
RunCount = $entry.RunCount
}
}
# Add PreviousRun0 through PreviousRun6 if they exist
for ($i = 0; $i -le 6; $i++) {
$col = "PreviousRun$i"
if ($entry.$col -and $entry.$col -ne "") {
$timeline += [PSCustomObject]@{
Timestamp = [datetime]$entry.$col
Executable = ($entry.ExecutableName -split "\\")[-1]
FullPath = $entry.ExecutableName
RunCount = "(prior run $i)"
}
}
}
}
# Sort by time and filter to incident window
$incidentStart = [datetime]"2026-09-15 00:00:00"
$incidentEnd = [datetime]"2026-09-20 23:59:59"
$timeline |
Where-Object { $_.Timestamp -ge $incidentStart -and $_.Timestamp -le $incidentEnd } |
Sort-Object Timestamp |
Format-Table Timestamp, Executable, FullPath -AutoSize
Using Loaded Files to Identify Tool Capabilities
The list of files accessed by an executable on its first run is recorded in Prefetch. This tells you what DLLs the tool loaded — which reveals its capabilities even if you don't have the binary.
| DLL in loaded files list | Capability it suggests |
|---|---|
| WDIGEST.DLL | WDigest authentication — may be credential-related tool (Mimikatz, LaZagne) |
| SAMLIB.DLL | SAM database access — account enumeration or credential extraction |
| VAULTCLI.DLL | Windows Credential Manager access |
| DPAPI DLLs (CRYPTDPAPI, etc) | DPAPI decryption — browser credential extraction, credential vault decryption |
| RASAPI32.DLL | VPN/dial-up credentials access |
| WININET.DLL + WS2_32.DLL | HTTP networking — download/upload capability (C2, exfil) |
| PSAPI.DLL / DBGHELP.DLL | Process inspection, memory reading — typical for credential dumpers |
Prefetch is reliable for two reasons: attackers rarely think to clean it up, and it records execution even when the file has been deleted. An EDR that gets deployed after a compromise won't have process creation logs for activity before its deployment — but Prefetch records execution history that predates EDR deployment. It's also useful when EDR telemetry is unavailable (host wasn't EDR-covered, telemetry was deleted). The combination of execution timestamp, file path, and loaded DLLs makes Prefetch one of the most information-dense artifacts in a Windows forensic investigation. Always parse it on every host you're investigating.
Q & A
Q: The server you're investigating has Prefetch disabled. How do you prove execution?
On Windows Server, Prefetch is off by default. Your execution evidence comes from alternative sources: (1) Shimcache (AppCompatCache in the registry) — records every executable that exists on the system, with a "has been executed" flag on some versions (ch08 covers this). (2) Amcache.hve — records SHA-1 hashes of executables and first execution time; more reliable for servers than Shimcache. (3) PowerShell Script Block Logs (Event 4104) — if the attacker used PowerShell, every script block executed is logged. (4) Windows Event Log process creation (Event 4688 with command line auditing enabled) — direct evidence of every process that started, with full command line. (5) EDR telemetry — if EDR was deployed, it has process creation records regardless of Prefetch state. The absence of Prefetch on servers is a known limitation — the other artifacts compensate for it adequately for most investigations.