logo
Kurashizu Blogwhere ideas flow
  • Home
  • News
  • Blog
  • About
  • Home
  • News
  • Blog
  • About

© 2026 Kurashizu. All rights reserved.

Admin·New Post·Service Status
Powered by Cloudflare·03.11.26
← Back to news

Dissecting Apple's Sparse Image Format (ASIF)

Original article
schamper.dev|142 points|21 comments|by supermatou|Jun 28, 2026

Analyzing Apple's Sparse Image Format (ASIF)

Originally published by Erik Schamper on schamper.dev Date: 2026-06-18 | Reading Time: ≈18\approx 18≈18 minutes

During WWDC 2025, Apple unveiled macOS 26 Tahoe, which introduced a novel disk image format known as ASIF.

Integrated primarily within the Virtualization framework, ASIF draws heavy influence from established virtual disk standards. In essence, it is a sparse virtual disk format, operating similarly to QCOW2, VHDX, or sparse VMDK files. For those unfamiliar, "sparse" means the file can represent a massive virtual disk while only consuming physical space for the data actually written to it.

Shortly before Tahoe's official launch in late 2025, I decided to challenge myself by building a parser for ASIF. While some time has passed, I want to document my methodology for tackling these problems, as it may provide a roadmap for those new to the art of reverse engineering file formats.

Throughout this guide, I have included specific Research Notes to provide deeper context.


🛠️ Phase 1: Environment Setup & Test Data

To begin, we need a controlled sample. I'll use the official Apple documentation to generate a file, then populate it with a predictable pattern.

Research Note: When testing, I prefer writing a pattern where the content is tied to the offset. While there are more sophisticated patterns, simply filling the file with verifiable data makes the subsequent analysis much smoother.

Step-by-Step Execution

  • Create a 1GiB blank ASIF image.
  • Attach the image to a device node.
  • Write a sequential byte pattern using Python.
  • Detach the image.
# Create the sparse image
❯ diskutil image create blank --fs none --format ASIF --size 1GiB file
file.asif created

# Attach the image without mounting the filesystem
❯ diskutil image attach -nomount file.asif /dev/disk4

Now, I'll use a Python script to write 1MB chunks of incrementing bytes (0-255) to the disk:

# Python 3.14.0 (main, Oct 7 2025) on darwin
fh = open("/dev/disk4", "wb")
for i in range(255):
    # Write 1MB of the current byte value
    fh.write(bytes([i] * 1024 * 1024))
fh.close()
# Clean up
❯ hdiutil detach disk4
"disk4" ejected.

🔍 Phase 2: Initial Heuristic Analysis

The first step in any reverse engineering task is "eyeballing" the hexdump to find patterns.

❯ xxd file.asif | head -5
0000 0000: 73 68 64 77 00 00 00 01 00 00 02 00 00 00 00 00  shdw....
0000 0010: 00 00 00 00 00 00 02 00 00 00 00 00 00 04 14 00  ........
0000 0020: 8a f9 ea d2 cf 38 49 c0 8e ec 00 95 cf 5c 78 99  ....8I...\x
0000 0030: 00 00 00 00 00 1d cd 65 00 00 08 00 00 00 00 00  .....e....
0000 0040: 00 10 00 00 02 00 00 00 00 00 00 00 ff ff ff ff  ........

Immediately, we see a "magic" string (shdw) followed by what appear to be big-endian integers.

Research Note:

  1. Magic Bytes: Always search for these strings/hex values on Google, GitHub, or VirusTotal Retrohunt to see if the format is already documented.
  2. Endianness: Identifying integer widths is like riding a bike. Scan in 4-byte (uint32) or 8-byte (uint64) chunks. Look for "round" hex numbers or values that correlate with file offsets or known sizes.

The Reverse Engineering Workflow


📐 Phase 3: Defining the Structure

I'll use dissect.cstruct to create a preliminary hypothesis of the header layout.

# /// script
# requires-python = ">=3.10"
# dependencies = ["dissect.cstruct"]
# ///
import sys
from dissect.cstruct import cstruct, dumpstruct

asif_def = """
struct header {
    char magic[4];
    uint32 field4;
    uint32 field8;
    uint32 fieldC;
    uint64 field10;
    uint64 field18;
    char field20[16];
    uint64 field30;
    uint64 field38;
    uint32 field40;
    uint32 field44;
    uint32 field48;
    uint32 field4C;
};
"""
c_asif = cstruct(asif_def, endian=">") # Big Endian
with open(sys.argv[1], "rb") as fh:
    header = c_asif.header(fh)
    dumpstruct(header)

Header Analysis Results

FieldOffsetSizeValueInterpretation
magic[4]0x00004 bytesb'shdw'File Signature
field40x00044 bytes0x1Unknown
field80x00084 bytes0x200Sector Size (512B)
fieldC0x000c4 bytes0x0Unknown
field100x00108 bytes0x200Possible Offset
field180x00188 bytes0x41400Possible Offset
field20[16]0x002016 bytes\x8a\xf9...Random/Hash?
field300x00308 bytes0x1dcd65Virtual Size?
field380x00388 bytes0x80000000000Unknown
field400x00404 bytes0x100000Unknown
field440x00444 bytes0x2000000Unknown
field480x00484 bytes0x0Unknown
field4C0x004c4 bytes0xffffffffEnd Marker?

Key Observations

We see 0x200 appearing twice, which strongly suggests a sector size of 512 bytes.

Regarding the total size, we can perform a simple calculation: Virtual Disk Size (Sectors)=1 GiB512 bytes=0x200000\text{Virtual Disk Size (Sectors)} = \frac{1\text{ GiB}}{512\text{ bytes}} = 0x200000Virtual Disk Size (Sectors)=512 bytes1 GiB​=0x200000

While field30 in my specific dump showed 0x1dcd65, the author notes that this field likely represents the virtual disk size in sectors. If field8 is indeed the sector size, then field10 and field18 are likely pointers to other sections of the file.

Header Diagram Placeholder

To further verify these assumptions, I'll expand the hexdump analysis:

❯ xxd -a file.asif | head -32

(Analysis continues...)