saadtahir-dev/FIMountKit
GitHub: saadtahir-dev/FIMountKit
FIMountKit 是一个 macOS Swift 库,为应用提供挂载和卸载多种取证及通用磁盘映像的统一接口。
Stars: 0 | Forks: 0
# FIMountKit
一个用于在 macOS 上挂载取证和通用磁盘映像的 Swift Package。
| | |
|---|---|
| **SPM package name** | `FIMountKit` |
| **Library product** | `FIMountKit` — 在应用代码中 `import FIMountKit` |
| **Platform** | macOS 13+ |
| **Swift** | 5.9+ |
专为取证工作流和系统级分析设计:在单一的 `ImageMountingService` API 背后支持 DMG、RAW/DD、EWF、AFF4、VMDK、VHD 以及分卷 RAW 映像。
## ⚠️ 重要提示:您必须使用本地 Package — 而非 Xcode 的 URL 流程
**请勿通过 Xcode 的“Add Package Dependencies”对话框或 `Package.swift` 中的 `url:` 引用来添加 FIMountKit。**
FIMountKit 的子模块(`libewf-spm`、`libvmdk-spm`、`libvhdi-spm`)在其 `Package.swift` 中使用了
`unsafeFlags` 来链接预构建的静态库。
Swift Package Manager **会静默阻止远程 package 使用 `unsafeFlags`**,
这将导致构建失败并出现晦涩的 linker 错误。
使用 FIMountKit 唯一受支持的方法是将其在本地 **clone**,并作为**本地 package** 添加到
Xcode 中。这是一个一次性的设置步骤。
## 将 FIMountKit 添加到您的应用
### 步骤 1 — 使用子模块 Clone
务必使用 `--recurse-submodules` 进行 clone。内置的取证库
(`libewf`、`libvmdk`、`libvhdi`、`libcaff4`)是 git submodules,如果不加此标志将无法
获取。
```
git clone --recurse-submodules https://github.com/saadtahir-dev/FIMountKit.git
```
如果您已经未加该标志进行了 clone:
```
git submodule update --init --recursive
```
### 步骤 2 — 在 Xcode 中作为本地 package 添加
1. 在 Xcode 中打开您的应用项目
2. 前往 **File → Add Package Dependencies**
3. 点击 **Add Local...**(对话框左下角)
4. 导航到您 clone FIMountKit 的文件夹并选中它
5. 点击 **Add Package**
6. 在 target dependency 表格中,选择 **FIMountKit** 并点击 **Add Package**
### 步骤 3 — 导入并使用
```
import FIMountKit
```
## 保持 FIMountKit 最新
由于 FIMountKit 是本地 package,更新通过 git 进行管理:
```
cd /path/to/FIMountKit
git pull
git submodule update --recursive
```
然后在 Xcode 中:**File → Packages → Reset Package Caches** 以应用更改。
## 功能特性
- 开箱即用的 SwiftPM 库(`FIMountKit` product)
- 基于扩展名的格式检测(`ImageFormatDetector`)
- 通过 `hdiutil` 原生附加 macOS 映像以呈现块设备
- 通过内置的 FUSE 工具(`ewfmount`、`vmdkmount`、`vhdimount`)支持 EWF / VMDK / VHD — 无需安装 Homebrew 工具
- 通过进程内的 **Libcaff4** 读取 AFF4(无需 CLI,无需 FUSE)
- 在附加前进行分卷 RAW 合并(分块、内存安全)
- 结构化日志记录,带有针对每次挂载的 correlation IDs
- `TemporaryResource` 跟踪以及在卸载时执行 LIFO 清理
- 可安全用于并发场景(`async`/`await`,对 TaskGroup 友好的 mounter)
- 可注入的 mounter 列表,便于测试或提供部分格式支持
## 支持的格式
| 类别 | 扩展名 | 流水线 |
|---|---|---|
| Apple 磁盘映像 | `.dmg`, `.sparseimage`, `.sparsebundle` | `hdiutil attach` |
| 原始映像 | `.raw`, `.dd` | `hdiutil attach` (`CRawDiskImage`) |
| EWF (EnCase) | `.e01`, `.ex01`, `.s01` | `ewfmount` (FUSE) → `ewf1` → `hdiutil` |
| AFF4 | `.aff4` | **Libcaff4** 完整提取 → 临时 `.img` → `hdiutil` |
| VMware VMDK | `.vmdk` | `vmdkmount` (FUSE) → `vmdk1` → `hdiutil` |
| Microsoft VHD | `.vhd` | `vhdimount` (FUSE) → `vhdi1` → `hdiutil` |
| 分卷 RAW | `.001`, `.002`, … | 合并分段 → `hdiutil` |
**检测与合并说明:** `ImageFormatDetector` 也会将 `.000` 和 `.00001` 映射到分卷 RAW,但 `SplitRawMerger` 仅合并**以 `.001` 开始**的集合。请使用 `.001` 作为第一分段以实现可靠的分卷挂载。
**不支持:** `.vhdx`、`.iso`、`.img`(未映射的别名)以及上表之外的格式。
## 工作原理
```
Input URL
↓
ImageFormatDetector (file extension → ImageType)
↓
ImageMountingService (path validation, logging, mounter selection)
↓
ImageMounter (per format)
↓
├─ DMG / RAW / sparse → ProcessExecutor → hdiutil
├─ EWF / VMDK / VHD → bundled *mount (FUSE) → RawImageMounter → hdiutil
├─ AFF4 → Libcaff4 AFF4Image → temp file → RawImageMounter → hdiutil
└─ Split RAW → SplitRawMerger → RawImageMounter → hdiutil
↓
MountResult (mount points, devices, temporaryResources)
```
大多数非原生格式采用**两阶段**处理:生成或暴露一个扁平的 raw 映像,然后复用 `RawImageMounter`。
## 系统要求
| 要求 | 用途 |
|---|---|
| macOS 13+ | Package 平台(`Package.swift`) |
| Swift 5.9+ / Xcode 15+ | 构建 |
| `hdiutil` (系统) | 所有用于附加卷的挂载 |
| [macFUSE](https://osxfuse.github.io/) 4.x 或 5.x | **仅限 EWF、VMDK、VHD**(FUSE 工具) |
AFF4、DMG、RAW 和分卷 RAW **不**需要 macFUSE。但 AFF4 **确实**需要足够的磁盘空间来容纳映像的完整提取副本。
## 依赖项
FIMountKit 将四个取证库作为 **git submodules** 打包在 `Dependencies/` 下。使用 `--recurse-submodules` 进行 clone 时会自动解析它们。
| Submodule | 路径 | Repo |
|---|---|---|
| `libewf-spm` | `Dependencies/libewf-spm` | [github.com/saadtahir-dev/libewf-spm](https://github.com/saadtahir-dev/libewf-spm) |
| `libvmdk-spm` | `Dependencies/libvmdk-spm` | [github.com/saadtahir-dev/libvmdk-spm](https://github.com/saadtahir-dev/libvmdk-spm) |
| `libvhdi-spm` | `Dependencies/libvhdi-spm` | [github.com/saadtahir-dev/libvhdi-spm](https://github.com/saadtahir-dev/libvhdi-spm) |
| `libcaff4-spm` | `Dependencies/libcaff4-spm` | [github.com/saadtahir-dev/libcaff4-spm](https://github.com/saadtahir-dev/libcaff4-spm) |
每个 submodule 都提供了**通用 fat 静态归档**(arm64 + x86_64)以及 SPM 资源包中的**内置 CLI 工具**。在适用的情况下,OpenSSL/zlib 依赖项会被静态链接或通过系统链接。
**依赖关系图:**
```
FIMountKit
├── libewf-spm
│ ├── CLibEWF / CLibEWFFuse (static libs)
│ ├── CLibEWFResources (bin: ewfmount, …)
│ └── libewf (Swift: EWFToolLocator)
├── libcaff4-spm
│ ├── Ccaff4 (static libaff4 + headers)
│ └── Libcaff4 (Swift: AFF4Image)
├── libvmdk-spm
│ ├── CLibVMDK (static libs)
│ ├── CLibVMDKResources (bin: vmdkmount, vmdkinfo)
│ └── LibVMDK (Swift: VMDKToolLocator)
└── libvhdi-spm
├── CLibVHDI (static libs)
├── CLibVHDIResources (bin: vhdimount, vhdiinfo)
└── LibVHDI (Swift: VHDIToolLocator)
```
## 用法
### 默认服务
```
import FIMountKit
let service = ImageMountingService(
log: { message, level, component in
print("[\(component.rawValue)] [\(level.rawValue)] \(message)")
}
)
let result = try await service.mount(
url: URL(fileURLWithPath: "/path/to/image.vmdk")
)
for mountPoint in result.mountPointURLs {
print(mountPoint.path)
}
try await service.unmount(result)
```
### 挂载选项
```
let options = MountOptions(
workspaceDirectory: URL(fileURLWithPath: "/tmp/fimountkit-workspace"),
volumeMountPoint: URL(fileURLWithPath: "/Volumes/CaseImage")
)
let result = try await service.mount(
url: URL(fileURLWithPath: "/path/to/image.e01"),
options: options
)
```
| 选项 | 用途 |
|---|---|
| `workspaceDirectory` | FUSE 挂载目录(EWF/VMDK/VHD),可选的分段合并输出父目录 |
| `volumeMountPoint` | 传递给 `hdiutil -mountpoint`(必须为空或可创建) |
### 自定义 mounter 集合
```
let service = ImageMountingService(
mounters: [
DMGImageMounter(),
RawImageMounter(),
EWFImageMounter(),
]
)
```
默认工厂(`ImageMounterFactory.makeDefaultMounters()`):DMG、Raw、AFF4、EWF、VMDK、VHDI、SplitRaw — 依次执行。服务会选择其 `supportedTypes` 包含已检测类型的**第一个** mounter。
## 工具解析
`ProcessExecutor` 始终接收一个**绝对可执行路径**;它不会搜索 `PATH`。
| 组件 | 工具解析 |
|---|---|
| `DMGImageMounter` | `SystemToolLocator` → `hdiutil` |
| `RawImageMounter` | `SystemToolLocator` → `hdiutil` |
| `EWFMountManager` | `EWFToolLocator.bundledToolPath("ewfmount")` 或系统回退 |
| `VMDKMountManager` | `VMDKToolLocator.vmdkmount` 或系统回退 |
| `VHDIMountManager` | `VHDIToolLocator.vhdimount` 或系统回退 |
| `AFF4ImageMounter` | 进程内 **Libcaff4**(`AFF4Image`) — 无内置 CLI |
| `SplitRawMerger` | 纯 Swift / Foundation I/O |
## 公共 API 接口
| 类型 | 角色 |
|---|---|
| `ImageMountingService` | `mount(url:options:)`、`unmount(_:)` |
| `ImageFormatDetector` | 扩展名 → `ImageType` |
| `ImageMounter` | 每种格式的挂载/卸载协议 |
| `MountResult` | `sourceURL`、`type`、`deviceIdentifiers`、`mountPointURLs`、`temporaryResources`、`metadata` |
| `MountOptions` | 工作区和卷挂载点 |
| `MountError` | 类型化失败(`unsupportedType`、`detectionFailed`、…) |
| `ImageMounterFactory` | 默认 mounter 列表 |
| `*ImageMounter` | `DMG`、`Raw`、`AFF4`、`EWF`、`VMDK`、`VHDI`、`SplitRaw` |
## 资源管理
- FUSE 挂载目录、合并的分卷 RAW 文件以及 AFF4 临时提取内容都会记录在 `MountResult.temporaryResources` 中。
- 卸载时会执行特定于格式的分离操作,然后按**相反顺序**删除临时工件。
- 如果在部分设置后发生挂载失败,mounter 会尝试进行清理(例如,如果 `hdiutil` 失败,则关闭 FUSE)。
## 源码结构
```
Sources/ImageMounter/
├── ImageMountingService.swift
├── Detection/ImageFormatDetector.swift
├── Core/ ImageType, MountResult, MountError, MountOptions
├── Mounters/
│ ├── DMG/ DMGImageMounter
│ ├── RAW/ RawImageMounter
│ ├── EWF/ EWFImageMounter, EWFMountManager
│ ├── AFF4/ AFF4ImageMounter
│ ├── VMDK/ VMDKImageMounter, VMDKMountManager
│ ├── VHD/ VHDIImageMounter, VHDIMountManager
│ ├── SplitRAW/ SplitRawImageMounter, SplitRawMerger
│ └── ImageMounterFactory.swift
├── Infrastructure/ ProcessExecutor, HDIUtil helpers, SystemToolLocator, MountPathValidator
└── Logging/ ImageMounterLogHandler, Logger
Dependencies/
├── libewf-spm/ (git submodule)
├── libvmdk-spm/ (git submodule)
├── libvhdi-spm/ (git submodule)
└── libcaff4-spm/ (git submodule)
```
## 构建与测试
```
git clone --recurse-submodules https://github.com/saadtahir-dev/FIMountKit.git
cd FIMountKit
swift package resolve
swift build
swift test
```
## 注意事项
- 必须安装并加载 **macFUSE** 才能挂载 EWF、VMDK 和 VHD。
- **AFF4** 挂载会在附加前将整个逻辑映像读取到临时文件中 — 大型映像需要按比例分配空闲磁盘空间和时间。
- **多分段 EWF**(`.e01` + `.e02` + …):打开第一分段;libewf / `ewfmount` 会解析整个集合。
- 只要 `hdiutil` 暴露了**多个分区**,系统即支持每个映像包含多个分区。
- 未使用 App Sandbox 时,嵌入内置二进制文件的主机应用可能需要 **Hardened Runtime** 例外(例如禁用库验证)。
## 状态
- 已为列出的所有格式实现核心挂载路径
- 可扩展的 `ImageMounter` 协议和可注入的服务
- 已在 Apple Silicon macOS 上通过 `image-mounter-poc` 批量回归 UI 完成验证
标签:Swift, 开源库, 搜索引擎爬虫, 磁盘镜像挂载