Skip to content

AD1 on-disk format

The AccessData AD1 ("Custom Content Image") layout as implemented by ad1-core, derived from the al3ks1s/AD1-tools reverse-engineered C reference (libad1) and cross-checked against the Cerbero AD1 package and DFIRScience's overview.

AD1 is a logical container — a tree of files/folders with per-file metadata and zlib-compressed data — not a sector image. All integers are little-endian.

Logical addressing

Tree/metadata/chunk addresses are offsets into a virtual space formed by concatenating each segment's body with its 512-byte margin removed:

usable_per_segment = fragments_size * 65536 - 512
segment_index      = logical_offset / usable_per_segment
within_segment     = logical_offset % usable_per_segment
physical_offset    = within_segment + 512

A read that runs past one segment's data continues in the next (.ad2, …).

Detection (first bytes of segment 1)

Bytes Meaning
ADCRYPT\0 encrypted variant — refused (Ad1Error::Unsupported)
ADSEGMENTEDFILE\0 normal AD1
anything else not AD1 (the bytes are shown in the error)

Segment header (offset 0 of each .adN)

Offset Type Field
0x00 char[16] signature ADSEGMENTEDFILE\0
0x18 u32 segment_index
0x1c u32 segment_number (total segments)
0x22 u32 fragments_size (segment size in 64 KiB units)
0x28 u32 header_size

Logical header (physical 0x200 = logical 0)

Offset Type Field
0x200 char[15] signature
0x210 u32 image_version (commonly 3 or 4)
0x218 u32 zlib_chunk_size (max decompressed bytes per chunk)
0x21c u64 logical_metadata_addr
0x224 u64 first_item_addr
0x22c u32 data_source_name_length
0x234 u64 data_source_name_addr

Item header (at a logical offset)

Offset Type Field
+0x00 u64 next_item_addr (sibling)
+0x08 u64 first_child_addr
+0x10 u64 first_metadata_addr
+0x18 u64 zlib_metadata_addr (chunk table; 0 if none)
+0x20 u64 decompressed_size (0 for directories)
+0x28 u32 item_type (0 = file, 5 = folder)
+0x2c u32 item_name_length
+0x30 char[name_length] item_name (/ mapped to _)
+0x30+len u64 parent_folder

Tree walk: from first_item_addr, recurse first_child then next_item siblings. Paths join ancestor names with /.

Metadata record (at a logical offset)

Offset Type Field
+0x00 u64 next_metadata_addr
+0x08 u32 category
+0x0c u32 key
+0x10 u32 data_length
+0x14 u8[data_length] data

Category 0x01 HASH_INFO (key 0x5001 MD5 — 32 ASCII hex; 0x5002 SHA1 — 40 ASCII hex); 0x05 TIMESTAMP (keys 0x07 access, 0x08 modified, 0x09 change; value YYYYMMDDThhmmss).

Chunk table (at item.zlib_metadata_addr) and data

u64 chunk_count
u64 address[0 ..= chunk_count]   // chunk_count + 1 entries

Chunk i compressed bytes occupy [address[i], address[i+1]); each is an independent zlib stream inflating to at most zlib_chunk_size bytes (the last is smaller). Decompressed byte b lives in chunk b / zlib_chunk_size, so read_at inflates only the chunks a range overlaps.

Hash verification (the auditor)

ad1-forensic recomputes MD5/SHA1 over a file's decompressed content and compares to the stored hex; a mismatch is AD1-HASH-MISMATCH.