Skip to main content

Command Palette

Search for a command to run...

What Are PAC Files? A Deep Dive into Unisoc's Firmware Container Format

Updated
8 min read
What Are PAC Files? A Deep Dive into Unisoc's Firmware Container Format

If you've ever flashed firmware on a Unisoc/Spreadtrum-based Android device, you've encountered a .pac file. But what exactly is it? Let's open the hood.


The Short Answer

A PAC file is a proprietary binary firmware container developed by Spreadtrum Communications (now Unisoc) — one of China's largest semiconductor companies, whose ARM-based SoCs power hundreds of millions of budget smartphones and embedded devices worldwide.

Think of a PAC file as a self-describing, all-in-one firmware deployment package: it bundles every image a device needs to boot (bootloaders, modem firmware, system partitions, NV data) along with the metadata that tells the flashing tool exactly how to deploy them.


What Do PAC Files Do?

In the embedded/mobile world, flashing firmware to a device isn't as simple as copying a file. You need to:

  1. Communicate with the device at the hardware level (usually UART at boot)

  2. Load a bootloader into RAM first, because the device's stock ROM is too minimal to handle flashing

  3. Erase specific flash partitions

  4. Write each firmware image to the correct address in memory or storage

  5. Verify the writes with checksums

  6. Reset the device to boot the new firmware

A PAC file orchestrates all of this. It is consumed by Spreadtrum's SPD Flash Tool (a Windows-based utility), which reads the PAC, follows its embedded instructions, and walks the device through the entire flashing sequence over USB or UART.

The PAC format replaces what would otherwise be a folder full of loose binaries plus a separate XML config — it makes distribution of a complete firmware package into a single atomic artifact.


The Anatomy of a PAC File

Here's where it gets interesting. A PAC file has a precise, tightly-packed binary layout with three major sections:

1. The PAC Header (2,124 bytes, always at offset 0)

The very first thing in every PAC file is a fixed-size header. It's a packed C structure (1-byte alignment, matching the Windows tool exactly) that contains:

Field Description
version Struct version string — often a device codename like "SharkL2"
product_name Human-readable product/device name
product_version Firmware version string
file_count Number of firmware images in this package
file_offset Byte offset to the image directory
mode Flash mode: Normal, NV Backup, or Factory
flash_type Target storage: NOR Flash, NAND Flash, or eMMC
nand_strategy Bad-block handling strategy (for NAND targets)
nand_page_type NAND page size (2K, 4K, 8K…)
magic Always 0xFFFAFFFA — identifies a valid PAC
crc1 CRC-16 over the header itself
crc2 CRC-16 over the entire file

Notably, all string fields are stored in UTF-16LE (because the original tooling was Windows-native), and all strings are fixed-width, null-padded arrays. The magic number 0xFFFAFFFA is the first thing any parser checks to confirm it's looking at a valid PAC.

2. The Image Directory — FILE_T Entries (2,580 bytes each)

Immediately after the header comes an array of file descriptor structs — one per firmware image. Each entry is exactly 2,580 bytes and describes a single embedded image:

Field Description
file_id Logical role: "FDL", "FDL2", "MODEM", "NV", "SYSTEM", etc.
file_name Original filename on disk
file_size Size of the actual image data in bytes
file_flag 1 = has real data; 0 = operation-only (no payload)
check_flag 1 = mandatory; 0 = optional
data_offset Byte offset from the PAC start to this image's raw data
addr[5] Up to 5 physical memory/flash addresses for downloading
omit_flag Whether this file can be skipped in "flash all" mode

The file_id field is the key: it identifies the logical role of each image. Typical images in a PAC include:

  • FDL — First Device Loader: a tiny bootloader sent to the device's RAM over UART, bootstrapping the flash process

  • FDL2 — Second-stage loader: initializes DDR, sets up the flash controller

  • MODEM — Cellular modem firmware (often the largest image)

  • NV — Non-Volatile data: device-specific calibration, IMEI, WiFi MAC addresses

  • BOOT — Linux/Android boot partition

  • SYSTEM — The Android system partition image

3. The Embedded XML Configuration

Tucked between the FILE_T array and the first raw image data is a variable-size XML block — and this is arguably the most interesting part of the format.

This XML is the "recipe" for the flash sequence. It describes, for each firmware image, the exact sequence of operations the flash tool must perform:

