泄露程序执行记录的 Windows Shim 缓存

Windows Shim 是一种让新操作系统能兼容旧程序的技术。顾名思义,Shim 会在应用程序和操作系统之间增加一层处理逻辑,比如拦截和修改 API 调用的参数和返回值,模拟旧的操作系统,实现兼容应用程序的目的。

由于并非所有的程序都需要进行兼容处理,Windows 为此提供了一个名为 Application Compatibility Database (应用程序兼容性数据库)的数据库,通常简写为 SDB,其中保存了需要兼容的程序清单,以及需要进行的兼容性操作。然而,每次创建进程都要查询数据库会损耗性能,Windows 将查询结果缓存在内存中。该缓存被称作 Application Compatibility Cache,大小为 1024 条,并且在每次系统关机时写入注册表作为持久化保存。正是由于这个特性,导致泄露了用户程序执行记录。

--- title: Windows Shim 原理 --- flowchart TD step1["ntdll.dll!NtCreateProcess 创建进程"] step2["ntdll.dll!LdrpInitializeProcess 初始化新进程"] step3["ntdll.dll!LdrpLoadShimEngine 加载并初始化 Shim 引擎"] step4["shimeng.dll!SE_InstallBeforeInit 加载 SDB 数据库 %WINDIR%\AppPatch\sysmain.sdb"] step1 --> | 进入新进程上下文 | step2 --> | PEB pShimData 成员不为空 | step3 --> step4
注意
该流程还原自 ReactOS 源代码,更接近于 Windows XP/2003 系统,和当前主流的 Windows 操作系统(比如 Windows 10/11)并不一致。

Shim Cache 存放在注册表如下位置:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\AppCompatCache
regedit
app-compat-cache

可以使用 Zimmerman Tools 中的命令行工具 AppCompatCacheParser 来查看缓存内容:

cmdAppCompatCacheParser.exe --csv c:\temp --csvf results.csv

需要注意的是,由于注册表中保存的是上一次系统关机时写入的持久化缓存,所以工具无法提取到这次开机后运行的程序记录。也因为如此,仅仅删除该注册表键也是无法清除缓存记录的——重启后你仍能在注册表里找到它。

好在 Windows 提供了两个 API 可以用来完成清理缓存操作:

  1. 由 Apphelp.dll 提供的 ShimFlushCache(),最低支持支持 Windows Vista 和 Windows 2008 操作系统

    c++BOOL WINAPI ShimFlushCache(
      _In_opt_ HWND      hwnd,
      _In_opt_ HINSTANCE hInstance,
      _In_opt_ LPCSTR    lpszCmdLine,
      _In_     int       nCmdShow
    );
  2. 由 kernel32.dll 导出的 BaseFlushAppcompatCache(),最低支持支持 Windows XP 和 Windows 2003 操作系统

    c++BOOL WINAPI BaseFlushAppcompatCache(void);

由于 BaseFlushAppcompatCache() 没有参数,我们可以通过 rundll32.exe 来进行调用(必须以管理员身份运行):

cmdrundll32.exe kernel32.dll,BaseFlushAppcompatCache

最后,记得删除注册表中的 AppCompatCache 键值。


顺带一提,我们可以创建自定义的 SDB 数据库,实现代码注入。这种挂马方法比较隐蔽,沙盒无法检测到恶意代码的注入行为,常见的系统分析软件,比如 Autoruns,也不会检测 Shim 数据库。

使用微软的 Compatibility Administrator 工具可以创建自定义 SDB,该程序是 Windows Assessment and Deployment Kit 中的一个组件。通常被用于注入代码的兼容性修复操作有 InjectDllRedirectEXE

customsdb

不过这两种操作只能用于 32 位程序。

创建的 .sdb 文件可以用 Windows 自带的 sdbinst 命令安装到其他系统。通过检查下面的注册表项来确认系统是否被植入自定义 SDB:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Custom\
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\InstalledSDB\