Validation¶
Every container decoder is checked against a real image produced by the tool an
examiner would actually encounter (EnCase, qemu-img, hdiutil), and the NTFS
fingerprinting is proven end-to-end against a genuine NTFS boot region from a
public CTF disk. Run cargo test --all-features; the fixtures and assertions are
linked below.
disk-forensic is validated against real images produced by real tools, not
only synthetic fixtures — the doer/checker discipline: code you validated only
with fixtures you hand-built inherits your own blind spots. Where a third-party
decoder crate does the work, its output was byte-exact verified (boot signature
and a marker planted deep in the disk) through that crate before it was wired
into open().
All fixtures live in tests/data/ and the assertions are in
tests/open_tests.rs and
tests/cli_tests.rs. Run cargo test --all-features.
Container decoders¶
Each container fixture wraps the same known MBR disk (a 1 MiB image with a recognisable marker at sector 100), produced from a raw image with the tool a real examiner would encounter, then decoded back and checked.
| Format | Fixture | How produced | Decoder | Asserted |
|---|---|---|---|---|
Raw / dd |
apm.bin |
committed | in-place | scheme parses |
| E01 / EWF | gpt_130_partitions.E01 |
real EnCase image (130-partition GPT) | ewf |
decodes → Scheme::Gpt |
| VMDK | df.vmdk |
qemu-img convert -O vmdk (monolithicSparse) |
vmdk |
decodes → Scheme::Mbr, virtual size exact |
| QCOW2 | df.qcow2 |
qemu-img convert -O qcow2 (v3) |
qcow |
decodes → Scheme::Mbr, virtual size exact |
| VHDX | df.vhdx |
qemu-img convert -O vhdx |
vhdx |
decodes → Scheme::Mbr, virtual size exact |
| VHD (dynamic) | df-dynamic.vhd |
qemu-img convert -O vpc |
built-in (src/vhd.rs) |
decodes → Scheme::Mbr, CHS-rounded size |
| VHD (fixed) | df-fixed.vhd |
qemu-img convert -O vpc -o subformat=fixed |
built-in (src/vhd.rs) |
decodes → Scheme::Mbr, CHS-rounded size |
| DMG | df.dmg |
hdiutil convert -format UDZO |
dmg-core |
decodes → Scheme::Mbr |
| ISO 9660 | df.iso |
hdiutil makehybrid -iso -joliet |
iso9660-forensic |
volume label + clean exit |
| AFF4 (physical) | aff4::testutil::test_aff4 |
spec-faithful builder in the aff4 reader crate |
aff4 |
decodes → disk view, virtual size + bytes exact |
| AFF4 (logical) | aff4::testutil::test_aff4_logical |
builder in the aff4 reader crate |
aff4 (via logical::open) |
container::open → OpenError::LogicalContainer; logical::open lists + reads |
| AD1 | ad1::testfix::build |
spec-faithful builder in the ad1-core reader crate (independent flate2 + RustCrypto ground truth) |
ad1-core (via logical::open) |
container::open → OpenError::LogicalContainer; logical::open lists + reads bytes exact |
The VHD virtual size is qemu's CHS-geometry-rounded value (1 079 296 bytes, not 1 MiB) — the test asserts the real value observed in the footer, a concrete example of why synthetic-only assumptions are unsafe.
Real NTFS, end to end¶
tests/data/ntfs.vmdk carries a genuine NTFS boot region — the real
$Boot/BPB (OEM "NTFS ") extracted from MaxPowersCDrive.E01 of the
public DEF CON DFIR CTF 2018 image — re-based onto a compact 64 MiB disk with
real Windows MBR boot code and wrapped in a sparse VMDK (192 KiB). Only the BPB
hidden_sectors/total_sectors were adjusted so the geometry stays
self-consistent with the compact layout.
vmdk_with_real_ntfs_fingerprints_the_partition asserts the whole stack on real
data: VMDK decode → MBR parse → the partition fingerprints as
DetectedFs::Ntfs (filesystem identified from its real VBR, not the partition
type byte alone).
This validates filesystem fingerprinting at the partition level. disk-forensic
does not parse NTFS internals ($MFT, etc.) — that is a filesystem analyzer's
job — so the fixture is a boot-region slice, not a fully mountable volume.
Report normalization¶
Every layer is normalized into the shared
forensicnomicon::report
model (Report { findings, provenance, timeline }), categorized with the
canonical Category::from_code, so disk4n6 renders one uniform view.
| Layer | Findings | Provenance | Timeline | Finding evidence offset |
|---|---|---|---|---|
| MBR | ✅ | ✅ | — (no datable events) | ✅ (offset) |
| GPT | ✅ | ✅ | — (no datable events) | ❌ — analyzer exposes no uniform offset |
| APM | ✅ | ✅ | — (no datable events) | ❌ — analyzer exposes no uniform offset |
| ISO 9660 | ✅ | ✅ (full volume + Rock Ridge authoring intel) | ✅ (PVD create/modify + authoring window) | ❌ — offsets are per-AnomalyKind |
tests/iso_normalize_tests.rs checks the ISO provenance completeness and the
reconstructed timeline against the real df.iso volume (system/mastering id,
session count, Rock Ridge owners, and the create/authoring-window events),
asserting timeline events by structure — present, attributed, and datable
(when.is_some()) — never by literal timestamp value.
Known boundary: uniform per-finding byte-offset evidence is only produced for
MBR, because only mbr-forensic exposes a top-level Anomaly.offset. GPT/APM/ISO
bury offsets inside individual AnomalyKind variants; surfacing them uniformly
requires those analyzer crates to expose an offset, not a per-variant match here.
Safety properties¶
#.- Fuzz target (
cargo fuzz,fuzz/fuzz_targets/analyse_disk.rs) exercisesanalyse_diskagainst crafted/corrupted input. - Corrupt or unsupported-variant containers fail with a typed
OpenError::Decoderather than producing silently wrong output; a legacy QCOW1 and a VHD differencing disk are rejected explicitly.