<BMAConfig>
  <ProductList>
    <Product name="MyDevice">
      <File id="FDL" type="FDL">
        <Block base="0x00000000" size="0x00002000" />
        <Operation type="CheckBaud" />
        <Operation type="Connect" />
        <Operation type="Download" />
        <Operation type="Reset" />
      </File>
      <File id="MODEM" type="CODE">
        <Block base="0x40000000" size="0x04000000" />
        <Operation type="EraseFlash" />
        <Operation type="Download" />
        <Operation type="Verify" />
      </File>
    </Product>
  </ProductList>
</BMAConfig>

Operation types include:

  • CheckBaud — Verify UART communication speed

  • Connect — Initialize the device handshake

  • Download — Write the image to the target address

  • EraseFlash — Erase the destination region first

  • Verify — Checksum the written data

  • Reset / ResetToNormal — Reboot the device

The XML schema has several variants across different device generations — a real-world parser has to handle BMAConfig → ProductList → Product, bare Product nodes, scheme-based structures, and case-insensitive attribute names. PAC parsing in the wild is defensive by necessity.

4. The Raw Image Data

The remainder of the file is the actual binary payloads: bootloaders, partition images, modem blobs. They're stored uncompressed, back-to-back, at the offsets specified by each FILE_T entry. No compression, no encryption (in most cases) — just raw binary data.


Integrity: CRC-16 Checksums

PAC files use CRC-16 (polynomial 0x8005) for integrity validation:

  • CRC1: Covers the header (excluding the CRC fields themselves) — lets a tool quickly validate the header is uncorrupted without reading the whole file

  • CRC2: Covers the entire PAC file — full package integrity check

The magic + dual-CRC design means a corrupted or truncated PAC is detected before flashing begins — important when a bad flash can brick a device.


The Boot Flow, Visualized

When the SPD Flash Tool processes a PAC, the sequence looks like this:

ROM Boot (on-device)
    → CheckBaud / Connect (UART handshake)
    → Download FDL (bootloader loaded into RAM)
    → FDL executes (initializes DDR, flash controller)
    → Download FDL2 (second-stage loader)
    → EraseFlash + Download MODEM/SYSTEM/BOOT (partition images written)
    → Download NV (device-specific data preserved or overwritten)
    → Reset (device boots new firmware)

The entire sequence — including what gets flashed, in what order, at which memory addresses — is driven by that embedded XML. The binary structures tell you what data, and the XML tells you what to do with it.


Why This Format Exists

PAC was designed for a practical constraint: a single USB/UART connection to a device that has no OS running. You can't scp files or use ADB — you're communicating with a ROM-level bootloader that speaks a proprietary protocol. The PAC format lets the flash tool have everything it needs in one place: the bootstrap loaders, the data, and the step-by-step instructions.

It also solves the distribution problem: instead of shipping a folder of 10 loose .bin files with a separate XML and a README, Unisoc/OEMs ship one .pac file that is entirely self-contained.


Open-Source Tools

The PAC format is undocumented publicly, but the binary layout has been reverse-engineered by the community. Tools like upac (a CLI written in modern C++) let you inspect, extract, and verify PAC files without needing Windows or the official SPD Flash Tool:

upac info firmware.pac      # Show header, product name, version, CRCs
upac list firmware.pac      # List all embedded images
upac extract firmware.pac   # Extract all images to disk
upac xml firmware.pac       # Dump the embedded XML configuration
upac ops firmware.pac       # Show per-file operation sequences
upac verify firmware.pac    # Validate CRC-16 integrity

Key Takeaways

  • PAC = Firmware container for Unisoc/Spreadtrum devices: one file bundles all images, metadata, and flash instructions

  • Binary header (2,124 bytes) describes the package; FILE_T entries (2,580 bytes each) describe each embedded image

  • Embedded XML provides the operation recipe — what to do, in what order, at which memory addresses

  • CRC-16 dual checksums protect header and full-file integrity

  • All strings are UTF-16LE encoded (Windows legacy), stored in fixed-size null-padded arrays

  • Flash modes support Normal, NV Backup (preserves IMEI/calibration), and Factory (full wipe)


Have you worked with PAC files, SPD Flash Tool, or other firmware formats? Drop a comment — there's a lot of ground to cover in the Unisoc ecosystem.