Windows Prefetch file format
This description is mainly based on the awesome work done at Forensics Wiki
|0x0000||4||DWORD||Format version (0x11: XP, 0x17: Win7, 0x1a: Win8)|
|0x0004||4||CHAR||String "SCCA" ("magic")|
|0x000C||4||DWORD||Size of the prefetch file|
|0x0010||60||CHAR||Name of the executable, UTF-16LE encoded and padded with 0x00|
|0x004C||4||DWORD||Checksum, computed with prioprietary algorithm (see dedicated section)|
|0x0054||4||DWORD||offset of Section A, relative to the beginning of the file|
|0x0058||4||DWORD||number of entries in Section A|
|0x005C||4||DWORD||offset of Section B, relative to the beginning of the file|
|0x0060||4||DWORD||number of entries in Section B|
|0x0064||4||DWORD||offset of Section C, relative to the beginning of the file|
|0x0068||4||DWORD||Length, in bytes, of the Section C|
|0x006C||4||DWORD||offset of Section D, relative to the beginning of the file|
|0x0070||4||DWORD||number of Section D|
|0x0074||4||DWORD||Total length, in bytes, occupied by all sections D|
The remaining data brefore reaching the Section A, depends on the operating system and is described here-under.
Before Windows 8 (Windows XP, Vista, 7)
|0x0078||8||QWORD||Last run timestamp, in MS format (100 nanoseconds intervals since January, 1st 1601)|
Since Windows 8
|0x0080||8||QWORD||Last run timestamp, in MS format (100 nanoseconds intervals since January, 1st 1601)|
|0x0088||7*8||QWORD||7 previous run timestamps, in MS format (100 nanoseconds intervals since January, 1st 1601), in reversed chronological order.|
The exact meaning of this section is still unknown.
Before Windows 7 (Windows XP and Vista)
Each entry occupies 20 bytes.
Since Windows 7
Each entry occupies 32 bytes.
The exact meaning of this section is still unknonwn.
Each entry occupies 12 bytes
Section C (Fileset)
This section is an array of UTF-16LE encoded strings (hence the separator is U+0000).
Each entry in this section is a file that is needed for the loading of the application.
Section D (Volume Information section)
In a prefetch file, you can have 1 Section D per volume involved during the loading of the application.
For example, if you run an EXE from a USB key, you may find one Volume Information block related to your USB key and another one regarding the operating system volume.
This behavior is confirmed on Windows 8 but has not been seen so far on previous Windows versions.
Hereunder offsets are relative to the beginning of one Section D.
|0x0000||4||DWORD||offset of the VolumeID UTF-16LE encoded string|
|0x0004||4||DWORD||Length (expressed in Unicode characters, including the terminating U+0000) of the VolumeID string|
|0x0008||8||QWORD||Creation timestamp of this volume, in MS format (100 nanoseconds intervals since January, 1st 1601)|
|0x0010||4||DWORD||Volume serial number|
|0x0014||4||DWORD||Offset of subsection S1, relative to the beginning of the section|
|0x0018||4||DWORD||Length of subsection S1, in bytes|
|0x001C||4||DWORD||Offset of the Directory Set array, relative to the beginning of the section|
|0x0020||4||DWORD||Number of entries in the Directory Set array|
Meaning of this content is still unknown
Each entry represents a directory that is involved during the loading of the application.
|0x0000||2||WORD||String length, in Unicode characters, excluding the terminating U+0000|
|0x0002||length + 1||WCHAR||Directory name, UTF-16LE encoded|
The algorithms depends on the operating system version (and not the Prefetch file format version).
Here are Python implementations of the algorithms we have found so far, documented on Internet.
def hashFilename(filename): """filename is the UTF-16 full uppercase file name""" hash_value = 0 for c in filename: hash_value = ((hash_value * 37) + ord(c)) % 0x100000000 hash_value = (hash_value * 312159269) % 0x100000000 if hash_value > 0x80000000: hash_value = 0x100000000 - hash_value return (abs(hash_value) % 1000000007) % 0x100000000
Since Windows 7 (Windows 8, Windows 8.1)
def hashFilename(filename): """filename is the UTF-16 full uppercase file name""" hash_value = 314159 fname_index = 0 fname_len = len(filename) while fname_index + 8 < fname_len: c = ord(filename[fname_index + 1]) * 37 c += ord(filename[fname_index + 2]) c *= 37 c += ord(filename[fname_index + 3]) c *= 37 c += ord(filename[fname_index + 4]) c *= 37 c += ord(filename[fname_index + 5]) c *= 37 c += ord(filename[fname_index + 6]) c *= 37 c += ord(filename[fname_index]) * 442596621 c += ord(filename[fname_index + 7]) hash_value = ((c - (hash_value * 803794207))) % 0x100000000 fname_index += 8 while fname_index < fname_len: hash_value = (((37 * hash_value) + ord(filename[fname_index])) % 0x100000000) fname_index += 1 return hash_value