windows预取文件(.pf)是微软未公开的二进制私有格式,需依赖逆向工程解析;c#无内置支持,且v17–v31格式历经4次不兼容变更,推荐使用prefetchlib库并注意权限、路径转换及时间戳修正。

Windows预取文件(.pf)不是标准可读格式
Windows预取文件是二进制私有格式,微软从未公开其结构定义,所有已知解析逻辑均来自逆向工程(如Sysinternals的Procmon、prefetch-parser等工具反推)。C#没有内置支持,FileStream.Read直接读出的是无意义字节流,必须按已知偏移和字段长度手动解包。
关键事实:从Windows XP到Win11,.pf格式经历过至少4次不兼容变更(v17–v31),不同系统版本的文件头、执行次数字段位置、路径字符串编码方式都不同。硬写一个“通用解析器”大概率在某台机器上直接崩溃或返回乱码。
用现成库比手写解析更可靠
推荐使用开源项目 prefetchlib(NuGet包名:PrefetchLib),它封装了多版本兼容逻辑,且持续跟进新Windows更新。安装后只需几行代码:
var pf = PrefetchFile.Load(@"C:WindowsPrefetchNOTEPAD.EXE-1A2B3C4D.pf");
Console.WriteLine($"Executable: {pf.ExecutableName}");
Console.WriteLine($"Run count: {pf.RunCount}");
foreach (var file in pf.Files) {
Console.WriteLine($" Accessed: {file.Path}");
}
注意:PrefetchLib默认只解析v26+(Win8起),若需支持XP/Win7旧文件,得切换分支或降级到v1.0.2并启用LegacyMode = true。
- 不要尝试用
BinaryReader配合网上过时的“字段偏移表”硬解——Win10 22H2之后的v31格式把文件访问列表移到了动态偏移区,固定偏移会越界读取 -
PrefetchLib的Files集合返回的是原始NT路径(如DeviceHarddiskVolume2WindowsSystem32 otepad.exe),需调用Path.GetFullPath()或查询\?GLOBALROOT符号链接才能转为常规路径
权限与文件锁定问题常被忽略
Windows默认禁止普通用户读取C:WindowsPrefetch目录下的.pf文件,即使你用Administrator运行程序,仍可能遇到UnauthorizedAccessException。这不是UAC弹窗问题,而是文件系统ACL限制。
- 必须以
SYSTEM权限运行(例如通过PsExec -s启动进程),或提前用icacls修改目录权限(不推荐生产环境) - .pf文件被系统独占锁定——不能在Explorer打开预取目录时运行解析程序,否则抛
IOException:“The process cannot access the file because it is being used by another process” - 某些杀软(如CrowdStrike)会拦截对
.pf的读取行为,报Access is denied而非具体错误码,此时需检查安全软件日志
时间戳字段容易误读
.pf文件里的时间不是标准FILETIME或Unix时间戳,而是Windows NT系统启动后的相对毫秒数(TickCount64风格),且部分版本还叠加了系统休眠补偿值。直接用DateTime.FromFileTime()会得到1601年的错误时间。
PrefetchLib内部做了修正:先读取文件头里的系统启动时间(UTC),再结合记录中的相对偏移计算真实时间。如果你自己解析,必须提取Header.BootTime(8字节)并做如下转换:
var bootTime = DateTime.FromFileTimeUtc(BitConverter.ToInt64(headerBytes, 0x00)); var runTime = bootTime.AddMilliseconds(relativeMs);
但要注意:Win11 23H2起,BootTime字段已被弃用,改存于扩展块中——这意味着没处理扩展块的解析器,时间字段将全为零。
真正麻烦的不是解析,而是确认你拿到的.pf文件是否还有效。系统每启动一次就可能重建预取,旧文件会被静默删除;而SSD设备上预取机制本身已大幅弱化,很多.pf文件的RunCount长期卡在1,分析价值极低。










