sltcnb/carvX
GitHub: sltcnb/BreadCrumb
基于特征码的纯 Python 文件提取工具,可从磁盘镜像和块设备中恢复已删除文件,无需文件系统元数据。
Stars: 0 | Forks: 0

## 演示

# carvX
基于特征码的文件提取工具,适用于磁盘镜像和块设备,其设计理念类似于
PhotoRec / Sleuth Kit。通过扫描原始字节来恢复已删除的文件——不需要
文件系统元数据,因此它适用于已格式化、已损坏或未知的
文件系统以及未分配空间。
纯 Python 3.10+ 实现,仅使用标准库。
## 安装
```
pip install -e .
# 或无需安装直接运行:
python3 -m carvx --help
```
纯 Python 3.10+ / 仅使用标准库。可选的附加依赖项可以增强特定功能:
`Pillow`(用于 `--validate` 的完整 JPEG/PNG 解码 + JPEG 双碎片重组),`pyewf`
(更稳健的 EWF/E01 处理),`pyahocorasick`(在特征码集庞大时提供更快的匹配)。
磁盘镜像格式(raw, split, EWF/E01, QCOW2, VMDK)会被自动检测。
## 用法
```
# carve 一个磁盘镜像
carvx image.dd -o recovered/
# carve 整个磁盘(raw 设备需要 root;在 macOS 上建议使用 /dev/diskN
# 而不是 /dev/rdiskN —— rdisk 需要块对齐读取)
sudo carvx /dev/disk4 -o recovered/ # macOS
sudo carvx /dev/sdb -o recovered/ # Linux
carvx \\.\PhysicalDrive1 -o recovered\ # Windows (admin shell)
carvx \\.\D: -o recovered\ # Windows, single volume
# 仅部分类型
carvx image.dd -t jpg,png,pdf,sqlite -o out/
# 扫描某个区域(例如单个分区:offset + length)
carvx /dev/disk4 --offset 209735680 --length 64G -o out/
# 通过已知的 cluster 对齐方式更快地扫描 filesystem
carvx image.dd --align 4096 -o out/
# 仅生成 inventory,不写入任何内容
carvx image.dd --dry-run
# 提速:8 个并行扫描进程(0 = 所有核心)
carvx image.dd -j 8 -o out/
# 同时输出 CSV + Sleuth Kit bodyfile;对整个源进行 hash 以备存证
carvx image.dd -o out/ --csv out/files.csv --bodyfile out/bodyfile --hash-source
# stdout 上的 JSON-lines 事件(用于封装到 GUI/pipeline 中)
carvx image.dd --machine -o out/
# 深度验证 carve(解码 JPEG/PNG/ZIP/gzip/SQLite),丢弃验证失败的
carvx image.dd --validate -o out/
carvx image.dd --drop-failed -o out/
# filesystem-metadata 恢复(恢复名称、路径、timestamps):
carvx image.dd --ntfs -o out/ # NTFS (Windows)
carvx image.dd --ext4 -o out/ # ext2/3/4 (Linux)
carvx image.dd --fat -o out/ # FAT12/16/32 + exFAT (SD/USB/cameras)
# 整个磁盘:列出 partitions,然后自动检测 FS + 恢复每一项
carvx disk.dd --list-partitions
carvx disk.dd --auto -o out/
# 列出支持的类型
carvx --list-types
```
## 模式
**Carving**(默认)—— 扫描原始字节以寻找文件签名。与文件系统无关,
从未分配空间中恢复,但只能处理连续的文件且无法保留原始名称。
`--validate` 会额外解码每个提取出的数据以确认完整性并修剪尾部。
**文件系统反删除** —— 解析文件系统元数据以获取已删除的条目,
恢复 **原始文件名、目录路径、时间戳,以及(在元数据
完好无损的情况下)碎片文件**:
| 标志 | 文件系统 | 碎片 | 备注 |
|----------|------------------------|----------------------|-------|
| `--ntfs` | NTFS | 是 (MFT runlists) | 跳过压缩/加密流 |
| `--ext4` | ext2 / ext3 / ext4 | 是 (extents + indirect blocks) | 名称取自 dir-entry slack |
| `--fat` | FAT12/16/32, exFAT | 仅首个 run | 长文件名从 VFAT/exFAT 条目中重建 |
| `--hfs` | HFS+ / HFSX | 是 (extent records) | 存活的文件始终恢复;仅当目录记录在日志中保留时才能恢复已删除的文件 |
| `--apfs` | APFS | 是 (file extents) | 写时复制扫描从旧节点副本恢复已删除的文件(名称+大小+数据) |
每种模式都会通过 MBR/GPT/APM 分区表自动定位其卷,或者采用
显式的 `--offset`。尽力而为的恢复(可能是已重用的簇,
碎片的 FAT 文件)会被标记为低置信度。
**整盘处理** —— `--list-partitions` 打印 MBR/GPT/APM 表以及
每个分区检测到的文件系统。`--auto` 随后在每个分区上运行匹配的反删除
模式(对任何无法识别文件系统的分区进行 carving),并将每个分区的结果
写入其各自的 `part
_/` 子目录中。
## 选项
| 标志 | 默认值 | 效果 |
|------------------|----------------|-------------------------------------------------|
| `-o, --output` | `./carved` | 输出目录 |
| `-t, --types` | all | 以逗号分隔的类型列表(可使用别名:jpeg, docx, mov, ...) |
| `--offset` | 0 | 源数据的起始偏移量(支持 K/M/G 后缀) |
| `--length` | 至末尾 | 从偏移量开始扫描的字节数 |
| `--align N` | 1 | 仅接受 N 字节对齐的文件头 |
| `--max-size` | 各类型不同 | 提取文件大小的全局上限 |
| `--min-size` | 0 | 丢弃过小的提取文件 |
| `--chunk` | 32M | 扫描块大小 |
| `-j, --jobs N` | 1 | 并行扫描进程数(0 = 所有核心) |
| `--ntfs` | off | NTFS MFT 反删除模式 |
| `--ext4` | off | ext2/3/4 inode + dirent 反删除模式 |
| `--fat` | off | FAT12/16/32 + exFAT 反删除模式 |
| `--hfs` | off | HFS+/HFSX 目录反删除模式 |
| `--apfs` | off | APFS 写时复制恢复模式 |
| `--auto` | off | 检测分区 + 文件系统,逐一进行反删除 |
| `--list-partitions` | | 打印 MBR/GPT/APM 表并退出 |
| `--grep PATTERN` | | 关键字/正则表达式搜索(ASCII+UTF-16);可重复 |
| `--sig-file FILE`| | 加载用户自定义的签名 (JSON) |
| `--timeline FILE`| | 写入 MACB 时间线 (.csv/.jsonl) |
| `--html FILE` | | 写入 HTML 报告 + 图片库 |
| `--validate` | off | 深度解码提取结果;设置已验证/失败置信度 |
| `--drop-failed` | off | 与 --validate 一起使用,丢弃解码失败的提取数据 |
| `--no-bifragment`| off | 禁用双碎片间隙重组 |
| `--no-skip-blank`| off | 也扫描全零(TRIM'd/sparse)区域 too |
| `--matcher` | auto | 签名匹配后端 (auto/regex/aho-corasick) |
| `--no-skip` | off | 继续扫描已提取文件内部 |
| `--no-dedup` | off | 保留哈希值相同的重复提取文件 |
| `--dry-run` | off | 仅报告发现,不写入任何内容 |
| `--report FILE` | `/manifest.json` | JSON 清单文件路径 |
| `--csv FILE` | | 同时将发现结果写为 CSV |
| `--bodyfile FILE`| | 同时写入 Sleuth Kit bodyfile (供 mactime 使用) |
| `--hash-source` | off | 将整个源的 SHA-256 写入清单(用于证据保管链) |
| `--machine` | off | 在标准输出输出 JSON-lines 格式事件 |
| `-q, --quiet` | off | 隐藏进度输出 |
| `--list-types` | | 打印签名表并退出 |
## 支持的类型
| 类型 | 文件 | 结尾检测 |
|--------|--------------------------------|--------------------------------------------|
| jpg | JPEG | 标记遍历 + 熵扫描直到 EOI |
| png | PNG | 块遍历直到 IEND |
| gif | GIF87a/89a | 块遍历直到文件尾 |
| bmp | BMP | 文件头大小字段 |
| tif | TIFF | IFD + strip/tile extent walk |
| pdf | PDF | 最后一个 `%%EOF` (以下一个 PDF 文件头为界) |
| zip | ZIP, docx/xlsx/pptx, jar, apk, epub, odf | EOCD record, central-dir cross-check |
| gz | gzip | zlib 流解码(多成员) |
| 7z | 7-Zip | 签名头中的 next-header 偏移量 |
| rar | RAR4/5 | 无 — 限制大小提取,未经验证 |
| sqlite | SQLite 3 | page_size × page_count |
| mp4 | MP4 / MOV | 顶层 box 遍历 |
| riff | WAV, AVI, WebP | RIFF 大小字段 |
| mp3 | MP3 (带 ID3v2 标签) | ID3 大小 + MPEG 帧遍历 |
| exe | PE (exe/dll) | 节表 + Authenticode 证书 |
| elf | ELF | 节头表结尾 |
| macho | Mach-O thin + universal | load command / fat arch extents |
| ole | OLE2/CFB (doc, xls, ppt, msi) | FAT max-used-sector walk |
| mp4 | MP4/MOV/HEIC/AVIF/3GP/M4A/M4V | ISO-BMFF box 遍历 + 基于 brand 的扩展名 |
| mkv | Matroska / WebM | EBML 元素 + Segment 大小 |
| ogg | OGG (Vorbis/Opus/Theora) | 页面遍历直到流结束 |
| flac | FLAC | 元数据块(帧为尽力而为) |
| psd | Photoshop | 节遍历(图像数据为尽力而为) |
| ico | ICO / CUR | 目录条目表 extent |
| evtx | Windows event log | 头块计数 |
| hive | Windows registry (regf) | 基础块 + hbins 大小 |
| plist | Apple binary plist | trailer offset-table identity |
每次提取都会生成一个 SHA-256 哈希并存放在 `//f_.` 中。
一个 JSON 清单(`/manifest.json`)会记录偏移量、大小、哈希值,以及
该结构是否被完整解析(`validated`)或者其大小是否为
尽力而为的回退结果。
## 文件系统与操作系统支持
Carving 与文件系统无关:它扫描原始字节,因此 NTFS, ext2/3/4, FAT,
exFAT, APFS, XFS, btrfs,以及损坏或未知的文件系统都以相同方式工作。
可在 Linux, macOS 和 Windows 上运行(原始设备访问会使用扇区对齐
读取并自动进行 IOCTL 大小检测)。
固有的提取限制(与 PhotoRec 相同):
- 只有 **连续的** 文件能完整恢复 —— 碎片文件只能提取出第一个
碎片加上一些垃圾数据。
- NTFS 压缩或 EFS 加密的文件在磁盘上不是原始格式。
- 全盘加密:当您提供凭证时(见下文),**BitLocker 会被透明解锁**;LUKS/FileVault 仍然需要已解锁的设备。
- TRIM 过的 SSD 块读出的全是零 —— 任何工具都无法恢复。
- 没有文件名/时间戳 —— 这需要文件系统元数据恢复
(属于 Sleuth Kit `fls`/`icat` 的范畴),而不是 carving。
## BitLocker (Windows FVE)
carvx 可以**就地**解密 BitLocker 卷:提供凭证后,
锁定的卷在相同的偏移量处读取回来时就是明文的 NTFS 数据,因此 carving、
`--ntfs`/`--auto` 反删除模式、`--grep` 和 `--list-partitions` 等所有功能都能
像磁盘从未被加密过一样工作。
```
# carve 受 recovery-key 保护的 SSD 镜像(XTS-AES,Win10/11 默认)
carvx disk.E01 --bitlocker-recovery-key 471806-...-635835 -o out
# whole-disk 自动模式:检测 BitLocker partition,unlock,恢复 NTFS
carvx /dev/sdb --auto --bitlocker-recovery-key 471806-...-635835
# 其他 protectors
carvx disk.dd --bitlocker-password 'Hunter2!' # user passphrase
carvx disk.dd --bitlocker-bek startup.BEK # startup key file
carvx disk.dd --bitlocker-fvek 0011aabb... # raw FVEK (hex)
```
支持的加密算法:AES-XTS-128/256 (Windows 8+/10/11, 包括 SSDs),
AES-CBC-128/256,以及 AES-CBC + Elephant diffuser (Vista/7)。挂起的卷
(clear-key protector)无需凭证即可解锁。解密是纯 Python 实现且
只读的;安装可选的 `cryptography` 包(`pip install
carvx[bitlocker]`)可以切换为 C 语言支持的 AES,从而大幅提升速度。
## 注意事项
- 默认情况下,carvx 会跳过已验证的提取结果(PhotoRec 行为)。使用
`--no-skip` 可以同时查找嵌入在其他文件内部的文件。
- 未经验证的提取结果(rar, 旧版 sqlite, 窗口边缘的 gz)尾部可能带有垃圾
数据 —— 真实的数据位于前面。
- 单线程吞吐量大约为 150–250 MiB/s;主要受限于
正则表达式扫描和源数据的读取速度。
- 请在证据的只读镜像(`dd`/`ddrescue` 副本)上操作,切勿在
原始证据上操作 —— 这是标准的取证惯例。
## 输出
每次提取的结果都会存入 `//f_.`(carving 模式)或
`/ntfs/`(NTFS 模式),并附带 SHA-256 哈希。JSON 清单
(`/manifest.json`)会记录每个文件的:偏移量/M 编号、大小、哈希值、
`validated`/`confidence`、`duplicate_of`,以及(针对 NTFS)原始名称 + 时间戳。
此外还有扫描元数据:工具版本、源路径/大小、开始/结束时间、选项,
以及可选的整个源的哈希值。CSV 和 Sleuth Kit bodyfile 导出是可选的。
## 测试
```
pip install pytest
pytest tests/ # 150+ tests
# 或独立的 integration check(无需 pytest):
python3 tests/make_test_image.py
```
该测试套件会构建合成镜像(每种支持的类型一个,一个合成 NTFS
卷,以及在系统格式化工具可用时,通过操作系统构建 **真实的 ext4/FAT32/exFAT/HFS+/APFS 镜像**),其中包含已删除 + 碎片文件,并验证每次恢复的
哈希值是否与原始文件匹配。处理器测试还会输入截断的、损坏的和
纯噪声的数据,以确认程序不会崩溃且不会产生经验证的误报。
磁盘镜像读取器会与 `qemu-img` 生成的 QCOW2/VMDK 进行对比检查。如果测试
需要使用的工具不可用,则会干净地跳过,而不是报错失败。标签:Python, 数字取证, 数据恢复, 文件雕刻, 无后门, 磁盘镜像分析, 自动化脚本, 逆向工具