解析 ShimCache:Windows 10 与 11 的二进制格式

约 2 分钟阅读

TL;DR. 首个 u32 = 头部大小(通常为 0x300x36)。在该偏移处,每条条目以四字节魔术 10ts 开头,随后是 12 字节的头部与可变长度的正文:UTF-16LE 路径(带长度前缀,不以空字符终止)、FILETIME mtime 和尾部数据块。执行标志在 Windows 10 中已被移除。

自 XP 起,Windows 的每一个版本都提供了不同的 ShimCache 布局。Windows 10 / 11 这一变体是今天调查人员最常遇到的,也是相对最好解析的 —— 前提是你知道规则。本文从头到尾走一遍这个格式。

Windows 10/11 ShimCache 条目的二进制布局

你要解析的 Blob

起点是下列位置的二进制值:

HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\AppCompatCache\AppCompatCache

如果你已经从 SYSTEM 配置单元提取出了这个值(见 ShimCache 存储在哪里),你手里就是一个 blob,通常几十到几百 KB。其中包含一个小头部,后面是一系列条目,每条以已知的 magic 开头。

头部

blob 开头四字节(little-endian)给出 第一条条目的偏移。在 Windows 10 / 11 上,这通常是 0x300x340x350x36,依 build 而定。某些配置单元会写入 0x80,那是仍使用 10ts / 00ts 条目签名的 Windows 8.x 布局。

写得稳健一些的解析器不会把头部大小写死:先读首个 u32,再校验该偏移处的字节确实是已识别条目的开头。如果声明的偏移既没有 10ts 也没有 00ts,则向前扫描查找 10ts 并从那里继续 —— 这能容忍现实系统偶尔产生的略微不规范的配置单元。

一条 Windows 10 / 11 条目,逐字节

每条现代条目都以四字节签名 10ts0x31 0x30 0x74 0x73)开头。完整布局如下:

偏移大小字段
0x004Magic —— "10ts"
0x044序号 / 未知(解析器通常忽略)
0x084条目数据大小 —— 此字段之后的字节数
0x0C2路径大小(字节,UTF-16LE)
0x0EN路径字节(UTF-16LE,不含终止符
0x0E + N8最后修改时间 —— FILETIME(从 1601-01-01 UTC 起的 100 ns 计数)
0x16 + N4尾部数据大小
0x1A + NM尾部数据(通常为空或很小)

整条条目长度因此是 0x0C + entry_data_size。下一条紧跟其后。

两点务必注意:

  1. 路径为 UTF-16LE,且 不以空字节结尾。请使用路径大小字段;不要读到 null。
  2. FILETIME 是文件的 $STANDARD_INFORMATION mtime,并非程序运行时刻。原因详见 关于执行证明的文章

Windows 10 里被拿掉的东西

更早的 Windows 在每条条目里塞了一个 Executed 标志。在 Windows 10 / 11 中,这个标志被彻底移除。缓存内已没有任何方法告诉你二进制是否真的运行过 —— 这件事得交给 Prefetch、AmCache 或事件日志 去回答。

实际操作

一个健壮的解析器会:

  1. 在头部声明的偏移处校验四字节 magic。
  2. 通过读取条目数据大小并步进来遍历条目。
  3. 把路径作为 UTF-16LE 解码。
  4. FILETIME 转换为下游工具偏好的时间戳格式(Unix 毫秒就很方便)。
  5. 把操作系统格式标签(Windows 10/11、8.x、8.0)暴露出来供下游过滤。

如果想看一个工作中的参考实现,开源的 Shimcache Parser 正是用 Rust 实现了上述算法,并以 WebAssembly 形式送到你的浏览器 —— 没有服务器,没有上传。

参考实现

相关文章