首页 / 资讯中心 / 文章详情

Windows使用痕迹检测工具:基于MFT、ShellBag与Prefetch的本地行为取证

Windows使用痕迹检测工具:基于MFT、ShellBag与Prefetch的本地行为取证 ★ FEATURED ARTICLE
简介这是一款面向Windows系统安全分析与数字取证初学者的本地化痕迹检测工具适用于IT运维人员、网络安全爱好者及计算机取证入门学习者用于快速梳理个人设备近期使用行为轨迹。资源包共39个文件含5个核心可执行程序如RegistryFinder.exe、查看器.exe、4个批处理脚本用于注册表上下文菜单增删、17个图标资源ico/svg及配置类文件config、ini、xml等整体体积仅4.5MB轻量便携无需安装即可运行。已有1660人下载学习体现了其在实操型取证小工具领域的实用认可。用户可直接获取完整的注册表痕迹解析能力包括数月乃至数年内的目录/文件访问记录、关键词搜索历史、USB设备接入日志并附带离线注册表浏览、结果导出、界面定制等配套功能所有操作均在本地完成不联网、不写入、不泄露隐私。1. Windows使用痕迹检测工具不是“监控软件”而是你电脑里沉睡的数字考古现场上周帮某高校实验室复现一个学生提交的毕业设计——他声称自己“没在考试期间打开过任何参考资料”但导出的浏览器历史、剪贴板记录、最近文档时间戳和 USB 设备插拔日志四条时间线在凌晨2:17完全重合。这不是玄学是这套 Windows 使用痕迹检测工具干的。它不依赖远程控制、不写入可疑服务、不挂钩系统关键API而是像一位沉默的法医只读取 Windows 自身留下的原始证据链NTFS $MFT 中的文件创建/访问/修改时间MAC 时间、ShellBag 注册表项里的资源管理器路径导航记录、Prefetch 文件中程序启动频次与上次执行时间、JumpList 缓存里的最近打开项目、以及 Event Log 中 Security 和 System 日志里被忽略的登录登出、服务启停、驱动加载事件。它适合安全审计人员做终端合规检查、IT 管理员排查异常操作、取证初学者理解 Windows 证据留存机制也适合开发者拆解“如何在无管理员权限下尽可能多地采集本地行为痕迹”。这不是一个黑匣子而是一份可验证、可调试、可裁剪的痕迹采集清单。2. 架构与模块拆解六个核心采集器如何协同构建证据时间轴这套工具采用模块化采集统一时间归一化结构化输出的设计思路。所有采集器均以独立 Python 模块实现通过collector_base.py提供统一接口collect()方法返回dict主程序main.py负责调度、去重、按时间戳排序并导出为 JSON 或 CSV。整个流程不写入新文件、不修改注册表、不调用高权限 API如NtQuerySystemInformation绝大多数操作普通用户权限即可完成。关键在于每个模块只负责“拿什么”和“怎么拿”不负责“怎么用”——这使得二次开发时替换某个采集器比如把 JumpList 解析换成自定义的 Edge History 解析几乎不影响其他模块。2.1 NTFS $MFT 时间分析器绕过文件系统缓存的真实时间证据Windows 文件系统会为每个文件/目录在主文件表$MFT中维护多个时间戳Created创建、Modified最后修改、Accessed最后访问、Changed元数据最后变更。其中 Accessed 时间默认在 Win10 1803 后被禁用以提升性能但 $MFT 记录本身仍存在且未被清除。本工具通过pytsk3库直接读取磁盘镜像或卷设备如\\.\C:跳过 Windows API 层获取原始 $MFT 条目# collectors/mft_collector.py import pytsk3 import datetime def collect(drive_path\\\\.\\C:): img pytsk3.Img_Info(drive_path) fs pytsk3.FS_Info(img) results [] for file_obj in fs: if not file_obj.info or not file_obj.info.meta: # 跳过无效条目 continue meta file_obj.info.meta # 注意pytsk3 返回的是 Unix 时间戳秒需转为 datetime created datetime.datetime.fromtimestamp(meta.crtime, tzdatetime.timezone.utc) modified datetime.datetime.fromtimestamp(meta.mtime, tzdatetime.timezone.utc) accessed datetime.datetime.fromtimestamp(meta.atime, tzdatetime.timezone.utc) changed datetime.datetime.fromtimestamp(meta.ctime, tzdatetime.timezone.utc) results.append({ path: file_obj.info.name.name.decode(utf-8, errorsignore), type: file if file_obj.info.meta.type pytsk3.TSK_FS_META_TYPE_REG else dir, created_utc: created.isoformat(), modified_utc: modified.isoformat(), accessed_utc: accessed.isoformat(), changed_utc: changed.isoformat() }) return results提示pytsk3需要管理员权限才能直接访问\\.\C:这类物理设备路径。若无权限工具自动降级为扫描C:\Users\*下常见目录如 Desktop、Documents、Downloads的os.stat()结果——虽丢失 Accessed 时间但 Modified/Changed 仍具强参考价值。2.2 ShellBag 注册表解析器还原你打开过的每一个文件夹路径Windows 资源管理器为提升导航速度会将用户访问过的文件夹路径、视图设置、排序方式等缓存在注册表HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Shell\BagMRU及其子键中。这些二进制数据结构复杂但本工具使用registry_parser基于python-registry封装逐层解析# collectors/shellbag_collector.py from Registry import Registry import struct def _parse_shellbag_binary(data): 解析 ShellBag 二进制流提取 Unicode 路径 if len(data) 4: return None # ShellBag 头部2字节签名 2字节长度 sig data[0:2] if sig ! b\x00\x00: # 常见起始标志 return None path_parts [] offset 4 while offset len(data): # 每个路径段以 2 字节长度开头后跟 UTF-16LE 字符串 seg_len struct.unpack(H, data[offset:offset2])[0] offset 2 if seg_len 0 or offset seg_len * 2 len(data): break try: seg data[offset:offset seg_len * 2].decode(utf-16-le) path_parts.append(seg) offset seg_len * 2 except (UnicodeDecodeError, struct.error): break return \\.join(path_parts) if path_parts else None def collect(): reg_path rHKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Shell\BagMRU # 实际代码中通过 winreg 或 Registry 库打开对应 hive # 此处省略打开逻辑聚焦解析核心 results [] # 递归遍历 BagMRU 子键对每个值的数据调用 _parse_shellbag_binary # ... return results逻辑说明ShellBag 是树状结构每个节点代表一次导航动作。工具不只提取最终路径还记录该路径在树中的深度、父节点哈希、以及最后一次访问时间从LastWriteTime获取从而还原出“用户从桌面进入 Downloads再点开某个子文件夹”的完整导航链。2.3 Prefetch 文件解析器程序启动行为的冷数据快照Windows 将常用程序的启动信息缓存在C:\Windows\Prefetch\目录下文件名形如EXPLORER.EXE-3F5B1A2C.pf。每个.pf文件包含程序路径、加载的 DLL 列表、启动次数、以及最后一次运行时间精确到毫秒。本工具使用自研prefetch_parser.py解析# collectors/prefetch_collector.py import struct import os import glob def parse_prefetch_header(data): if len(data) 0x10: return None # Prefetch 格式头部4字节签名 SCCA 4字节版本 4字节文件大小 4字节运行次数 sig data[0:4] if sig ! bSCCA: return None version struct.unpack(I, data[4:8])[0] file_size struct.unpack(I, data[8:12])[0] run_count struct.unpack(I, data[12:16])[0] # 时间戳在偏移 0x70 处UTC last_run_ts struct.unpack(Q, data[0x70:0x78])[0] # 转换为 Windows FILETIME100纳秒单位→ datetime dt datetime.datetime(1601, 1, 1) datetime.timedelta(microsecondslast_run_ts // 10) return { version: version, run_count: run_count, last_run_utc: dt.isoformat(), file_size: file_size } def collect(): pf_dir rC:\Windows\Prefetch results [] for pf_file in glob.glob(os.path.join(pf_dir, *.pf)): try: with open(pf_file, rb) as f: data f.read() header parse_prefetch_header(data) if not header: continue # 提取程序路径在偏移 0x90 后的 Unicode 字符串区 exe_path extract_exe_path_from_pf(data) # 实际函数略 results.append({ prefetch_file: os.path.basename(pf_file), exe_path: exe_path, run_count: header[run_count], last_run_utc: header[last_run_utc] }) except Exception as e: continue # 跳过损坏文件 return results参数说明run_count是累计启动次数非当日次数last_run_utc是最后一次启动的绝对时间比事件日志更早触发程序刚加载时即写入是判断“是否真运行过”的黄金指标。3. 运行与配置三步启动零依赖安装含离线环境适配本工具设计为“开箱即用”无需安装全局 Python 环境或编译依赖。所有第三方库均打包进vendor/目录主程序通过sys.path.insert(0, vendor)优先加载。运行只需三步且每步都附带离线兜底方案。3.1 环境准备Python 3.8 与可选的管理员权限工具要求 Python 3.8 或更高版本因使用zoneinfo处理时区。若目标机器无 Python可使用pyinstaller打包的单文件trace_detector.exe位于dist/目录该文件已内置所有依赖双击即运行。注意pytsk3模块在 Windows 上需预编译的.pyd文件。项目已提供vendor/pytsk3-2023.2-cp38-cp38-win_amd64.pyd对应 Python 3.8 x64若使用 Python 3.9/3.10请自行下载匹配版本替换vendor/pytsk3.pyd。3.2 首次运行命令行参数详解与输出格式选择进入项目根目录执行python main.py --output json --target C:\ --scope full参数说明--output: 指定输出格式支持json默认、csv、html生成带搜索框的静态页面--target: 指定扫描目标路径可为C:\全盘、C:\Users\Alice指定用户、或C:\evidence.img磁盘镜像文件--scope: 控制采集深度light仅注册表事件日志、normal默认加 MFTPrefetch、full全模块含 ShellBagJumpList剪贴板若需静默运行并记录日志python main.py --output csv --target C:\Users\ --scope normal report.csv 2 error.log3.3 输出解读JSON 报告结构与关键字段含义生成的report.json是一个列表每个元素代表一条痕迹记录结构如下{ id: mft_123456, source: mft, timestamp_utc: 2024-05-20T08:14:22.123Z, event_type: file_created, path: C:\\Users\\Alice\\Downloads\\invoice.pdf, details: { size_bytes: 2048000, md5_hash: a1b2c3... }, confidence: 0.95 }关键字段source: 数据来源模块mft,shellbag,prefetch,eventlog等timestamp_utc: 统一转换为 UTC 的时间戳所有模块时间已对齐event_type: 事件语义类型file_created,folder_opened,program_executed,usb_connectedconfidence: 该记录的可信度0.0~1.0由模块内部算法计算如 MFT 时间直接读取为 0.95ShellBag 时间来自注册表 LastWrite 为 0.854. 常见问题与避坑指南那些让你怀疑人生却有解的报错实际部署中90% 的失败不是代码问题而是 Windows 权限、路径编码、或数据损坏导致的“静默跳过”。以下是我在某公司内网批量扫描 200 台 Win10 终端时踩出的血泪经验每一条都附带复现方式和确定性解法。4.1 现象main.py运行后立即退出控制台无任何输出error.log为空原因Python 脚本被 Windows SmartScreen 或企业组策略拦截.py文件关联被禁用导致双击或python main.py实际未执行。解决右键main.py→ “属性” → 勾选“解除锁定”若存在在 PowerShell 中执行Set-ExecutionPolicy RemoteSigned -Scope CurrentUser改用绝对路径调用C:\Python38\python.exe C:\trace_tool\main.py。4.2 现象mft_collector报错OSError: [WinError 5] 拒绝访问。但其他模块正常原因pytsk3尝试打开\\.\C:需要管理员权限而当前 CMD/PowerShell 未以管理员身份运行。解决方案 A推荐右键“Windows PowerShell管理员” → 运行cd /d C:\trace_tool python main.py --scope normal跳过 MFT用os.stat()降级方案 B在config.yaml中设置mft_fallback: true工具将自动切换至目录扫描模式。4.3 现象shellbag_collector解析出大量乱码路径如\\\Desktop原因ShellBag 数据中存在非标准 Unicode 编码或空字节截断decode(utf-16-le)失败后返回?占位符。解决修改shellbag_collector.py中的decode行seg data[offset:offset seg_len * 2].decode(utf-16-le, errorsreplace)errorsreplace会将非法字节替换为 而非抛异常中断更彻底的方案启用--debug-shellbag参数工具会将原始二进制数据保存为shellbag_raw.bin供后续人工分析。4.4 现象prefetch_collector扫描极慢10 分钟CPU 占用 100%原因C:\Windows\Prefetch\目录下存在数百个.pf文件而部分旧版 Prefetch 文件v17/18解析逻辑有缺陷导致无限循环。解决在prefetch_collector.py的parse_prefetch_header函数开头添加校验if version not in [17, 23, 26, 30]: # 仅支持主流版本 return None或直接在命令行限制扫描数量python main.py --prefetch-limit 50只解析最近 50 个。4.5 现象生成的report.json中timestamp_utc全为null原因系统时区设置为非 UTC如China Standard Time而部分模块如事件日志解析未正确调用astimezone(timezone.utc)转换。解决运行前设置环境变量set PYTHONIOENCODINGutf-8 set TZUTC或在main.py开头强制设置import os os.environ[TZ] UTC time.tzset() # 仅 Unix 有效Windows 下此行可忽略5. 二次开发实战如何为你的场景定制一个“USB 插拔追踪器”当基础功能满足后真正的价值在于按需扩展。我曾为某实验室定制一个 USB 设备插拔追踪模块用于监控实验设备接入行为。它不依赖 WMI常被禁用而是解析C:\Windows\System32\winevt\Logs\System.evtx中的EventID 20001设备安装和20003设备移除事件。以下是如何从零添加该模块的完整路径。5.1 新建采集器模块usb_collector.py在collectors/目录下新建usb_collector.py继承CollectorBase已在collector_base.py定义# collectors/usb_collector.py import xml.etree.ElementTree as ET from datetime import datetime, timezone from collector_base import CollectorBase class UsbCollector(CollectorBase): def __init__(self, log_pathrC:\Windows\System32\winevt\Logs\System.evtx): self.log_path log_path def collect(self): if not os.path.exists(self.log_path): return [] results [] # 使用 wevtutil 命令导出 XML比直接解析二进制 evtx 稳定 cmd fwevtutil qe {self.log_path} /q:*[System[(EventID20001 or EventID20003)]] /f:XML try: output subprocess.check_output(cmd, shellTrue, stderrsubprocess.STDOUT, encodingutf-8) except subprocess.CalledProcessError: return [] root ET.fromstring(output) for event in root.findall(.//Event): try: event_id int(event.find(.//EventID).text) time_created event.find(.//TimeCreated).get(SystemTime) # 解析 XML 中的 UserData/DeviceInstanceID device_id_elem event.find(.//UserData//DeviceInstanceID) device_id device_id_elem.text if device_id_elem is not None else unknown # 提取厂商/型号从 EventData 中的 Name 字段 name_elem event.find(.//EventData//Data[NameName]) device_name name_elem.text if name_elem is not None else unknown results.append({ source: usb, event_type: usb_connected if event_id 20001 else usb_disconnected, timestamp_utc: time_created.replace( , T) Z, device_id: device_id, device_name: device_name, confidence: 0.92 }) except (AttributeError, ValueError, ET.ParseError): continue return results5.2 注册到主程序让main.py认识它修改main.py在模块导入区添加from collectors.usb_collector import UsbCollector在采集器初始化列表中加入collectors [ MftCollector(), ShellBagCollector(), PrefetchCollector(), EventLogCollector(), UsbCollector(), # ← 新增这一行 ]5.3 添加命令行开关与配置项在argparse配置中增加parser.add_argument(--no-usb, actionstore_true, helpSkip USB device collection)并在调度逻辑中判断if not args.no_usb: collectors.append(UsbCollector())5.4 验证与调试三步确认模块生效快速验证插入 U 盘运行python main.py --no-mft --no-prefetch --output json | jq .[] | select(.sourceusb)应看到类似{ source: usb, event_type: usb_connected, timestamp_utc: 2024-05-20T14:22:33.123Z, device_id: USB\\VID_0781PID_5581\\..., device_name: SanDisk Ultra Fit }时间精度验证对比 Windows 事件查看器中同一事件的时间戳确认毫秒级一致离线验证将System.evtx复制到另一台机器修改UsbCollector.__init__()中的log_path指向该文件确保离线分析能力。从那以后我每次新增采集器都强制走一遍“命令行开关 → 单模块测试 → 时间对齐 → 离线验证”四步闭环。不是为了炫技而是因为痕迹数据一旦出错下游的审计结论就是空中楼阁。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站