How the Windows Prefetch hash is calculated
A filename like NOTEPAD.EXE-1B4A5887.pf looks like the eight hex characters are a checksum. They are not. They are a hash of the launch path of the executable, encoded as UTF-16, prefixed with \DEVICE\HARDDISKVOLUME{n}\. The byte contents of notepad.exe never enter the calculation. Two copies of the identical binary at C:\Windows\System32\notepad.exe and C:\Users\analyst\notepad.exe produce two different .pf files because their paths differ.
This is the entire reason Prefetch can distinguish psexec.exe in C:\Program Files\ from psexec.exe dropped to C:\Users\victim\AppData\Local\Temp\. The path hash is the per-launch-location identifier.
Three algorithms, three eras
The function has been replaced twice in the OS history:
- Windows XP / Server 2003. The original SuperFetch hash. Simple rolling computation over the UTF-16 path.
- Windows Vista / 2008. Same family, different constant multiplier, different starting seed. Vista hashes do not match XP hashes for the same path and vice versa.
- Windows 7 onward. "Hash Function 5" in Microsoft's terminology. Still in use on Windows 10 and Windows 11. Folds groups of bytes from the UTF-16 path through a multiply-and-XOR loop, masks to 32 bits, and that gets rendered as the eight hex characters in the filename.
The path string fed into all three includes a \DEVICE\HARDDISKVOLUME{n}\ prefix and the file path in upper-case UTF-16 LE. On later Windows versions there are some quirks around how command-line arguments and certain volume formats enter the calculation, but for the common case the path is exactly what you would expect.
Why it matters in practice
A planted Prefetch file is one of the cheaper anti-forensics tricks: copy a .pf from another host onto the target. The path inside the SCCA payload is whatever it was on the source machine; the filename hash is what the source machine computed. A planted file does not have a hash that matches its own embedded path when recomputed under the destination's algorithm, especially across major Windows versions.
The check is mechanical:
- Parse the SCCA payload.
- Pull out the executable path (the
Executable filenamefield plus the volume information). - Reconstruct the canonical UTF-16 LE upper-case string with the
\DEVICE\HARDDISKVOLUME{n}\prefix. - Run it through the algorithm for the version field in the payload (v17 uses the XP function, v23 the Vista function, v26/v30/v31 use Hash Function 5).
- Compare against the eight characters in the filename.
A mismatch is conclusive. Either the file was carried in from a machine running a different OS version, or somebody hand-crafted it and got the algorithm wrong.
When the hash is the only thing left
Modern SCCA payloads no longer keep a raw path string in the way XP did. They keep the executable filename (no path), the volume serials, and the file-metric list of everything the binary touched. The "where did this run from" question often has to be answered from the filename hash combined with the volume information section. Pair this with MFT volume records and LNK drive serials, which carry the same NTFS serial number, and you can usually pin the binary to a specific physical or removable volume.
If the binary lived on a USB stick that has since been pulled and the volume serial does not match any volume on the host, the filename hash is your remaining lead. Reverse the hash function in the obvious direction (you cannot, it is one-way) — or, more usefully, hash the candidate paths you think the binary ran from and look for the match. Practitioners maintain small tables of expected hashes for common attacker tooling locations for exactly this reason.
Implementation notes
Open-source implementations of all three functions live in libyal's libscca documentation and in the frnsc-prefetch Rust crate. The Vista and Win7+ functions are short enough to inline — around a dozen lines each — without pulling in a dependency. PECmd surfaces the parsed path; recomputing the hash from there is a one-liner.
The parser on this site does not verify the hash automatically yet. It surfaces both the parsed executable path and the filename suffix so you can cross-check by hand. Adding a mismatch flag is on the list.
Further reading
- libyal, libscca format documentation — the most thorough public write-up of all three algorithms.
- Yogesh Khatri, posts on Prefetch hashing across Windows versions — practical examples with known-good test vectors.