xbqt/forefst
GitHub: xbqt/forefst
首个开源的 ReFS 3.4–3.14 数字取证分析套件,提供文件列表、时间线构建、事务日志解析、已删除文件恢复及磁盘结构文档,填补了 ReFS 取证工具链的长期空白。
Stars: 1 | Forks: 0
# forefst — ReFS 取证分析
**针对 Microsoft 弹性文件系统 (ReFS) 3.4 至 3.14 版本的取证工具及字节级结构文档。**
ReFS 是 Microsoft 推出的现代自愈文件系统——它是 Storage Spaces 和 Dev Drives 的默认文件系统,并在 Windows Server 和 Windows 11 上日益普及。然而,公开的取证文档实际上一直停留在 ReFS 3.4 版本,这是由 Paul Prade 等人在《*Forensic analysis of the resilient file system (ReFS) version 3.4*》(2019) 中逆向工程的版本,并且至今仍没有与成熟的 NTFS 工具链相媲美的 ReFS 替代品。本项目旨在填补这一空白:它将磁盘分析推进到了当前的 3.14 版本,并提供了两个零依赖读取原始卷的工具。
- **[forefst.py](forefst.py)** — 取证工具:一个文件列表查看器及适用于 ReFS 卷的完整取证套件。
- **[refsanalysis.py](refsanalysis.py)** — 分析工具:每次解码一个磁盘结构,用于学习该格式并针对新版本验证取证工具。
- 每个在此过程中解码的磁盘格式的 **[结构文档](docs/)**,也已作为可浏览的网站发布在 **[xbpt.gitlab.io/forefst](https://xbpt.gitlab.io/forefst/)**。
- 用于构建受控 ReFS 测试镜像并重新验证每一项结论的 **[实验与验证材料](analysis/)**。
所有 3.4–3.14 版本均可解析;部分扩展字段依赖于具体版本,其中在 3.10+ 和 3.14 版本上覆盖最全。要求为 Python 3.7+,仅使用标准库——克隆后即可运行。
为什么叫 *forefst*?因为它源自 *forensic*(取证)和 Re*FS*。同时也因为 ReFS 的核心全是 B+-tree。最后,因为我认为,地球上的森林(forest)越多越好。
该项目最初是为我的硕士论文(《*Forensic analysis of the Resilient File System (ReFS) version 3.14*》,蒙斯大学,2026 年)构建的,现在其影响范围已远远超出了最初的学术领域。这是它的首次公开发布。
希望它能对您有所帮助——欢迎随时通过提交 issue 提供反馈或修正。
## 此处的新颖之处
**首先,是工具本身。** ReFS 已经有十四年的历史,而且——据我所知——从来就没有过一个真正开源的 ReFS 取证工具。我所了解的开源工具(libfsrefs、pyrefs、Prade 等人工作的 Sleuth Kit 扩展,以及像 ARIN 这样的日志解析器)每个都只能覆盖其中一部分——要么是早期版本、单个痕迹,要么是停留在 3.4 版本的格式——而所有能够打开*当前*版本卷的工具都是闭源商业软件,或者是你无法审计的文件列表。因此,据我所知,forefst 是第一个任何人都可以下载、逐行阅读、并直接指向现代 ReFS 卷以完成完整取证工作的工具——包括列表、时间线、两种日志、已删除文件、先前版本、内容提取和安全描述符。而 refsanalysis 是其前辈们未曾提供的伴侣工具:这是一个结构级别的实验室,当 Microsoft 再次更改格式时,它能保持一切皆可重新测试——这也是早期工作往往落后的原因。
**其次,是相关知识。** 关于该格式最后一份公开的映射是 3.4 分析版本,而 ReFS 自那以后已经发展了很长一段路:页引用从 104 字节缩小到了 48 字节(使用 SHA-256 时为 72 字节),v3.14 通过间接列表访问其十三个根节点,一个原生格式标记和一个版本回显现在记录了卷自身的历史,重做操作码集从 29 增长到了 44,`$SI` 增加了八个字节,而一些完整的功能——流快照、Dev Drive——都是在现有文献之后才出现的。这里的文档将字节级记录从 3.4 延续到了 3.14,并在过程中重新审视了 3.4 时代的基础:在 2019 年只能被部分解码的 Container Table 行现在已被完全映射,早期工作搁置的检查点比对现已实现(得出了一个坦诚的负面结果:在干净卸载后,两个检查点解码出相同的树),并且属性集——在文献中几乎只是草稿——现在每一项都有了一页字节级的文档。所有这些都存在于包含 432 项结论并附有分级证据的结论登记册中。
**还有一些功能即使在成熟的 NTFS 工具链中也无出其右**——正是这些功能让 forefst 成为一个*取证*工具,而不仅仅是一个解析器:
- **从事务日志中重建用户操作。** `mlog --parse` 将持久日志解码为具体的操作——CREATE、WRITE、RENAME、MOVE、DELETE——并使用了特意保持保守的语义:通过比较父目录 OID 来区分 RENAME 和 MOVE,并且仅当对象自身的表被销毁时才报告 DELETE,而不是在重命名也会产生的纯粹行移除时就报告。开源 NTFS 领域从未有过维护良好的 `$LogFile` 解析器;这是 ReFS 中最接近它的工具。
- **NTFS 无法提供的时间戳篡改(timestomp)信号。** ReFS 不保留 `$FILE_NAME` 时间戳副本——但是硬链接文件*每个名称*都携带一组 `$SI`,因此日期倒退的名称在其同级项和变更日志中会显得很突兀,从而独立于日志并精准地标示出被篡改的名称。
- **无法被抹除的删除证据。** 对象 ID(OID)是单调递增且永不重用的,因此 OID 序列中的缺口就是某个对象曾经存在并被删除的持久证据——即使该对象曾涉及的每一个字节都被完全覆盖,该证据依然存在。需要注意的是:小的 *resident* 文件没有自己的对象 ID(它们内联存在于其父目录中),因此一连串被删除的常驻文件可能只会留下很小的缺口,或者根本没有缺口——这种信号对于目录和较大对象最为强烈。即便如此,NTFS 也根本无法提供这种功能:MFT 记录会被循环使用,久而久之就会抹除自身的证据。
- **介质本身的溯源。** `summary` 根据磁盘上的标记(升级操作永远无法设置该标记)将卷分类为原始卷、升级卷或原生卷——这是卷历史的永久签名,并会产生实际的功能影响(POSIX unlink 和硬链接需要原生格式)。
- **一个会公开自身不确定性的解析器。** `--provenance` 会标出那些基于结构推断,而不是基于在反编译的驱动程序和磁盘字节中都确认的事实的输出字段。每个发出的字段都可以追溯到结论登记册中的分级证据——因此对于“*你怎么知道这一列是对的?*”这个问题,有现成的书面答案。
每项内容都在 [docs/concepts/](docs/concepts/) 下有深入的文档说明。
## 快速开始
唯一的要求是 **Python 3.7+**——仅使用标准库:无需 `pip install`,无依赖,无需构建。在 Linux、macOS 或 Windows 上克隆并运行。这两个工具都严格以**只读**方式打开输入,因此将它们指向证据是安全的(唯一具备写能力的操作 `refsanalysis.py bootedit repair`,默认处理稀疏*副本*,并且除非你主动要求,否则会拒绝 `--inplace`)。
```
# forefst 能做的一切,每行一条
python3 forefst.py --list
# 完整的 forensic 文件列表 -> 38 列 CSV
python3 forefst.py disk.raw -o files.csv
# 单行 volume 概览(版本、大小、计数、upgrade 状态)
python3 forefst.py disk.raw summary
# 将 durable transaction log 解码为具体的文件操作
python3 forefst.py disk.raw mlog --parse
```
输入可以是原始 ReFS 镜像(`dd` / `.raw`)、原始磁盘或分区设备,或者是导出为原始格式的 E01(`ewfexport disk.E01`,或使用 `xmount --in ewf --out raw` 挂载)——forefst 会自动在全盘镜像中查找 ReFS 分区。读取活动设备(`/dev/sdX`、`\\.\PhysicalDriveN`)需要 root / 管理员权限;镜像文件则不需要。每个子命令都有详细的帮助说明:`python3 forefst.py help `。
### 两分钟快速尝试
示例镜像属于 **Git LFS** 对象。最快的入门方法是直接下载下面使用的那个镜像——无需克隆:
```
curl -L -O https://github.com/xbqt/forefst/raw/main/analysis/samples/disks/win11refs2tsnapshots/win11refs2tsnapshots.raw.zst
zstd -d win11refs2tsnapshots.raw.zst
python3 forefst.py win11refs2tsnapshots.raw summary # volume overview
python3 forefst.py win11refs2tsnapshots.raw -o files.csv # full file listing
python3 forefst.py win11refs2tsnapshots.raw snapshots # this image showcases CoW stream snapshots
```
如果你已经克隆了代码库,可以通过 `git lfs install && git lfs pull` 获取所有的示例镜像。请参阅 [`analysis/samples/README.md`](analysis/samples/README.md) 了解其他镜像(每个都是 Git LFS 对象)及其演示内容。
## 给 NTFS 分析师的建议
首先要提到的一个结构差异,因为它重塑了整个工作流:**没有什么需要提取的东西。** NTFS 分析通常意味着从镜像中提取出一个文件——比如 `$MFT`、`$UsnJrnl:$J`、`$Boot`、`$SDS`——并将其喂给解析器(这正是像 MFTECmd 这样的工具所填补的细分领域)。ReFS 没有类似 `$MFT` 的单一文件;它的元数据分布在挂载于检查点根表上的 Minstore B+-tree 中。因此,你不是在提取某个痕迹,而是将 forefst 指向原始镜像(或设备),由它自行引导加载该卷。下面的每一个命令读取的都是整个卷,而不是某个切分出来的文件。
| 为了…… | NTFS(典型工作流) | ReFS (forefst) |
|---|---|---|
| 将每个文件的元数据导出为 → CSV / body / JSON | `$MFT` 解析器 | `files` — 38 列,可直接用于 Timeline Explorer |
| 变更日志 | `$UsnJrnl:$J`(需要 `$MFT` 提供路径) | `usn` — 名称和 FileID 直接从卷本身进行解析 |
| 事务日志 → 用户操作 | `$LogFile`(无维护中的开源解析器) | `mlog --parse` → CREATE / WRITE / RENAME / MOVE / DELETE |
| 时间戳篡改检测 | `$SI` 对比 `$FN`,亚秒级为零 | `timestomp` — USN 佐证 + 硬链接 `$SI` 差异(ReFS 没有 `$FILE_NAME` 副本) |
| 安全描述符 | `$SDS` 解析器 | `security`(外加 `--audit` 篡改检查) |
| 回收站 | `$I` / `$R` 解析器 | `recyclebin` |
| 已删除文件 | TSK / Carving | `deleted` → `export deleted [--carve]`,提供针对每个条目的可恢复性判定 |
| 提取内容 / ADS | TSK `icat` | `extract`, `export ads` |
| 先前版本 | VSS 工具 | `snapshots` — CoW 流快照 |
| 超级时间线 | 在 Timeline Explorer / mactime / Plaso 中组装 | `timeline` — USN + MLog + `$SI` MACB,合并处理 |
| 卷分类排查 | fsstat / `$Boot` | `summary` — 包括原始 / 升级 / 原生状态,`--hash-image` |
**有些东西会让你感到熟悉——但有一件则不然:**
- `Created`、`Modified`、`Changed` 和 `Accessed` 属于 `0x10` (`$SI`) 集合。特意没有设置 `0x30` 集合:ReFS 不保留 `$FILE_NAME` 时间戳副本,因此时间戳篡改可以通过[其他信号](docs/concepts/timestomp_detection.md)被捕获。
- `files` 和 `usn` 的输出可以在 `FileId` + `HomeOid`——也就是 USN 128 位 FileID——上进行**关联**,这样你就可以直接从文件行深入查看其变更日志历史。
- 这些 CSV 文件可以直接在 Timeline Explorer 中打开;`--body` 用于输入给 mactime。
- 这是纯 Python 编写的,而不是编译过的 C#,所以请预期会用一些原始速度来换取可移植性;`timeline --fast` 和 `-q` 是用于快速分类排查的利器。
深入了解:[NTFS 对比 ReFS](docs/concepts/ntfs_comparison.md) · [工具与痕迹映射](docs/concepts/tool_artifact_map.md)。
## 仓库结构
```
forefst/
├── forefst.py # forensic file lister + full forensic suite
├── refsanalysis.py # structure / lab tool — decode one on-disk structure at a time
├── docs/ # standalone ReFS structural reference
│ ├── structures/ # 25 byte-level on-disk layouts
│ ├── concepts/ # 33 forensic concepts & mechanisms
│ ├── attributes/ # 11 per-attribute pages
│ ├── examples/ # 5 worked walkthroughs (real tool output)
│ ├── tools/ # tool usage documentation
│ ├── website/ # Hugo site generator — publishes this reference as a static site
│ ├── methodology.md # how every claim was verified
│ └── KNOWLEDGE_MAP.md # topic -> authoritative-source index
└── analysis/ # lab materials + verification harness (the tools don't depend on it)
├── reference_table.csv # the live claim register (432 findings)
├── lab/ # VM setup, disk generation, activity generator + baseline
├── samples/ # captured tool output + samples/corpus/ + sample disks
└── reports/ # verification scripts, results, per-claim audit/ harness
```
`docs/` 目录树是一个独立的 ReFS 参考手册——可以从 **[docs/README.md](docs/README.md)** 或主题索引 **[docs/KNOWLEDGE_MAP.md](docs/KNOWLEDGE_MAP.md)** 开始。它涵盖了磁盘上的 **[结构](docs/structures/)**(VBR、超级块、检查点、B+-tree 页面、13 个系统表、目录条目)、取证相关的 **[概念](docs/concepts/)**(写时复制、删除恢复、版本检测、WSL 元数据等)、**[属性](docs/attributes/)**(`$STANDARD_INFORMATION`、`$DATA`、`$EA`、`$REPARSE_POINT`、`$SNAPSHOT` 等),以及包含实际工具输出的 **[实践示例](docs/examples/)**。
相同的参考内容也被发布为静态网站,由 **[docs/website/](docs/website/)** Hugo 站点根据 `docs/` 生成(`docs/website/README.md` 包含构建 + 部署的详细信息)。仓库根目录下的 `.gitlab-ci.yml` 和 `.github/workflows/` 会在每次发生更改时重新构建并发布它。
## forefst.py — 取证工具
`forefst.py [options]`。默认的子命令是 `files`;其余部分构成了一个完整的 ReFS 取证工具包:
| 你想要…… | 命令 |
|--------------|---------|
| 列出所有文件及其元数据 (CSV / JSON / body) | `forefst.py disk.raw -o files.csv` |
| 恢复已删除的文件(先查看,然后再写出) | `forefst.py disk.raw deleted` → `forefst.py disk.raw export deleted ./recovered` |
| 构建超级时间线 (USN + MLog + `$SI` MACB) | `forefst.py disk.raw timeline --csv` |
| 解析 USN 变更日志 | `forefst.py disk.raw usn --csv usn.csv` |
| 解析 MLog 持久日志 (重做记录) | `forefst.py disk.raw mlog --stats` |
| 恢复 CoW 先前版本 (流快照) | `forefst.py disk.raw snapshots --extract ./versions` |
| 标记时间戳篡改 (timestomping) | `forefst.py disk.raw timestomp --min HIGH` |
| 映射所有者 / ACL,或进行 `$Secure` 篡改检查 | `forefst.py disk.raw security --files` · `security --audit` |
| 查找所有特殊文件 (ADS、重解析、WSL、硬链接、稀疏、EFS、压缩、完整性、EA) | `forefst.py disk.raw specials` |
| 解析符号链接 / 联接点 / WSL 重解析点 | `forefst.py disk.raw reparse -v` |
| 解码 `$RECYCLE.BIN`(`$I` 元数据 + `$R` 有效载荷) | `forefst.py disk.raw recyclebin` |
| 提取单个文件的内容,或转储其所有属性 | `forefst.py disk.raw extract /path` · `details /path` |
| 查看哪些输出字段**不是** 100% 确定的 | `forefst.py disk.raw --provenance` |
### 示例 — 已删除文件的恢复
```
$ python3 forefst.py disk.raw deleted
── B+-tree Node Slack Scan ──
(ReFS deletion removes only the row's index slot; the row body persists)
FILE Change Journal (resident, live-slack @ cluster 13824)
Deleted from: FS Metadata
Recoverable: metadata only (non-resident — file data is not in this remnant)
```
ReFS 的删除恢复具有**五种方法**——Trash 表、检查点差异、孤立页扫描、流快照重建以及 B+-tree 节点空闲空间扫描。`deleted` 命令默认运行其中三种(Trash 表、检查点差异、节点空闲空间);`--scan-pages` 会增加孤立页扫描,而流快照重建——这是唯一一条能够获取确切内容的路径——被单独作为 `snapshots` 暴露出来,因为它还能恢复仍然存在的文件的先前版本。每个恢复的条目都会带有**可恢复性判定**标记:*完整文件*(常驻内容在记录中为内联形式)、*有区段支持的(extent-backed)*(extent map 仍然存在的非常驻数据,因此 `export deleted --carve` 可以将其还原),或者*仅限元数据*。`export deleted DIR` 会将可恢复的文件写出。
## refsanalysis.py — 分析工具
如果说 `forefst.py` 回答的是“*这个卷上发生了什么?*”,那么 `refsanalysis.py` 回答的则是“*这个结构看起来是什么样的?*”——它每次解码一个磁盘结构。它是用于学习该格式、验证取证工具以及适应新的 ReFS 版本的得力助手。
| 类别 | 子命令 |
|----------|-------------|
| 快速分析 | `summary`, `summary++`, `all` |
| 文件系统内容 | `files`, `attributes`, `details` |
| 结构 | `boot`, `supb`, `chkp`, `objects`, `schema`, `parentchild`, `containers`, `upcase`, `oid30` |
| 引导扇区修复 | `bootedit`(VBR 检查 / 导出 / 修复) |
运行 `python3 refsanalysis.py --list` 以获取完整的命令集及每个命令的选项。
## 构建方式
forefst 及其文档的存在是为了使 ReFS 分析具备*法庭科学可靠性(forensically sound)*——这是我硕士论文的核心目标:为分析师提供一个可审计的开源工具,以及足以理解它所读取内容的详尽文档。refsanalysis 和实验程序是保证其持续可靠的保障;ReFS 发展迅速,因此工具和知识都必须能够针对每一个新版本进行重新测试。
关于它是如何构建的一点说明:这些代码是在大量 LLM 辅助下编写的——我是一名安全工程师和取证分析师,而不是专业的开发人员。让它值得信赖的不是它的生成方式,而是它的验证方式。这些工具背后的每一项结构性结论,都必须在两个独立的地方成立——反编译的 `refs.sys` 驱动程序和一个包含 110 多个镜像的实验库——然后才能被纳入[结论登记册](analysis/reference_table.csv)中,并且这些工具会针对整个语料库进行回归测试。它肯定不是完美无缺的,但它发出的每一个事实都可以追溯到你可以自己重新审查的证据。该方法详见下文及 [docs/methodology.md](docs/methodology.md)。非常欢迎提供反馈和错误报告。
## 参考资料
在这些工具的背后是对 ReFS 磁盘上格式的全面逆向工程——这是关于 3.14 版本最完整的公开记录。以下是各项重点内容,每一项都是深入了解文档的起点:
- **[磁盘结构](docs/structures/README.md)** — 引导链(VBR → 超级块 → 检查点)以及将每个对象映射到其具体字节的 13 个系统表(对象、容器、Schema、分配器等)。
- **[`$STANDARD_INFORMATION`](docs/attributes/STANDARD_INFORMATION.md)** — 每个文件携带的时间戳、`SecurityId` 和 USN 链接:这是 MACB 时间线以及[时间戳篡改检测](docs/concepts/timestomp_detection.md)的基础。
- **[写时复制与删除恢复](docs/concepts/deletion_recovery.md)** — 为什么 ReFS 从不就地覆盖元数据,以及在遭遇删除、格式化或升级后[到底有哪些内容能存活下来](docs/concepts/what_survives.md)。
- **[变更历史](docs/structures/usn_journal.md)** — 根据 USN 日志和 [MLog](docs/structures/mlog.md) 持久事务日志重建活动。
- **[版本检测](docs/concepts/version_detection.md)** — 区分原生 v3.14 卷与升级版卷,以及三种在取证上截然不同的卷状态。
完整的索引——包括每种结构、属性和概念——都位于 **[docs/](docs/README.md)** 中。
**验证方式。** 每一项结论都存在于一个实时登记册中——即 [`analysis/reference_table.csv`](analysis/reference_table.csv),**涵盖了 ReFS 3.4–3.14 的 432 项结构性结论**——并带有**证据级别**标记:**E1**(二进制字符串文字)、**E2**(PDB 符号 / 反编译代码)、**E3**(结构推断)、**RD**(从 110 多个卷的原始磁盘库中解析得出,并与 4 个 `refs.sys` 构建版本进行反编译比对)。只有当反编译的驱动程序和原始磁盘一致同意(`E2+RD`)时,一项字节级的结论才会被接受。具体方法、证据模型以及一个操作示例位于 **[docs/methodology.md](docs/methodology.md)** 中;复现它所需的一切都已随 `analysis/`(`lab/`、`samples/`、`reports/`)发布——你可以通过 `REFS_DISKS` / `REFS_CORPUS` 将脚本指向你自己的语料库。
## 许可证
由 Baptiste Bonnet 编写,并在 GNU General Public License v3.0 或更高版本下发布——详见 [LICENSE](LICENSE)。
标签:HTTP工具, Python, ReFS, 子域名变形, 提权工具, 数字取证, 数据恢复, 文件系统, 无后门, 网络安全审计, 自动化脚本, 逆向工具