{"id":51595156,"url":"https://github.com/securityronin/ad1-forensic","last_synced_at":"2026-07-11T18:01:36.708Z","repository":{"id":368536206,"uuid":"1285675772","full_name":"SecurityRonin/ad1-forensic","owner":"SecurityRonin","description":"Pure-Rust reader + forensic auditor for AccessData AD1 logical images (FTK Imager Custom Content Image)","archived":false,"fork":false,"pushed_at":"2026-07-10T17:26:30.000Z","size":49850,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-07-10T19:12:14.625Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","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-07-01T03:34:04.000Z","updated_at":"2026-07-10T17:26:35.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/SecurityRonin/ad1-forensic","commit_stats":null,"previous_names":["securityronin/ad1-forensic"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/SecurityRonin/ad1-forensic","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SecurityRonin%2Fad1-forensic","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SecurityRonin%2Fad1-forensic/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SecurityRonin%2Fad1-forensic/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SecurityRonin%2Fad1-forensic/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/SecurityRonin","download_url":"https://codeload.github.com/SecurityRonin/ad1-forensic/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SecurityRonin%2Fad1-forensic/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35370428,"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-07-11T02:00:05.354Z","response_time":104,"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":[],"created_at":"2026-07-11T18:01:35.167Z","updated_at":"2026-07-11T18:01:36.694Z","avatar_url":"https://github.com/SecurityRonin.png","language":"Rust","funding_links":["https://github.com/sponsors/h4x0r"],"categories":[],"sub_categories":[],"readme":"# ad1-forensic\n\n[![Crates.io](https://img.shields.io/crates/v/ad1-core.svg)](https://crates.io/crates/ad1-core)\n[![Docs.rs](https://img.shields.io/docsrs/ad1-core)](https://docs.rs/ad1-core)\n[![License: Apache-2.0](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE)\n[![CI](https://github.com/SecurityRonin/ad1-forensic/actions/workflows/ci.yml/badge.svg)](https://github.com/SecurityRonin/ad1-forensic/actions/workflows/ci.yml)\n[![Fuzz](https://github.com/SecurityRonin/ad1-forensic/actions/workflows/fuzz.yml/badge.svg)](https://github.com/SecurityRonin/ad1-forensic/actions/workflows/fuzz.yml)\n[![Sponsor](https://img.shields.io/badge/sponsor-h4x0r-ea4aaa?logo=githubsponsors)](https://github.com/sponsors/h4x0r)\n\n**Open AccessData AD1 logical images in Rust — the format TSK, libewf, and\nBitCurator can't read. List files, extract bytes, and verify the stored\nper-file hashes.**\n\nFTK Imager's AD1 (\"Custom Content Image\") is a *logical* evidence container — a\ntree of files with metadata and zlib-compressed data, not a disk image. Nothing\nin the open-source disk-forensics stack parses it. `ad1-core` does, with no\n`unsafe` and no C dependencies.\n\n## 30 seconds to a file listing\n\n```toml\n# Cargo.toml\n[dependencies]\nad1-core = \"0.1\"\n```\n\n```rust\nuse ad1::Ad1Reader;\n\nlet img = Ad1Reader::open(std::path::Path::new(\"evidence.ad1\"))?;\nfor entry in img.entries() {\n    let kind = if entry.is_dir { \"DIR \" } else { \"FILE\" };\n    println!(\"{kind} {:\u003e10}  {}\", entry.size, entry.path);\n}\n\n// Read a file's bytes (only the overlapping zlib chunks are inflated):\nif let Some(f) = img.entries().iter().find(|e| !e.is_dir) {\n    let mut buf = vec![0u8; f.size as usize];\n    let n = img.read_at(f, 0, \u0026mut buf)?;\n    println!(\"read {n} bytes of {}\", f.path);\n}\n# Ok::\u003c(), ad1::Ad1Error\u003e(())\n```\n\n`Ad1Reader::open` discovers split segments (`.ad1`, `.ad2`, …), parses the file\ntree, and exposes each entry's path, size, stored MD5/SHA1, and timestamps.\n`read_at` is positioned — it inflates only the chunks your range overlaps, not\nthe whole file.\n\n## Tamper detection\n\n`ad1-forensic` recomputes each file's hash and compares it to the value stored in\nthe image, emitting graded [`forensicnomicon`](https://crates.io/crates/forensicnomicon)\nfindings:\n\n```rust\nfor finding in ad1_forensic::audit(std::path::Path::new(\"evidence.ad1\")) {\n    println!(\"[{:?}] {} — {}\", finding.severity, finding.code, finding.note);\n}\n```\n\n| Code | Meaning |\n|---|---|\n| `AD1-HASH-MISMATCH` | stored hash ≠ recomputed hash (tamper signal) |\n| `AD1-ENCRYPTED` | `ADCRYPT` image — content not verifiable |\n| `AD1-SEGMENT-MISSING` | a declared `.adN` segment is absent |\n| `AD1-SIZE-LIE` | fewer bytes decompressed than declared |\n| `AD1-UNREADABLE` | the structure could not be parsed |\n\n## Built for untrusted input\n\n- **No `unsafe`** (`#![forbid(unsafe_code)]`), **no C** (pure-Rust zlib + hashes).\n- **Panic-free on malformed input** — bounds-checked reads, capped allocations,\n  cycle guards. Two `cargo-fuzz` targets back this up.\n- **Encrypted (`ADCRYPT`) images are refused**, never decoded to garbage.\n\nSee [docs/format.md](docs/format.md) for the on-disk layout and\n[docs/validation.md](docs/validation.md) for how correctness is established.\n\n---\n\n[Privacy Policy](https://securityronin.github.io/ad1-forensic/privacy/) · [Terms of Service](https://securityronin.github.io/ad1-forensic/terms/) · © 2026 Security Ronin Ltd\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsecurityronin%2Fad1-forensic","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsecurityronin%2Fad1-forensic","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsecurityronin%2Fad1-forensic/lists"}