用 USN 日志找回已删除文件的证据

当一个文件已被删除、从回收站清空,且 MFT 表项已被回收 — USN 日志通常仍然记着它的名字、父级和时间线。如何把这些证据取出来。

作者: 阅读约 2 分钟

非技术利益相关者最常向取证分析师提出的问题,几乎都是"你能证明这个文件存在过吗?"的某种变体。当文件已从磁盘删除、回收站 被清空,且时间长到 $MFT 表项已经被复用,答案几乎总取决于 USN 日志还记得什么。以我的经验,当其他东西都被抹去之后,日志是"这文件曾经存在过吗"问题最耐久的证人。

本文走一遍日志在文件被删除后保留了什么、如何把证据取出来,以及日志成为你最后一道防线的情形。

删除之后日志保留什么

在 NTFS 上删除一个文件时,会发生三件事:

  1. 父目录中的 $FILE_NAME 条目被解开。该目录的 INDEX_ALLOCATION 在没有它的情况下被重写。
  2. MFT 表项被标记为未使用,但不会清零。下一个分配到该表项的文件会覆盖其 data runs、属性和时间戳。
  3. USN 日志获得一条 FileDelete | Close 记录,记下文件、其父引用、其 MFT 表项号与序列号,以及删除时间戳。

三者中,日志记录最具韧性。目录条目在父级被重写的那一刻消失 — 在繁忙的卷上可能只是几秒之内。MFT 表项一旦有新文件被创建就可能被复用 — 几分钟之内就可能。而日志记录坐在一个环形缓冲区里,几天到几周才转一圈。

对于几天前被删除的文件,日志通常给你:

  • 作为 UTF-16 LE 末端叶子的文件名。只是叶子,没有路径。
  • 父目录的 MFT 引用号。配合解析过的 $MFT 可解析为路径。
  • FILETIME UTC 形式的删除时间戳
  • 文件占据的 MFT 表项号与序列号。便于与雕刻出的痕迹和 $LogFile 的 before-image 做交叉参照。
  • 一段生命周期轨迹。同一 FileReferenceNumber 上的 FileCreateDataExtendBasicInfoChange 和重命名记录告诉你文件何时被创建、如何被写入,以及在被删除前其时间戳是否曾被修改。

一个四元组 — MFT 引用、文件名、父引用、删除 FILETIME — 加上同一份日志中更早的一次创建事件,通常就足以满足法律保全或锚定更深入的调查。多数时候你还会从同一份 $J 拿到 FileCreate 记录,从而独立于 $STANDARD_INFORMATION 得到文件年龄和创建时间。

最小恢复工作流

带着一份解析过的日志和一份解析过的 $MFT(两者的 提取指南):

  1. 在感兴趣的时间窗内FileDelete 过滤。按时间戳排序。这就是你要分诊的删除主清单。
  2. 对任何重要的删除,FileReferenceNumber 分组。你能得到文件的完整历史:创建、写入、重命名对、属性变更,以及最终的删除。
  3. 通过 $MFT 解析父路径。本页的解析器在同时提供两个文件时会自动完成。没有 $MFT 时,你停在末端文件名。
  4. 若删除非常近期,交叉检查 $LogFile。它可能仍包含目录条目的"before"镜像,作为文件存在的第二个独立证人。

"MFT 引用、文件名、父路径、创建时间戳、删除时间戳、完整生命周期轨迹"这一组合,通常足以写出一份经得起辩护的报告。

当父路径无法解析时

有时你查看时父 MFT 表项本身已被回收 — 在文件系统繁忙的主机上很常见,特别是文件服务器和 CI runner。日志仍给你文件名和父引用号,但 $MFT 不再知道那个引用过去指向哪个文件夹。

通常有三个回退能把路径救回来:

同一父引用上更早的 RenameNewName 记录。 把所有日志记录按父引用分组。任何更早的 RenameNewName,其新父正好与你未解析的引用匹配,而它自己的路径能解析,就告诉你该引用过去对应的是哪个文件夹。

