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,分析价值极低。
