C#读取Windows预取文件 C#如何解析.pf文件以分析程序执行

来源:这里教程网 时间:2026-02-21 17:42:10 作者:

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

相关推荐