一、概述
Prefetch 有一个致命弱点:它是可选的。被显式关闭、被清理、或只记录路径哈希,都会让执行证据链断掉。
Windows 还提供了两个不依赖功能开关的执行相关工件:
| 工件 |
位置 |
记录什么 |
独立性 |
| ShimCache(AppCompatCache) |
SYSTEM hive 的注册表值 |
程序被启动过的路径与时间 |
系统必开,普通设置无法关闭 |
| Amcache |
%SystemRoot%\AppCompat\Programs\Amcache.hve |
程序的路径、SHA-1、PE 编译时间、首次执行时间 |
Windows 8+ 默认开启 |
三者形成三角验证:Prefetch 给次数与精确时间、ShimCache 给「系统看到过它启动」、Amcache 给「文件身份与首次运行时刻」。任何一个单独用都有缺口,交叉才能收敛。
这两个工件各有一个极易导致错误结论的陷阱:
- ShimCache 的时间戳不是执行时间——它是目标 exe 的文件修改时间。
- ShimCache 在硬断电时会丢失尚未落盘的条目——它常驻内存,正常关机/重启时才写入 hive。
它与 Prefetch 深度解析 并列,属于分析环节的执行侧证据,但独特价值在现场状态这个维度:
- 上游依赖 注册表 hive 的正确提取。离线注册表必须用
ControlSet001(而不是 CurrentControlSet),这是本领域最高频的坑,方法见注册表取证;Amcache 则是独立文件,接入方式见只读挂载与安全接入。
- 它自己要先判断现场状态。系统运行中 / 正常关机 / 硬断电,三种状态下 ShimCache 的可用性完全不同,这个判断必须在分析之前做完。
- 下游支撑 两处。Amcache 的 SHA-1 是文件身份的锚点,可用于判断「这个可执行文件是否被替换过」;内存里的 ShimCache 覆盖了磁盘版的缺口,从内存读取的方法见 3.3。
落到具体形态上:
| 形态 |
位置 |
关键结构 |
| ShimCache |
HKLM\SYSTEM\ControlSet001\Control\Session Manager\AppCompatCache\AppCompatCache |
REG_BINARY 形式的注册表值,不是独立文件;条目有序排列,有容量上限,满了之后新条目挤掉最旧的 |
| 路径历史 |
Windows 2000/XP/2003 为 ...\Session Manager\AppCompatCache(无内层子键),Vista 及以后多一层 |
用错版本路径是常见的「读不到」原因 |
| 格式版本 |
Win7 及以前旧格式有 0x01 Executed 标志位,可区分「枚举过」与「执行过」 |
Win8 起该标志位被移除,改为 AVL 树式新结构,所有条目格式上等价 |
| Amcache |
C:\Windows\AppCompat\Programs\Amcache.hve |
完整路径、SHA-1(只覆盖前 30 MB)、PE 编译时间戳、首次执行时间、文件大小 |
| 内存中的 ShimCache |
活系统或内存镜像 |
数据主要常驻内存,会话注销与正常关机时才落盘 |
三个判定要点:ShimCache 与功能开关无关、Win8 起无法区分「枚举过」与「执行过」、Amcache 的 SHA-1 只覆盖前 30 MB。
据此能回答的是:系统是否尝试启动过某个路径的程序(不受 Prefetch 开关影响)、某个可执行文件的 SHA-1 与 PE 自称的编译时间、以及它在系统上的首次执行时刻。
回答不了的是这些,外加两个必须写进报告的结论:
- ShimCache 的时间戳是目标 exe 的
$STANDARD_INFORMATION 最后修改时间,不是执行时间。 系统登记条目时读的是文件信息(为判断版本与兼容性),顺手把文件修改时间记了下来。它不记录「你什么时候点了这个 exe」——兼容性缓存不需要这个信息。把文件修改时间当执行时间,是本领域最常见的技术性错误。
- 硬断电机器上磁盘 ShimCache 的「缺失」是技术必然,不是证据。 数据常驻内存,硬断电(拔电源、崩溃、长按电源键)会使尚未落盘的更新丢失,磁盘上的 ShimCache 停留在上一次成功写入时。写成「未发现相关程序执行痕迹」是错误表述,正确写法是「因系统异常断电,该时段执行痕迹未落盘,无法据此判断」。
- Win8/10/11 上不能证明执行成功。 旧格式的
0x01 Executed 标志位已被移除,所有条目在格式上等价,只能证明「系统尝试启动过该路径的程序」。
- 完整的文件哈希。 Amcache 的 SHA-1 只覆盖前 30 MB(为性能做的设计),比对时必须标注范围,不能当作完整文件哈希。
- 文件的真实编译时间。 PE 编译时间戳可被伪造,只作「程序自称的编译时间」。
- 执行次数。 ShimCache 与 Amcache 都不记次数,次数只有 Prefetch 的
Run Count 可靠。
读到这里你应该已经理解:NTFS 的 $STANDARD_INFORMATION 四个时间戳语义,否则无法理解 ShimCache 时间戳的真实含义,见NTFS 结构与 MFT 解析;注册表 hive 的离线提取方法,特别是 ControlSet001 的选择,见注册表取证;Prefetch 深度解析的结论,后面的可信度对比表需要拿它作对照;Windows 的关机语义与内存落盘机制,这是判断现场状态的前提;以及内存取证的基本概念(从内存读注册表键值),见内存取证总览。
二、核心原理
2.1 ShimCache 是什么、为什么必然存在
Windows 需要知道「这台机器上曾跑过哪些程序」来解决应用程序兼容性问题:老程序在新系统上跑不起来时,系统需要找到可能让它跑起来的垫片(shim)。
为加速这个查找,系统维护一个有序的路径缓存,这就是 ShimCache。
关键特性:
- 与功能开关无关。删掉注册表值后,重启时系统会重新建表。
- 有容量上限,满了之后新条目会挤掉最旧的。
- 以
REG_BINARY 形式存放在注册表里,不是独立文件。
注册表位置(离线必须用 ControlSet001):
HKLM\SYSTEM\ControlSet001\Control\Session Manager\AppCompatCache\AppCompatCache
路径历史:Windows 2000 / XP / 2003 是 ...\Session Manager\AppCompatCache(无 AppCompatCache 子键层);Vista 及以后多了一层。用错版本路径是常见的「读不到」原因。
格式随版本变化:Windows 7 及以前的旧格式条目中有 0x01 Executed 标志位,可区分「文件被枚举过」与「文件被执行过」;Windows 8 起该标志位被移除,改为 AVL 树式新结构,所有条目在格式上等价。因此在 Win8/10/11 上,ShimCache 只能证明「系统尝试启动过该路径的程序」,不能证明执行成功。
2.2 ★ 陷阱一:时间戳不是执行时间
这是本篇最重要的技术纠正。
大量二手资料把 ShimCache 的时间戳描述为「执行时间」,这是错的。
ShimCache 条目中记录的时间,语义是:
目标 exe 文件本身的 $STANDARD_INFORMATION 最后修改时间(NTFS MFT 记录里的「修改时间」)
系统登记条目时读的是文件信息(为判断版本与兼容性),顺手把文件修改时间记了下来。
它不记录「你什么时候点了这个 exe」——兼容性缓存不需要这个信息。
这带来一个非常危险的后果:
对 exe 执行 timestomp(把文件修改时间改为 2019-01-01)
↓ ShimCache 中对应条目的时间也变成 2019-01-01
↓ 分析师看到 ShimCache 说「2019 年运行过 notepad.exe」→ 错误结论
| ShimCache 时间显示 |
真实含义 |
不能推断 |
| 2019-03-11 02:14:55 |
那个 exe 文件的修改时间是 2019-03-11 |
不能推断「2019 年运行过它」 |
| 2024-11-10 08:22:41 |
该 exe 在 2024-11-10 被修改/编译 |
不能推断「当天运行过它」 |
| 无时间 |
条目存在,但文件已删或时间不可解析 |
不能推断「从未运行过」 |
报告表述模板:
- 错误:「ShimCache 显示
malware.exe 于 2019-03-11 被执行过。」
- 正确:「ShimCache 中存在
C:\...\malware.exe 的条目,表示系统曾尝试启动过该路径下的程序。条目时间 2019-03-11 02:14:55 是该文件的最后修改时间(经 MFT 记录核对一致),不构成执行时间证据。」
真正执行时间的正确来源:
| 需求 |
正确来源 |
| 程序执行时间 |
Prefetch、事件日志 4688、Sysmon 事件 1 |
| 文件访问时间 |
$STANDARD_INFORMATION 的 Access(易被关闭或延迟更新) |
| 文件修改时间 |
MFT $STANDARD_INFORMATION(ShimCache 里就是这个) |
2.3 ★ 陷阱二:硬断电可能丢失尚未落盘的条目
这是本篇最重要的实战提醒。
程序启动 → 兼容性服务把路径加入【内存中的 ShimCache】
↓ 内存中持续更新
【会话注销 / 系统正常关机重启】→ 写入 SYSTEM hive(磁盘)
- 数据主要常驻内存,在会话注销与系统正常关机/重启时写入
SYSTEM hive 的 AppCompatCache 值。
- 硬断电(拔电源、崩溃、长按电源键)会使尚未落盘的更新丢失,磁盘上的 ShimCache 停留在上一次成功写入时的状态(具体粒度随 Windows 版本与内存压力而异,无法精确到「丢失最后几条」)。
最后正常关机:2024-11-14 23:40;案发:2024-11-15 01:07(USB 接入并运行程序)
→ 磁盘 ShimCache 很可能没有 01:07 之后运行的程序条目
→ 这不是「没执行」,而是「证据未落盘」
| 现场状态 |
处置 |
| 系统运行中 |
先做内存采集再关机,用内存插件读 ShimCache |
| 正常关机 |
磁盘 ShimCache 基本完整,可直接用 |
| 硬断电/崩溃 |
磁盘 ShimCache 只反映上次成功写入的状态,必须记录此局限 |
要反复强调的一句话:遇到硬断电的机器,磁盘 ShimCache 的「缺失」是技术必然,不是证据。 写成「未发现相关程序执行痕迹」是错误表述;正确写法是「因系统异常断电,该时段执行痕迹未落盘,无法据此判断」。
2.4 Amcache:另一个独立工件
Amcache(Application Compatibility Cache)是 Windows 8 引入的独立注册表 hive,位于 C:\Windows\AppCompat\Programs\Amcache.hve,记录程序的完整路径、SHA-1 哈希、PE 编译时间戳、首次执行时间与文件大小。
两条重要限制:SHA-1 只覆盖文件前 30 MB(为性能做的设计),比对时必须标注范围,不能当作完整文件哈希;PE 编译时间可被伪造,只作「程序自称的编译时间」。
独立性价值:即使 Prefetch 被关闭、即使 ShimCache 因断电丢失,Amcache 作为独立文件仍可用。
局限是只记录首次执行时间,文件从未运行过则不在其中。
2.5 三者的可信度对比
| 维度 |
Prefetch |
ShimCache |
Amcache |
| 记录「执行尝试」 |
是 |
是 |
是(首次) |
| 记录「执行次数」 |
是(Run Count) |
否 |
否 |
| 记录「精确执行时间」 |
是(8 槽内) |
否(是文件修改时间) |
仅首次 |
| 记录「完整路径」 |
是 |
是 |
是 |
| 记录「文件哈希」 |
否 |
否 |
SHA-1(前 30MB) |
| 记录「PE 编译时间」 |
否 |
否 |
是(可伪造) |
| 可被功能开关关闭 |
是 |
否 |
否 |
| 硬断电丢失未落盘条目 |
否(运行时写盘) |
可能 |
部分 |
| 独立文件 |
否 |
否(在 SYSTEM hive 内) |
是 |
推荐组合策略:先查 EnablePrefetcher 确认 Prefetch 是否可用;再查 ShutdownTime 判断是否异常关机(正常则 ShimCache 可用,异常则需内存采集或记录局限)。
无论前两步如何,Amcache 作为独立文件均可用于建立路径与哈希基线;最后三者加事件日志与 LNK 交叉验证形成结论。
三、操作步骤
3.1 提取并解析
mkdir -p /evidence/hives
# SYSTEM hive 与 Amcache.hve(Win8+;Win7 用 RecentFileCache.bcf)
for h in SYSTEM Amcache.hve; do
fls -o 2048 -r -p /work/DigiForensics.dd | grep -iE "config/SYSTEM$|AppCompat/Programs/$h$" \
| grep -E '^r/r' | awk '{print $2}' | head -1 \
| while read -r ino; do icat -o 2048 /work/DigiForensics.dd "$ino" > "/evidence/hives/$h"; done
printf '%s: ' "$h"; xxd -l 4 -p "/evidence/hives/$h" # 期望 72656766(regf)
done
# 确认控制集编号(离线必做)
rip.exe -p /evidence/hives/SYSTEM -a /tmp/select.txt select
# \Select\Current : 1 → 路径写 ControlSet001
# 解析 ShimCache:自动识别 2000~11 各种格式,版本判断交给工具
AppCompatCacheParser.exe -f C:\evidence\hives\SYSTEM --csv C:\evidence\out
# → Path / ModifiedTime / InsertedTime / Executed(仅 Win7 有 Executed)
AmcacheParser.exe -f C:\evidence\hives\Amcache.hve --csv C:\evidence\out
# → Path / SHA1 / Compile Time / First Run Time / File Size
| 工具 |
输入 |
输出 |
适用 |
RegRipper appcompatcache |
SYSTEM hive |
文本/十六进制 |
快速查看,二进制需另解析 |
| AppCompatCacheParser |
SYSTEM hive |
CSV(自动识别版本) |
首选 |
| Volatility 3 |
内存镜像 |
CSV/TXT |
运行中 / 断电前机器 |
3.2 核对时间戳语义(必做)
拿到 ShimCache 条目后,必须核对它的时间到底是什么:
# 从 ShimCache CSV 中挑一个可疑路径
grep -i 'suspicious' /evidence/out/AppCompatCacheParser_*.csv
# → Path: C:\Users\admin\AppData\Local\Temp\suspicious.exe
# ModifiedTime: 2019-03-11 02:14:55
# 拿这个时间与文件的 MFT 时间戳对照
fls -o 2048 -r -p /work/DigiForensics.dd | grep -i 'suspicious.exe'
istat -o 2048 /work/DigiForensics.dd <inode>
# Modified: 2019-03-11 02:14:55 ← 与 ShimCache 一致
# Created : 2024-11-10 08:22:41
# Accessed: 2024-11-14 21:30:44
| 对照结果 |
结论 |
| ShimCache 时间 = MFT Modified |
证实是文件修改时间,不是执行时间 |
| ShimCache 时间 ≠ MFT Modified |
文件可能已被 timestomp,或时间字段解析有误,需进一步核实 |
| 文件已删除,无法对照 |
用 Amcache 的 Compile Time 互证;或从 VSS/备份找旧版本 |
这个核对只需做一次。一旦确认「ShimCache 时间 = MFT Modified」,后续所有条目都按这个语义解读。
3.3 从内存读取 ShimCache
机器运行中时磁盘 ShimCache 可能未落盘,硬断电时未落盘部分已丢失;运行中可从内存读当前会话条目。
Volatility 3 插件:windows.shimcachemem.ShimcacheMem,从对应版本的内存结构中读取。
vol -f /evidence/DigiForensics-mem.raw windows.shimcachemem.ShimcacheMem
vol -f /evidence/DigiForensics-mem.raw windows.info # 先确认内核符号可用
vol -f /evidence/DigiForensics-mem.raw windows.amcache.Amcache
| 情形 |
该怎么做 |
| 系统运行中 |
先做内存采集,再用本节命令 |
| 系统硬断电 |
内存已丢,未落盘部分无法恢复,必须记录局限 |
| 系统正常关机 |
用 3.1 的磁盘路线即可 |
3.4 三者交叉验证