一、概述
Windows 8 起系统里就常驻一个资源使用监控组件(SRU)。它每小时把每个应用的运行状况、资源消耗和网络流量写进一个 ESE 数据库。
任务管理器的「应用历史记录」标签页只露出其中很小一角,绝大多数记录在界面上根本看不到。取证价值正在这里。界面上读不到的,SRUDB.dat 里留着——用户 SID、可执行文件完整路径、前后台 CPU 时间、磁盘读写字节数、上下文切换次数、发送与接收字节数,一条记录里全有。
数据外泄案件中,SRUM 常常是唯一能回答「哪个用户在什么时段、用哪个程序、往外发了多少数据」的工件。它留下的是已经结束的网络会话的量化痕迹,会话本身早消失了,浏览器历史里什么都查不到。
代价是窗口短。历史通常只有 30 到 60 天,跨重启保留,被覆盖后无法恢复。所以它必须优先提取:每多等一天,可用证据就少一天。
这属于流程里「先提取什么」这个决策环节,位置在采集优先级判断之前。上游先解决镜像接入:SRUDB.dat 是 ESE 数据库,在活系统上直接打开或挂载分析都可能改写它,原始文件必须先固定下来,参见只读挂载与安全接入。
它自己要处理三件事。数据库可能被占用,也可能处于未正常关闭状态,得先判断可读性再决定是否修复;SID 必须关联到用户名,否则数据再准也落不到具体的人身上;时间基准要显式转换,SRUM 的 Timestamp 是 Unix 秒,跟 NTFS 的 FILETIME 完全是两回事。
下游依赖它的输出。网络发送字节数是外泄量化的核心输入。UserInputTime 接近 0 而 Duration 很大,说明程序在跑但没人操作——这个区分是判断自动化脚本与人工操作的关键。InterfaceLuid 还能把「在家大量外发」与「在公司正常同步」分开。
这些内容 SRUM 自己搞不定。程序执行要靠Prefetch 深度解析与ShimCache 与 Amcache确认,网络会话细节要靠事件日志深度分析。
落到具体表上:
| 表 |
主要字段 |
取证价值 |
Application Resource Usage({D10CA2FE-...FA89}) |
TimeStamp、AppId、UserId、前后台 CycleTime、BytesRead/BytesWritten |
程序执行 + 用户归属 + 磁盘读写量 |
App Timeline Provider({5C8CF1C7-...}) |
AppId、UserId、EndTime、Duration、InFocusTime、UserInputTime |
前台活跃时长、是否有真实用户输入 |
| Network Data Usage |
TimeStamp、AppId、UserId、BytesSent、BytesRecvd、InterfaceLuid、L2ProfileId |
外泄量化的核心证据 |
| Network Connectivity Usage |
网络配置标识、连接时长、接口类型 |
印证「当时连的是哪类网络」 |
| Energy Usage |
累计电量消耗 |
长时间后台运行的旁证 |
| Push Notifications |
通知类型与数量 |
消息类应用活跃度 |
| 落点 |
%SystemRoot%\System32\SRU\SRUDB.dat |
ESE 数据库,需专用工具读取,不能用 SQLite 工具 |
三处容易出错的判定先记下来。App Timeline 是独立 GUID,Win8.1 上找不到它属于正常状态——这张表自 Win10 1607 起才存在。FA89 与 FA86 只差一位,却是两张完全不同的表,不要混引。Timestamp 的基准是 Unix 秒,Duration 是毫秒,量纲要逐列确认。
据此能回答的是:某个可执行文件在某个小时段是否运行过、属于哪个用户、消耗了多少 CPU 与磁盘 I/O、发送与接收了多少字节、当时窗口在前台多久、检测到多少键盘鼠标输入,以及走的是哪块网卡。
回答不了的部分很多,外加三条必须先写进报告的时间语义结论:
Timestamp 是记录的插入时间,不是行为发生时间。它表示「这一小时的数据被写入的时刻」,精度只有小时级,同一时间戳下常有多条记录共享它。
- 每小时首条与末条记录不精确对应真实的首次与末次执行。任何基于 SRUM 的时间断言都只能是估算,需要用 Prefetch、事件日志、NTFS 时间戳去收敛。
- 「某个程序跑过几小时」不等于「某个程序成功完成了某事」。SRUM 记的是资源消耗,不记行为结果。
- 具体传出了什么内容。字节数是量化指标,不是内容,内容判定要靠浏览器历史或代理侧日志。
- 被覆盖的时段。窗口通常只有 30 到 60 天,超出部分在镜像里就不存在。
- 网络会话的目的地。
InterfaceLuid 给的是接口标识,不是远端地址,后者要靠网络侧或事件日志。
UserId 对应的人。字段是 SID 字符串,必须关联注册表才能落到具体人,只报 SID 在报告里不可读。
AppId 一定是路径。对第三方程序通常是可执行文件完整路径,对系统内置组件则可能是描述性名称,判断「是否用户态程序」时不能想当然。
读到这里你应该已经知道:ESE 数据库的结构与读取方式,它是 Windows 自己的存储引擎,与 SQLite 完全不是一回事,误用 SQLite 工具只会得到无意义的输出;SID 与用户名的关联方法,基础在注册表取证的 hive 解析;
时间戳的纪元换算,SRUM 的 Unix 秒与 NTFS 的 FILETIME 之间需要显式转换;以及 Windows 的用户与网络配置概念,InterfaceLuid 与 L2ProfileId 的语义需要这块背景。
还有一条纪律:修复数据库是不可逆操作,只能在原始副本上做,原始文件的哈希必须先记录。
二、核心原理
2.1 位置与获取约束
数据库位于:
C:\Windows\System32\sru\SRUDB.dat
它与注册表中的扩展描述键配套:
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SRUM\Extensions
该键的每个子键对应一个 GUID 扩展及其配套 DLL,比如应用资源使用由 appsruprov.dll 提供、网络使用由 nduprov.dll 提供。
键里没有活动数据。它只描述数据库中会有哪些表,属于写入前的描述与缓冲配置,作用与数据库表结构对应,很容易被误当成数据源。
SRUDB.dat 是 ESE(Extensible Storage Engine,又称 Jet Blue)数据库,Active Directory、Windows Search 用的是同一引擎。
采集环节有两条硬约束:
- 在线系统上它被 Diagnostic Policy Service 等组件持有,通常无法直接复制,要通过卷影副本、离线磁盘、系统镜像或采集工具获取。
- 未正常关机时数据库可能处于非干净状态,解析前需要用
esentutl 修复。
2.2 落盘模型:为什么时间戳是「小时」
SRUM 的采集与写入模型决定了它的全部时间语义:
- 运行时数据先进入内存或注册表侧的暂存结构;
- 约每小时一次落盘到
SRUDB.dat;
- 系统关机或重启时也会触发一次落盘。
由此推出三条必须写进报告的结论。
Timestamp 是记录的插入时间,不是行为发生时间,它表示「这一小时的数据被写入的时刻」。
精度只有小时级,同一条时间戳下常有多条记录共享它。
每小时首条与末条记录都不精确对应真实的首次与末次执行。任何基于 SRUM 的时间断言都只能是估算,需要用 Prefetch、事件日志、NTFS 时间戳去收敛。
2.3 关键表与字段
| 表 |
GUID |
主要字段 |
取证价值 |
| Application Resource Usage |
{D10CA2FE-6FCF-4F6D-848E-B2E99266FA89} |
TimeStamp、AppId、UserId、前后台 CycleTime、BytesRead/BytesWritten、NumReadOperations/NumWriteOperations |
程序执行 + 用户归属 + 磁盘读写量 |
| App Timeline Provider |
{5C8CF1C7-7257-4F13-B223-970EF5939312} |
AppId、UserId、EndTime、Duration、InFocusTime、UserInputTime |
前台活跃时长、是否有真实用户输入 |
| Network Data Usage |
{973F5D5C-1D90-4944-BE8E-24B94231A174} |
TimeStamp、AppId、UserId、BytesSent、BytesRecvd、InterfaceLuid、L2ProfileId |
外泄量化的核心证据 |
| Network Connectivity Usage |
{DD6636C4-8929-4683-974E-22C046A43763} |
网络配置标识、连接时长、接口类型 |
印证「当时连的是哪类网络」 |
| Energy Usage |
{FEE4E14F-02A9-4550-B5CE-5FA2DA202E37} |
累计电量消耗 |
长时间后台运行的旁证 |
| Push Notifications |
{D10CA2FE-6FCF-4F6D-848E-B2E99266FA86} |
通知类型与数量 |
消息类应用活跃度 |
两条对应关系值得记住。
App Timeline Provider 是独立 GUID({5C8CF1C7-...}),与 Application Resource Usage 的 {D10CA2FE-...FA89} 不是同一张表。后者采集自 Windows 8.1,App Timeline 自 Windows 10(1607)起才有,Win8.1 机器上找不到它是正常的,不构成取证失败。
Application Resource Usage 的 GUID 尾号是 FA89,Push Notifications 是 FA86,两者仅差一位却完全不是一回事,不要混引。
UserId 字段是 SID 字符串,必须与注册表中的用户名关联才能落到具体人:
SELECT name FROM sam WHERE label = 'Users' AND sid = 'S-1-5-21-...';
AppId 字段对第三方程序通常是可执行文件完整路径,对系统内置组件则可能是描述性名称而非路径,这一点在判断「是否用户态程序」时不能想当然。
Network Data Usage 的 InterfaceLuid 标识走的是哪块网卡,L2ProfileId 标识当时的网络配置档。笔记本在咖啡厅与在办公室对应不同接口,LUID 也不同,这个维度能把「在家大量外发」与「在公司正常同步」区分开。
App Timeline 的时间语义要单独说明。
EndTime 是该次运行的近似结束时刻,Duration 是总时长,单位为毫秒;InFocusTime 是窗口处于前台的时间,UserInputTime 是检测到键盘或鼠标输入的时间。
UserInputTime 接近 0 而 Duration 很大,意味着程序在跑但没有人操作它,这是判断自动化脚本与人工操作的关键区分。
2.4 时间字段基准
SRUM 表中的 Timestamp 字段是 Unix 纪元起的秒数,与 NTFS FILETIME(1601 起 100 纳秒)完全不同。
Timeline 表中的部分持续时间字段以微秒或毫秒表示,量纲同样要逐列确认,不能凭字段名猜单位。
把 SRUM 与 NTFS、事件日志对齐时,转换要显式完成:
datetime(Timestamp, 'unixepoch') -- Unix 秒
-- 等价于 FILETIME 视角:
(Timestamp * 10000000) + 116444736000000000
三、操作步骤
3.1 提取与初步固定
在线系统先做卷影副本;离线检材从镜像中抽取:
mmls /work/DigiForensics.dd
fsstat -o 2048 /work/DigiForensics.dd
fls -o 2048 -r -p /work/DigiForensics.dd \
| grep -Ei '/Windows/System32/sru/.*\.dat$' \
| awk '{print $2}' \
| while read -r ino; do
icat -o 2048 /work/DigiForensics.dd "$ino" > "/evidence/sru/$(basename "${ino%%:*}").dat"
done
同时提取关联的注册表 hive,用于 SID 解析与扩展表对照:
fls -o 2048 -r -p /work/DigiForensics.dd \
| grep -Ei '/Windows/System32/config/(SOFTWARE|SYSTEM)$' | grep -E '^r/r' | awk '{print $2}' \
| while read -r ino; do
icat -o 2048 /work/DigiForensics.dd "$ino" > "/evidence/registry/$(basename "${ino%%:*}").hive"
done
3.2 修复数据库
复制一份工作副本后再修复,不要在原始证据上操作:
cp "/evidence/sru/SRUDB.dat" "/evidence/sru/work/SRUDB.dat"
cd /evidence/sru/work
esentutl.exe /r SRUDB /i
esentutl.exe /p SRUDB.dat
如果 esentutl 报告日志文件缺失,可在工作目录补齐与数据库同名的日志文件占位后再试。具体错误码含义以实际系统输出为准,不要跳过失败直接进入解析。
3.3 解析导出
SrumECmd.exe -f "C:\evidence\sru\SRUDB.dat" `
--csv "C:\evidence\out\srum" --csvf "srum_report.csv"
SrumECmd 会输出按表分类的 CSV,典型类别包括应用资源使用(AppResourceUseInfo)、应用时间线(AppTimelineProvider)与网络使用(NetworkUsages)。传入 SOFTWARE hive 可以自动把 SID 关联为用户名,省去手工查表。
3.4 手工查询核心表
用 esedatabaseview 或支持 ESE 的工具直接查看表结构后执行查询。先按「非常规应用 + 异常时段」过滤,再看网络用量:
-- 发送字节量 Top 20 的应用与用户
SELECT AppId, UserId, BytesSent, BytesRecvd,
datetime(Timestamp,'unixepoch') AS hour_utc
FROM "{973F5D5C-1D90-4944-BE8E-24B94231A174}"
ORDER BY BytesSent DESC
LIMIT 20;
-- 非常规可执行文件路径(系统目录之外的深路径)
SELECT AppId, UserId,
datetime(Timestamp,'unixepoch') AS hour_utc,
ForegroundBytesWritten AS fg_write
FROM "{D10CA2FE-6FCF-4F6D-848E-B2E99266FA89}"
WHERE AppId LIKE '%Users%' AND AppId LIKE '%.exe'
ORDER BY fg_write DESC;
3.5 与其他工件对齐
SRUM 单独只能给出「某小时某程序有活动」,必须交叉。
Prefetch 确认程序确实执行过,并给出执行次数与最后运行时间。事件日志用 4624 确认登录主体,7045 系统日志确认服务安装,判断是否为持久化。NTFS $UsnJrnl 确认文件创建与写入的具体时刻,把小时级估算收敛到分钟。防火墙或代理日志用接口 LUID 与时间窗去匹配真实连接。
若 SRUM 显示某程序在非工作时间有大量 BytesSent,而 Prefetch 记录的执行时间与之相符,两者互证后结论强度显著提升。
四、常见陷阱
4.1 采集与数据库处置
陷阱 1:★ 在原始证据文件上跑 esentutl 修复数据库
现象
从镜像里 icat 出来的 SRUDB.dat 直接交给 esentutl /r 修复,解析成功后把修复后的文件留作工作副本,原始文件覆盖掉了。报告里写「已修复并解析 SRUDB」。
原因
SRUDB.dat 是 ESE 数据库,未正常关机时可能处于非干净状态,存在未提交事务、日志与数据库不一致,直接解析会失败或只解析出部分表。
修复确实必要。但要注意它的副作用:
esentutl /r 会改写数据库结构,重建索引、回滚未提交事务、重写页结构
esentutl /p 整理数据库时直接重写整个文件
- 修复后的文件不再等于磁盘上的原始内容,哈希改变
一旦在原始文件上做这些操作,「该文件与磁盘镜像一致」这个前提就没了。
SRUM 是案件中经常成为唯一量化证据的工件,它的原始性争议代价很高。
影响
- 检材哈希链断裂,无法自证未改写证据,质证时构成硬伤
- 修复过程可能丢弃未提交事务中的数据,而这部分数据恰恰可能是关键记录
- 「修复后能解析」这个结果无法说明修复前后的差异,报告无法交代处置过程
核查
原始文件只读留存,工作副本上修复,两份都要记录哈希:
第一步:原始抽取文件立即计算哈希并登记
shasum -a 256 /evidence/sru/SRUDB.dat | tee /evidence/sru/SRUDB.dat.sha256
第二步:复制工作副本后再修复
mkdir -p /evidence/sru/work
cp -p /evidence/sru/SRUDB.dat /evidence/sru/work/SRUDB.dat
cd /evidence/sru/work
esentutl.exe /r SRUDB /i