$LogFile 的索引条目。 崩溃恢复日志保留 INDEX_ALLOCATION 更新的 before-image。Joachim Metz 的 libfsntfs 和 Jonas Schicht 的 LogFileParser 能把它们抽出来。对足够近期的删除,目录条目的 before-image 还在 $LogFile 中,并能解析为路径。

同一父引用下的兄弟文件。 把所有日志记录按父引用分组。如果有兄弟被重命名进入一个 MFT 表项仍然有效的目录,就借此转视角:重命名时刻的旧父就是你未知的文件夹。

当上述方法都无效时,解析器会把文件呈现为"filename: notes.docx, parent: 1234-5",不带已解析路径。这是有意的。捏造一个无法验证的路径,比承认它缺失更糟糕。

"已抹除"不等于"消失"

文件擦除工具 — CCleaner 的安全删除、Eraser、BleachBit、sdelete — 会先覆盖文件内容再删除它。它们在日志中留下的是更吵的轨迹,而不是更安静:

  • 在文件 FileDelete | Close 之前出现多条 DataOverwrite | Close 记录,表明刻意进行的内容覆盖。正常删除不会产生覆盖。
  • 同一文件几秒内出现 FileCreateDataOverwriteFileDelete,就是经典的证据销毁签名。
  • 如果擦除器还通过先写后删填充文件来抹除slack 空间,这些填充文件也有自己的创建/扩展/覆盖/删除序列 — 销毁瞬间附近的一群记录。

MITRE ATT&CK 的映射:内容覆盖是 T1485 Data Destruction,删除本身是 T1070.004 File Deletion。在你怀疑刻意销毁证据的主机上,日志通常能给你比任何其他痕迹更紧的时间戳。

文件内容怎么办

USN 日志从不携带内容。只有关于操作的元数据。如果你需要把字节找回,日志会告诉你去哪儿找:

  • 被删文件的 MFT 表项号。如果该表项尚未被复用,$MFT 仍保有 data runs,内容或许可经 The Sleuth Kit 的 icat 或 X-Ways 恢复。
  • 对非常近期的删除,$LogFile 的 before-image
  • 如果主机存在卷影副本 (VSS)。被删文件可能存在于之前的快照中。在活动主机上用 vssadmin list shadows,或在镜像上用 libyal 的 vshadowmount 都能把它们浮上来。
  • 应用特定的存储。OneDrive 的线上回收站、Office 在 \Users\<u>\AppData\Roaming\Microsoft\Office\UnsavedFiles\ 下的"自动保存"文件夹、浏览器缓存(浏览器取证工具 覆盖)、Outlook OST/PST 的暂存副本。
  • 如果文件足够近时还处于打开状态,页面文件RAM 转储

日志的角色是告诉你某个文件曾经存在过,以及它何时被删除。内容恢复是下游的事。

实用上的限制

环形缓冲区窗口。 在日志回卷过你能看到的最旧记录之前删除的文件,在这份痕迹里已经消失。补上 $LogFile 和影子副本。

不打日志的操作。 只更新属性的文件系统级加密变更、只触及分配位图的稀疏区域写入,以及某些 NTFS reparse-point 操作,可能不会产生 DataExtendDataOverwrite 记录。日志只看到 NTFS 选择记录的东西。

没有先行 FileCreateFileDelete 意味着文件是在当前日志最旧记录之前创建的。它在 $J 中的生命周期不完整,但删除时间戳和父引用仍然能锚定一份报告。

绑定挂载或卷挂载的共享。 如果数据驻留在远程文件系统上,你主机上的日志只知道本地句柄。请从真正承载它的文件系统取日志。

延伸阅读

  • Microsoft Learn — Change Journals 覆盖产生 FileDelete 记录的 API 表面和生命周期。
  • The Sleuth Kit 手册页 — 用于对仍处于已分配状态的 MFT 表项做内容恢复的 icatfls
  • SANS DFIR — Windows Forensic Analysis 海报 把日志放在与 $LogFile、卷影副本和其他删除恢复痕迹的语境里,一页搞定。
  • Joachim Metz 的 libfsntfs — 当删除还很新、目录条目尚未在别处被覆盖时,提取 $LogFile before-image 的开放参考。