SecurityRonin/ntfs-forensic

GitHub: SecurityRonin/ntfs-forensic

从零实现的 NTFS 取证读取器与分级异常审计器,能在不可信磁盘镜像上检测时间戳篡改、已删除记录、MFT 篡改等取证异常并从 USN 日志重构已删除文件的完整路径。

Stars: 0 | Forks: 0

# ntfs-forensic [![ntfs-core](https://img.shields.io/crates/v/ntfs-core.svg?label=ntfs-core)](https://crates.io/crates/ntfs-core) [![ntfs-forensic](https://img.shields.io/crates/v/ntfs-forensic.svg?label=ntfs-forensic)](https://crates.io/crates/ntfs-forensic) [![Docs.rs](https://img.shields.io/docsrs/ntfs-forensic)](https://docs.rs/ntfs-forensic) [![License: Apache-2.0](https://img.shields.io/badge/License-Apache--2.0-blue.svg)](LICENSE) [![CI](https://static.pigsec.cn/wp-content/uploads/repos/2026/06/867152ecc1025401.svg)](https://github.com/SecurityRonin/ntfs-forensic/actions) [![Sponsor](https://img.shields.io/badge/sponsor-h4x0r-ea4aaa?logo=github-sponsors)](https://github.com/sponsors/h4x0r) **一个从零开始构建的 NTFS 读取器和分级异常审计器 —— 从 `$UsnJrnl:$J` 更改日志中重构完整的文件路径(即使对于已删除、MFT 复用的文件),并呈现“干净”的文件系统驱动程序旨在隐藏的时间戳篡改、备用数据流、已删除记录和 MFT 残留空间。** 一个工作区,两个 crate: - **[`ntfs-core`](https://crates.io/crates/ntfs-core)** —— 读取器:`$MFT`、属性、索引、数据运行、LZNT1、`$UsnJrnl:$J` 更改日志记录解码,以及在任何 `Read + Seek` 源上进行 `NtfsFs` 路径导航。不使用 `unsafe`,没有 C 绑定。 - **[`ntfs-forensic`](https://crates.io/crates/ntfs-forensic)** —— 审计器:将解析后的 MFT 记录转换为严重性分级的 [`forensicnomicon::report::Finding`](https://crates.io/crates/forensicnomicon),以便 NTFS 卷的异常能与分区层和容器层统一汇总。 ## 在 30 秒内审计原始 MFT 记录 ``` [dependencies] ntfs-forensic = "0.5" # pulls in ntfs-core ``` ``` use ntfs_forensic::audit_record; use forensicnomicon::report::Source; let src = Source { analyzer: "ntfs-forensic".into(), scope: "NTFS".into(), version: None }; // Feed it a single raw 1024-byte MFT record; get back graded anomalies. for anomaly in audit_record(&mft_record_bytes) { let finding = anomaly.to_finding(src.clone()); println!("[{:?}] {} — {}", finding.severity, finding.code, finding.note); // e.g. [Some(High)] NTFS-TIMESTOMP — $SI created before $FN … } ``` `audit_record` 解析头部和属性,提取 `$STANDARD_INFORMATION`/`$FILE_NAME`,并对其发现进行评级。头部未能解析的记录不会产生异常(结构性损坏由读取器/雕刻器呈现,绝不会引发 panic)。 ## 异常代码 每个异常都是一个**观察结果**(“与……一致”);由检查人员得出结论。代码是稳定、公开的契约。 | 代码 | 严重性 | 观察内容 | |---|---|---| | `NTFS-TIMESTOMP` | 高 | `$STANDARD_INFORMATION` 时间显示与更难伪造的 `$FILE_NAME` 时间相比存在伪造痕迹(`$SI` 早于 `$FN`,或精确到整秒) | | `NTFS-ADS` | 低 | 命名的 `$DATA` 属性 —— 备用数据流(也有良性用途,例如 `Zone.Identifier`) | | `NTFS-SLACK-RESIDUE` | 低 | MFT 记录中超出其已用大小的残留空间中存在非零残留数据 | | `NTFS-DELETED-RECORD` | 信息 | 未被使用的 MFT 记录 —— 可恢复的已删除文件 | | `NTFS-MFTMIRR-MISMATCH` | 高 | `$MFT` 中的系统记录与其 `$MFTMirr` 副本不同 | | `NTFS-LOGFILE-CLEARED` | 中 | `$LogFile` 显示的重启区域间隙与日志被清除的情况一致 | 每条记录的异常来自 `audit_record` / `audit_components`;卷级别的配对(`NTFS-MFTMIRR-MISMATCH`,`NTFS-LOGFILE-CLEARED`)来自 `audit_mft_mirror($MFT, $MFTMirr)` 和 `audit_logfile($LogFile)`。 ## 读取器:导航卷 `NtfsFs`(在 `ntfs-core` 中,作为 `ntfs_core` 导入)从任何 `Read + Seek` 源读取文件和目录: ``` use ntfs_core::NtfsFs; use std::fs::File; let mut fs = NtfsFs::open(File::open("ntfs.img")?)?; // Read a file by path… let hosts = fs.read_file(r"\Windows\System32\drivers\etc\hosts")?; // …or list the root directory (MFT record 5). let root = fs.read_record(5)?; for entry in fs.directory_entries(&root)? { if let Some(name) = entry.file_name { println!("{}", name.name); } } # Ok::<(), ntfs_core::NtfsError>(()) ``` crates.io 上名为 `ntfs` 的原始 crate 是 Colin Finck 的通用读取器,因此此 crate 以 `ntfs-core` 发布并作为 `ntfs_core` 导入。 ### 在整个磁盘内打开分区 `OffsetReader` 将分区重新基定到偏移量 0,并且**在结构上无法读取超出分区边界的内容** —— 将来自 [`mbr-forensic`](https://github.com/SecurityRonin/mbr-forensic) / [`gpt-partition-forensic`](https://github.com/SecurityRonin/gpt-partition-forensic) 的偏移量和长度提供给它: ``` use ntfs_core::{NtfsFs, OffsetReader}; use std::fs::File; let part = OffsetReader::new(File::open("disk.img")?, 1_048_576, 500_000_000)?; let mut fs = NtfsFs::open(part)?; # Ok::<(), ntfs_core::NtfsError>(()) ``` ## 这与通用 NTFS crate 有何不同 大多数 NTFS crate 只回答一个问题:“这个卷上有什么文件?”而此工作区回答的是数字取证检查人员真正需要的问题: | 功能 | 通用 NTFS crate | 此工作区 | |---|---|---| | MFT 记录 + 属性解析 | ✅ | ✅ | | 目录索引遍历(`$INDEX_ROOT` / INDX) | ✅ | ✅ | | 数据运行、稀疏文件、LZNT1 解压缩 | ✅ | ✅ | | `$ATTRIBUTE_LIST`(严重碎片化的文件) | 部分 | ✅ | | `$SI`-与-`$FN` 时间戳篡改检测 | ✗ | ✅ | | 备用数据流枚举 | ✗ | ✅ | | 已删除记录雕刻(未分配的 `FILE`/`BAAD`) | ✗ | ✅ | | MFT 记录残留空间提取 | ✗ | ✅ | | `$MFTMirr` / `$LogFile` 篡改检查 | ✗ | ✅ | | 更新序列(修复)撕裂写入检测 | ✗ | ✅ | | `$UsnJrnl:$J` 更改日志记录解码(创建 / 删除 / 重命名 / 覆盖历史) | ✗ | ✅ | | **`$UsnJrnl:$J` 完整路径重构**(*Rewind* 算法 —— 即使对于已删除 + MFT 复用文件也能提供完整路径) | ✗ | ✅ | | USN 流式读取器 + 空闲空间 USN 记录雕刻 | ✗ | ✅ | | ReFS USN V3(128 位文件引用) | ✗ | ✅ | | 分区窗口隔离(无法读取超出卷的内容) | ✗ | ✅ | | 严重性分级的 `report::Finding` 输出 | ✗ | ✅ | | `#![forbid(unsafe_code)]` | — | ✅ | ## `$UsnJrnl:$J`:重构完整路径 —— 即使是已删除的文件 USN 更改日志记录了*什么*发生了更改以及*哪个* MFT 条目 —— 但仅限于文件**自己的名称**,而不是其路径。`ntfs-core` 通过使用 *Rewind* 算法遍历日志,重构每个日志事件的**完整路径**,包括那些已被删除且其 `$MFT` 记录后来被重用的文件: ``` use ntfs_core::mft::MftData; // Seed from the live $MFT, then rewind the $UsnJrnl:$J event stream. let mut engine = MftData::parse(&mft_bytes)?.seed_rewind(); for resolved in engine.rewind(&ntfs_core::usn::parse_usn_journal(&usn_bytes)?) { println!("{:<10?} {:<12?} {}", resolved.source, resolved.record.reason, resolved.full_path); // Allocated FILE_DELETE \Users\victim\AppData\Local\Temp\evil.exe } # Ok::<(), ntfs_core::NtfsError>(()) ``` `RewindEngine` 运行**两次遍历 —— 先反向,再正向** —— 因此在日志中途的重命名或 MFT 条目复用,在每个时间点都能解析为*正确*的路径。父目录在实时 `$MFT` 中已不存在的事件,仍然可以从日志自身的创建/重命名历史中解析,并标记为 `RecordSource::Carved` 或 `Ghost`。对于太大而无法保存在内存中的日志,`UsnJournalReader` 会对它们进行流式传输;`carve_usn_records` 从日志残留空间和未分配空间中恢复事件;而 `RefsAnalyzer` 处理 ReFS 的 128 位 USN V3 引用。 ## 读取器 API(`ntfs-core`) | 条目 | 用途 | |---|---| | `NtfsFs::open` / `read_file` / `read_record` / `directory_entries` / `resolve_path` / `read_named_stream` | 按路径或 MFT 记录号导航卷 | | `BootSector` | 卷引导记录(BPB / 扩展 BPB) | | `MftRecordHeader` / `apply_fixup` | FILE 记录和更新序列数组修复 | | `parse_attributes` / `Attribute` | 驻留和非驻留属性遍历 | | `StandardInformation` / `FileName` | 两组时间戳集 | | `decode_runlist` / `read_attribute_value` / `read_runs` | 数据运行(VCN→LCN)、稀疏 + 非驻留读取 | | `IndexRoot` / `parse_index_buffer` / `parse_entries` | 目录 B 树(`$INDEX_ROOT` / INDX) | | `parse_attribute_list` | 用于碎片化文件的扩展记录 | | `decompress` | LZNT1 解压缩 | | `carve_mft_entries` | 从原始 `$MFT` 区域雕刻 `FILE`/`BAAD` 记录 | | `compare_mft_mirror` / `parse_logfile` / `detect_journal_clearing` | `$MFTMirr` / `$LogFile` 解析原语 | | `parse_usn_record_v2` / `parse_usn_journal` / `UsnRecord` / `UsnReason` / `FileAttributes` | 解码 `$UsnJrnl:$J` 更改日志记录(V2/V3) —— 每个事件的 MFT + 父 MFT 引用、原因标志、文件名、属性和时间戳 | | `UsnJournalReader` | 在过大而无法整体加载的 `$J` 流上的流式、低内存迭代器 | | `carve_usn_records` | 从日志残留空间和未分配空间恢复 USN 记录 | | `MftData` / `MftEntry` | 高级 `$MFT` 聚合器(`$SI`/`$FN` 时间戳、ADS、路径解析);为 Rewind 引擎提供种子 | | `RewindEngine` / `ResolvedRecord` | 从 USN 日志进行**完整路径重构**(*Rewind* 算法 —— 两次遍历,支持重命名和 MFT 复用感知) | | `RefsAnalyzer` / `RefsFileId` | ReFS USN V3(128 位文件引用),仅支持日志 Rewind 的路径重构 | | `OffsetReader` | 有界分区窗口 | 审计器原语 —— `detect_timestomp`、`alternate_data_streams`、`record_slack`、`is_deleted`、`carve_file_records` —— 位于 `ntfs-forensic` 中 `audit_record` 旁边。 ## 信任,但要验证 `ntfs-forensic` 专为处理来自潜在被入侵系统的不可信磁盘映像而构建: - **`#![forbid(unsafe_code)]`** 覆盖两个 crate —— 没有 C 绑定,没有 FFI。 - **恶意输入下不引发 panic** —— 每个长度和偏移量都会根据结构声明的尺寸和实际缓冲区进行验证;该工作区在生产代码中禁用了 `clippy::unwrap_used` 和 `clippy::expect_used`。 - **模糊测试** —— 七个 `cargo-fuzz` 目标(`boot`、`record`、`attributes`、`attribute_list`、`runlist`、`index_buffer`、`compress`);一个 `fuzz.yml` CI 工作流构建并对每个目标进行冒烟运行。 - **在真实工件上验证** —— 启动解析器在真实磁盘映像上(`tests/real_image.rs`)与 The Sleuth Kit 进行了交叉验证,MFT 解析与 `mft` crate 作为独立预言机进行了交叉核对(`tests/parity_mft.rs`)。 - **在 CI 中强制执行 100% 行覆盖率**(`cargo llvm-cov --lib`,任何零命中行都会导致失败)。 ``` cargo test cargo +nightly fuzz run record # requires nightly + cargo-fuzz ``` ## 这适用的场景 `ntfs-core` 是 SecurityRonin 取证系列的 NTFS FS 层基础。完整的 `$UsnJrnl:$J` 读取器堆栈 —— 解码、流式传输、雕刻和 *Rewind* 完整路径重构 —— 位于 **`ntfs-core`** 中;[`usnjrnl-forensic`](https://github.com/SecurityRonin/usnjrnl-forensic) 现在是位于其上的一个轻量级 CLI 外壳(输出格式、实时监控),而 [`issen`](https://github.com/SecurityRonin/issen) 将此工作区作为其单一的、可审计的 NTFS 引擎使用。要获取对磁盘映像的 `Read + Seek` 并定位其中的 NTFS 分区,这些 crate 在上游组合: | Crate | 角色 | |---|---| | [`disk-forensic`](https://github.com/SecurityRonin/disk-forensic) | **编排器** —— 自动检测 MBR / GPT / APM 并输出每个分区的偏移量 / 长度 | | [`mbr-forensic`](https://github.com/SecurityRonin/mbr-forensic) | MBR 分区表 → NTFS 分区偏移量 / 长度 | | [`gpt-partition-forensic`](https://github.com/SecurityRonin/gpt-partition-forensic) | GPT 分区表 → NTFS 分区偏移量 / 长度 | | [`ewf-forensic`]() | E01 / Expert Witness Format 容器 | | [`vhdx-forensic`](https://github.com/SecurityRonin/vhdx-forensic) | VHDX 容器 | [隐私政策](https://securityronin.github.io/ntfs-forensic/privacy/) · [服务条款](https://securityronin.github.io/ntfs-forensic/terms/) · © 2026 Security Ronin Ltd
标签:NTFS, Rust, 可视化界面, 子域名变形, 数字取证, 文件系统, 网络流量审计, 自动化脚本, 通知系统