{"id":51015065,"url":"https://github.com/securityronin/dar-forensic","last_synced_at":"2026-06-21T09:02:37.898Z","repository":{"id":362821968,"uuid":"1260898491","full_name":"SecurityRonin/dar-forensic","owner":"SecurityRonin","description":"Pure-Rust forensic reader + anomaly auditor for Denis Corbin DAR (Disk ARchiver) archives, incl. Passware Kit Mobile / Cellebrite mobile extractions; formats 1-11, transparent gzip/bzip2/xz/zstd/lz4/lzo, multi-volume, hardened and fuzz-tested. dar-core reader + dar-forensic analyzer.","archived":false,"fork":false,"pushed_at":"2026-06-14T01:54:44.000Z","size":1264,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-14T03:19:23.356Z","etag":null,"topics":["archive","backup","compression","dar","dfir","disk-archiver","forensics","incident-response","mobile-forensics","parser","passware","rust"],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/SecurityRonin.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-06T02:10:16.000Z","updated_at":"2026-06-14T01:54:47.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/SecurityRonin/dar-forensic","commit_stats":null,"previous_names":["securityronin/dar-forensic"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/SecurityRonin/dar-forensic","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SecurityRonin%2Fdar-forensic","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SecurityRonin%2Fdar-forensic/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SecurityRonin%2Fdar-forensic/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SecurityRonin%2Fdar-forensic/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/SecurityRonin","download_url":"https://codeload.github.com/SecurityRonin/dar-forensic/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SecurityRonin%2Fdar-forensic/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34603638,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-21T02:00:05.568Z","response_time":54,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["archive","backup","compression","dar","dfir","disk-archiver","forensics","incident-response","mobile-forensics","parser","passware","rust"],"created_at":"2026-06-21T09:02:36.233Z","updated_at":"2026-06-21T09:02:37.891Z","avatar_url":"https://github.com/SecurityRonin.png","language":"Rust","funding_links":["https://github.com/sponsors/h4x0r"],"categories":[],"sub_categories":[],"readme":"# dar-forensic\n\n[![Crates.io](https://img.shields.io/crates/v/dar-forensic.svg)](https://crates.io/crates/dar-forensic)\n[![docs.rs](https://img.shields.io/docsrs/dar-forensic)](https://docs.rs/dar-forensic)\n[![License: Apache-2.0](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](LICENSE)\n[![CI](https://github.com/SecurityRonin/dar-forensic/actions/workflows/ci.yml/badge.svg)](https://github.com/SecurityRonin/dar-forensic/actions)\n[![Sponsor](https://img.shields.io/badge/sponsor-h4x0r-ea4aaa?logo=github-sponsors)](https://github.com/sponsors/h4x0r)\n\nPure-Rust reader for Denis Corbin **DAR (Disk ARchiver)** archives — the format mobile-forensics tools (Passware Kit Mobile, Cellebrite) use for full-filesystem extractions. Enumerates the catalog, seeks straight to any file for random-access extraction — transparently decompressing gzip, bzip2, xz, zstd, lz4 and lzo, and reading multi-volume (sliced) archives — and is hardened to be pointed safely at untrusted evidence. Zero `unsafe`, no GPL, no C bindings.\n\n## Two crates\n\n| Crate | Role | crates.io |\n|-------|------|-----------|\n| **`dar-core`** | read-only parser — open, enumerate, seek-extract, CRC-verify | `cargo add dar-core` |\n| **`dar-forensic`** | forensic-grade reader + anomaly auditor (`audit()` → graded findings, `write_bodyfile()`) | `cargo add dar-forensic` |\n\n`dar-forensic` re-exports the full `dar-core` reader, so the analyzer crate alone is enough for forensic work:\n\n```toml\n[dependencies]\ndar-forensic = \"0.7\"\n```\n\n## Quick start\n\n```rust\nuse std::fs::File;\nuse dar_forensic::DarReader;\n\n// `open` takes anything Read + Seek — a File, or a Cursor over bytes.\nlet mut reader = DarReader::open(File::open(\"userdata.1.dar\")?)?;\n\nfor entry in reader.entries() {\n    println!(\"{} ({} bytes)\", entry.path_lossy(), entry.size);\n}\n\n// Extract one file — a direct seek to its catalog offset, no scanning.\nlet data = reader.extract(\"root/etc/hostname\")?;\nprintln!(\"{}\", String::from_utf8_lossy(\u0026data));\n\n// Integrity check — recompute the stored per-file CRC over the data.\nprintln!(\"{}\", reader.verify(\"root/etc/hostname\")?); // CRC match | CRC mismatch: …\n\n// Forensic audit — flag catalogue anomalies (metadata only, no data read).\nfor finding in reader.audit() {\n    // e.g. [MEDIUM] DAR-PATH-TRAVERSAL: entry `../../etc/cron.d/x` contains a `..` …\n    eprintln!(\"{finding}\");\n}\n\n// Timeline export — write a Sleuth Kit bodyfile straight into `mactime`.\nreader.write_bodyfile(\u0026mut std::io::stdout())?;\n# Ok::\u003c(), dar_forensic::DarError\u003e(())\n```\n\n## What makes this different\n\nDAR is a C++ format; the reference implementation (`libdar`) is GPL with C bindings, and the `dar` name on crates.io is an empty placeholder. `dar-forensic` is the first standalone, dependency-light Rust reader — and it is built for forensic use, where the archive is *evidence from a potentially hostile source*:\n\n| | libdar (C++) | `dar-forensic` |\n|---|---|---|\n| Language / linkage | C++, GPL, C FFI | pure Rust, Apache-2.0, `unsafe_code = \"deny\"` |\n| Reads DAR formats 1–11 | ✅ | ✅ (1 + 7–11 validated against real archives) |\n| Tape-marks-disabled archives (Passware / mobile) | ✅ | ✅ |\n| Random-access extraction (`Read + Seek`) | ✅ | ✅ — composes with `ewf`, `vmdk`, … |\n| Transparent gzip / bzip2 / xz / zstd / lz4 / lzo decompression | ✅ | ✅ — pure-Rust decoders, no C |\n| Multi-volume (sliced) archives | ✅ | ✅ — `open_slices()`; file data spans slices transparently |\n| Tail-scan for 90+ GiB archives (≈107 MiB read, not 99 GiB) | — | ✅ |\n| Forensic anomaly audit (`audit()` → severity-graded findings) | — | ✅ — incomplete catalogue, path-traversal, absolute path, … (serde-exportable) |\n| Timeline export (Sleuth Kit bodyfile → `mactime`) | — | ✅ — `write_bodyfile()` straight from the catalogue |\n| Hardened against malicious input (no panic / OOM / backward seek) | — | ✅ |\n| Continuous fuzzing | — | ✅ `cargo fuzz` |\n| 100% line coverage, CI-enforced | — | ✅ |\n\n### Note on the \"Passware variant\"\n\nArchives written by Passware Kit Mobile have no `seqt_catalogue` escape, which once looked like a vendor-specific format. It isn't: the escape is an *optional sequential-read tape mark*, and Passware simply writes archives with tape marks **disabled** (equivalent to `dar -at`). They are **standard DAR** — official dar reads them too. `dar-forensic` locates the catalog by its `ref_data_name` label in that case (a real structural field, the same 10 bytes as the slice label), so it reads both tape-marked and tape-mark-free archives.\n\n## Anomaly codes\n\n`audit()` reads the catalogue only (no entry data) and returns severity-graded `Anomaly` values, most-severe first. Each carries a stable, machine-readable `code` (a published contract), a `severity`, and a human-readable note. Findings are **observations, not verdicts** — the analyst draws the conclusion.\n\n| `code` | Severity | What it flags |\n|--------|----------|---------------|\n| `DAR-CATALOG-INCOMPLETE` | High | Catalogue ended early — fewer entries recovered than the archive claims (truncation or corruption) |\n| `DAR-PATH-ABSOLUTE` | Medium | Entry path begins with `/` — extraction outside the intended root |\n| `DAR-PATH-TRAVERSAL` | Medium | Entry path contains a `..` component — directory-traversal on extraction |\n| `DAR-PATH-DUPLICATE` | Low | The same path appears more than once in the catalogue |\n| `DAR-TIME-FUTURE` | Low | An `atime`/`mtime`/`ctime` is far in the future — possible timestamp tampering |\n| `DAR-NAME-CONTROL` | Low | Entry name contains control characters (`\u003c 0x20` or `0x7f`) — terminal-injection / concealment |\n\nWith the `serde` feature, `Anomaly` is `Serialize` for JSON/structured export.\n\n## Format support\n\n| DAR format | `version_string` | Status |\n|------------|------------------|--------|\n| Format 11 (dar 2.7–2.8) | `\"0;3\"` (11.3) | Supported — validated against a dar 2.8.5 fixture |\n| Format 10 (dar 2.6) | `\"0:1\"` | Supported — validated against a dar 2.6.16 fixture |\n| Format 9 (dar 2.5) | `\"090\"` | Supported — validated against a dar 2.5.3 fixture **and a real 92 GiB Passware archive** |\n| Format 8 (dar 2.4) | `\"081\"` | Supported — validated against a dar 2.4.24 fixture |\n| Format 7 (dar 2.3) | `\"07\"` | Supported — validated against a dar 2.3.12 fixture |\n| Formats 2–6 (dar 2.0–2.3) | `\"02\"`–`\"06\"` | Same legacy grammar as 7; parsed but not yet validated against a fixture |\n| Format 1 (dar 1.0.x) | `\"01\"` | Supported — validated against a real dar 1.0.0 archive (flagless inode, `size·offset` cat_file, no CRC) |\n| Tape marks on **or** off | — | both supported (e.g. Passware writes them off) |\n| Archive creation / writing | — | Not supported (reader only) |\n\nThe format version is the header `version_string`, each byte `value + 48` (`\"090\"` → 9, `\"0:1\"` → 10.1). Formats ≤ 7 are structurally different — no `seqt_catalogue` escape (catalog located via the end *terminateur* trailer), `u16` uid/gid, bare-seconds timestamps, and a fixed 2-byte CRC; format 1 goes further still — no inode flag byte, and a `size·offset`-only file record with no CRC. Compressed pre-8 archives carry no per-entry codec byte, so the archive-global codec drives both the catalog and every entry. The full per-version layout, reverse-documented from the authoritative libdar source, is in [docs/implementation-notes.md](docs/implementation-notes.md) §11–§12.\n\n### Scope and limits\n\n- **Read-only** — does not create or modify archives.\n- **Decompression: gzip, bzip2, xz, zstd, lz4, lzo** — all six are transparently inflated for both the compressed catalog and extracted entry data (pure-Rust decoders, bounded against decompression bombs), in both dar's single-stream and per-block (`block_compressor`) modes. Encrypted entries are *listed* but `extract()` returns a clear error rather than wrong bytes — decryption is out of scope.\n- **All codecs always compiled** — a forensic reader must read every variant it encounters, so the six decompression codecs are not optional Cargo features. The only optional feature is `serde` (structured `audit()` export).\n- **CRC verification** — `verify(path)` recomputes libdar's per-file CRC over the decompressed data and compares it to the value stored in the catalogue, returning `Match`, `Mismatch { stored, computed }`, or `NotStored` (edition-1 archives record no CRC). It never withholds the bytes: data that fails its CRC can still be `extract`ed for analysis of the corruption.\n\n## Security\n\n`dar-forensic` is designed to be run on archives from potentially compromised or adversarial sources:\n\n- **No panics on malicious input** — every attacker-controlled length and offset is bounds- or overflow-checked.\n- **No allocation bombs** — a forged `stored_size` is validated against the real archive length *before* any allocation.\n- **No backward seeks** — a length that would cast to a negative `i64` seek is rejected.\n- **Bounded decoding** — infinints are `u64`-or-`Corrupt` (never silently truncated); NUL-terminated names are length-capped; the terminateur scan is bounded.\n- **Zero `unsafe`** and continuously fuzz-tested.\n\n### Running the fuzz target\n\n```bash\nrustup install nightly\ncargo install cargo-fuzz\n# three targets: the parser (fuzz_open), full read+extract (fuzz_read),\n# and the audit pipeline (fuzz_forensic)\ncargo +nightly fuzz run fuzz_open\n```\n\n## Testing\n\n187 tests — unit (private helpers + every error branch), synthetic-archive integration, and real-fixture integration — at **100% library line coverage, enforced in CI** (`cargo llvm-cov`, lcov gate), with a second gate that holds the public-API (`tests/`) suite to the same bar. The committed, reproducible fixtures are each **written by the upstream `dar` reference CLI** (one per dar release for formats 7–11) and `dar_xform`, so the reader is checked against an independent tool's output, not a self-encoded round trip; they cover all six `dar -z` codecs (gzip/bzip2/xz/zstd/lz4/lzo) in single-stream and per-block mode, and multi-volume (sliced) archives. Development additionally exercised the reader against a real dar-1.0.0 edition-1 archive, a confidential large Passware Kit Mobile archive (format 9), and a large Android extraction re-sliced with `dar_xform` — none committed, so no in-repo test reproduces them today; that real Android archive caught two bugs no synthetic fixture could (see `docs/implementation-notes.md`). The parser survives millions of `cargo fuzz` executions with zero crashes. Which oracle backs each capability, the evidence tiers, and the recommended `dar -x` differential and env-gated large-corpus tests are in [docs/validation.md](https://securityronin.github.io/dar-forensic/validation/).\n\n```bash\ncargo test\ncargo install cargo-llvm-cov \u0026\u0026 cargo llvm-cov --lcov --output-path lcov.info\n```\n\n\u003e The `--summary-only` line percentage can read slightly under 100% because the generic, reader-agnostic functions are monomorphized once per reader type across the test binaries; the lcov merge (and `--show-missing-lines`) confirms no source line is left uncovered.\n\n## Related crates\n\n`dar-forensic` reads the files *inside* a DAR archive. When the archive itself is wrapped in a disk-image container, these crates provide the same `Read + Seek` interface to feed it:\n\n| Crate | Format |\n|-------|--------|\n| [`ewf`](https://github.com/SecurityRonin/ewf) | E01 / Expert Witness Format (EnCase, FTK Imager) |\n| [`aff4`](https://github.com/SecurityRonin/aff4) | AFF4 v1 (Evimetry) |\n| [`vmdk`](https://github.com/SecurityRonin/vmdk) | VMware VMDK |\n| [`vhdx`](https://github.com/SecurityRonin/vhdx) | Microsoft VHDX (Hyper-V, Azure) |\n| [`vhd`](https://github.com/SecurityRonin/vhd) | Legacy VHD |\n| [`qcow2`](https://github.com/SecurityRonin/qcow2) | QEMU / KVM QCOW2 |\n| [`ufed`](https://github.com/SecurityRonin/ufed) | Cellebrite UFED |\n| [`dd`](https://github.com/SecurityRonin/dd) | Raw / flat / dd images |\n| [`iso9660-forensic`](https://github.com/SecurityRonin/iso9660-forensic) | ISO 9660 optical media |\n| [`dmg`](https://github.com/SecurityRonin/dmg) | Apple DMG / UDIF |\n\nFor forensic integrity analysis of container formats:\n\n| Crate | Format |\n|-------|--------|\n| [`ewf-forensic`](https://github.com/SecurityRonin/ewf-forensic) | E01 structural audit, Adler-32 / MD5 repair |\n| [`vhdx-forensic`](https://github.com/SecurityRonin/vhdx-forensic) | VHDX integrity analysis |\n\n---\n\n[Privacy Policy](https://securityronin.github.io/dar-forensic/privacy/) · [Terms of Service](https://securityronin.github.io/dar-forensic/terms/) · © 2026 Security Ronin Ltd\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsecurityronin%2Fdar-forensic","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsecurityronin%2Fdar-forensic","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsecurityronin%2Fdar-forensic/lists"}