Extract partition images from Android OTA payload.bin files — locally, from a URL, or straight from an OTA .zip
Project description
android-payload-dumper
A Python tool to extract partition images (.img) from Android OTA firmware payload.bin files — locally, from a URL without a full download, or straight from an OTA .zip without unpacking it first.
Android OTA updates ship as a payload.bin inside a firmware ZIP. That file contains compressed partition images (system, boot, vendor, etc.) packed in Google's Chrome OS update format (CrAU). This tool parses the payload, decompresses each partition in parallel, verifies integrity via SHA-256 hashes, and writes individual .img files that can be flashed or mounted.
Highlights:
- Stream from a URL with HTTP range requests — extract
boot.imgout of a 4 GB OTA without downloading the whole thing. - Read straight from OTA
.zip— nounzipstep required; operates on the storedpayload.binmember in place. - Parallel extraction across partitions with a live per-partition progress UI.
- Pipe to stdout with
-o -for composition withsimg2img,fastboot, and other tools. --jsonoutput for scripting and CI integration.
Prerequisites
Installation
As a global CLI (recommended)
pipx install android-payload-dumper
This puts payload-dumper on your PATH so you can run it from anywhere without activating a venv.
Install from this repo (for development)
git clone https://github.com/manojanasuri16/payload_dumper.git
cd payload_dumper
uv sync
Then run via uv run payload-dumper … (shown in examples below).
Pip alternative:
git clone https://github.com/manojanasuri16/payload_dumper.git
cd payload_dumper
pip install -e .
Install straight from GitHub with pipx
pipx install git+https://github.com/manojanasuri16/payload_dumper.git
Useful if you want the latest main without waiting for a PyPI release.
Getting payload.bin
- Download the full OTA firmware ZIP for your device (from the manufacturer's website, or sites like Full OTA Images for Pixel)
- Extract the ZIP — you'll find
payload.bininside - Copy
payload.bininto thepayload_dumperdirectory (or provide the full path)
Usage
Examples below assume you installed with
pipxandpayload-dumperis on your PATH. If you're running from a clone, prefix every command withuv run(e.g.uv run payload-dumper payload.bin).
Extract all partitions
payload-dumper payload.bin # local payload.bin
payload-dumper firmware_ota.zip # reads payload.bin out of the OTA zip
payload-dumper https://dl.example.com/device-ota.zip # streams from URL
All .img files are saved to the output/ directory. Partitions are extracted in parallel (up to 8 workers by default), with a live progress bar per partition and an overall total.
For URLs, the server must support HTTP range requests (nearly every OEM mirror and CDN does) — only the exact bytes needed per operation are fetched, so extracting just boot.img from a 4 GB remote OTA downloads on the order of 100 MB.
Extract specific partitions only
payload-dumper payload.bin -p boot system vendor
Custom output directory
payload-dumper payload.bin -o extracted/
Stream a single partition to stdout
Use -o - to pipe one partition's image into another tool without touching disk. Requires exactly one partition via -p; progress and banners go to stderr so stdout stays pure image bytes.
# Flash boot directly from a remote OTA
payload-dumper https://dl.example.com/ota.zip -p boot -o - | fastboot flash boot -
# Convert a sparse system image on the fly
payload-dumper payload.bin -p system -o - | simg2img - system.raw
Control parallelism
payload-dumper payload.bin -j 4 # 4 workers
payload-dumper payload.bin -j 1 # serial extraction
List partitions without extracting
payload-dumper payload.bin -l
payload-dumper payload.bin -l --json # machine-readable output for scripts/CI
Example output:
payload minor_version=0 block_size=4096 partitions=35
35 partitions
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━┓
┃ Partition ┃ Ops ┃ Types ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━╇━━━━━━━━━━━━━━━━━━━┩
│ boot │ 85 │ REPLACE_XZ │
│ dtbo │ 12 │ REPLACE_XZ │
│ modem │ 86 │ REPLACE_XZ │
│ system │ 384 │ REPLACE_XZ, ZERO │
│ vendor │ 205 │ REPLACE_XZ │
│ … │ … │ … │
└────────────────────────────┴─────┴───────────────────┘
Command Reference
payload-dumper [-h] [-o OUTPUT] [-p NAME ...] [-l] [--json] [-j WORKERS] [-V] payload
| Option | Description |
|---|---|
payload |
Path or URL to payload.bin or an OTA .zip containing it (required) |
-o, --output DIR |
Output directory for extracted images (default: output/). Use - to stream a single partition to stdout (requires -p NAME). |
-p, --partitions NAME [NAME ...] |
Extract only the listed partitions |
-l, --list |
List all partitions in the payload and exit |
--json |
With --list, emit JSON to stdout instead of a table |
-j, --workers N |
Parallel extraction workers (default: min(8, cpu_count)) |
-V, --version |
Print version and exit |
-h, --help |
Show help message |
Also runnable as a module: python -m payload_dumper payload.bin.
Supported Compression Formats
| Operation | Compression | Description |
|---|---|---|
REPLACE |
None | Raw uncompressed data |
REPLACE_BZ |
bzip2 | Bzip2 compressed data |
REPLACE_XZ |
XZ / LZMA | XZ or raw LZMA compressed data (with automatic format detection) |
ZERO |
None | Zero-filled blocks |
DISCARD |
None | Discarded/trimmed blocks |
How It Works
- Header parsing — Reads and validates the
CrAUmagic bytes, payload format version (v1/v2), manifest size, and metadata signature - Manifest deserialization — Uses Protocol Buffers to decode the
DeltaArchiveManifest, which describes all partitions, their operations, and hash info - Operation validation — Checks that all operations are supported up-front (full OTA only — no delta/incremental ops like
SOURCE_COPYorBSDIFF) so a failure doesn't leave half-written images on disk - Parallel extraction — Each partition is handed to a worker thread that opens its own file handle, iterates the ops in order, decompresses and writes, and updates a shared Rich progress bar
- Integrity verification — Each operation's compressed bytes are verified against its SHA-256 hash, and the final partition image is verified against the expected partition hash
Payload Binary Format
+---------------------+
| Magic: "CrAU" | 4 bytes
| Format Version | 8 bytes (uint64, big-endian)
| Manifest Size | 8 bytes (uint64, big-endian)
| Metadata Sig Size | 4 bytes (uint32, big-endian, v2 only)
| Manifest (protobuf) | <manifest_size> bytes
| Metadata Signature | <metadata_sig_size> bytes
| Data Blobs | Remaining bytes (compressed partition data)
+---------------------+
Limitations
- Full OTA only — Incremental/delta OTA payloads (containing
SOURCE_COPY,SOURCE_BSDIFF,PUFFDIFF,BROTLI_BSDIFF, etc.) are not supported. These require the previous version's partition images to apply diffs against. - No streaming — The entire payload file must be accessible on disk (no stdin/pipe support).
Troubleshooting
ModuleNotFoundError: No module named 'google' or 'rich'
Install (or reinstall) the package so its dependencies come with it:
pipx install android-payload-dumper # or: pipx upgrade android-payload-dumper
uv sync # if working from a clone
pip install -e . # pip-from-clone alternative
_lzma.LZMAError: Input format not supported by decoder
This was a bug in older versions. The current version automatically tries multiple LZMA formats (XZ container, LZMA alone, raw LZMA2) as fallbacks. Make sure you're using the latest version.
unsupported op error
Your payload is an incremental (delta) OTA, not a full OTA. This tool only supports full OTA payloads. Download the full OTA image for your device instead.
Regenerating Protobuf Bindings
If you modify update_metadata.proto, regenerate the Python bindings into the package:
uv add --dev grpcio-tools
uv run python -m grpc_tools.protoc \
--python_out=src/payload_dumper \
--proto_path=. \
update_metadata.proto
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file android_payload_dumper-2.3.0.tar.gz.
File metadata
- Download URL: android_payload_dumper-2.3.0.tar.gz
- Upload date:
- Size: 46.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a0bcc74e9a9181957128b076a8722208ee61a51460fd165cce68ac432c19f115
|
|
| MD5 |
ac71fa0652b58d38469c93c32850ed8f
|
|
| BLAKE2b-256 |
82f6923ff998d0d71a645148534732a8ae9bc1c9b3c9006c53ab96975354e8b5
|
Provenance
The following attestation bundles were made for android_payload_dumper-2.3.0.tar.gz:
Publisher:
release.yml on manojanasuri16/payload_dumper
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
android_payload_dumper-2.3.0.tar.gz -
Subject digest:
a0bcc74e9a9181957128b076a8722208ee61a51460fd165cce68ac432c19f115 - Sigstore transparency entry: 1356577173
- Sigstore integration time:
-
Permalink:
manojanasuri16/payload_dumper@a9eacbfe4e924f8afa09af6056e0c7d74b9c92b7 -
Branch / Tag:
refs/tags/v2.3.0 - Owner: https://github.com/manojanasuri16
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@a9eacbfe4e924f8afa09af6056e0c7d74b9c92b7 -
Trigger Event:
push
-
Statement type:
File details
Details for the file android_payload_dumper-2.3.0-py3-none-any.whl.
File metadata
- Download URL: android_payload_dumper-2.3.0-py3-none-any.whl
- Upload date:
- Size: 16.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6157fd296668ad0856e1a5d4ffb4bcc5799a32d60861fdd0b021e52cd3e82911
|
|
| MD5 |
031ab194a4219daebfd5c4341b933c06
|
|
| BLAKE2b-256 |
bed13bf2c61391badbb69a4a62c5c06227a13d475a9820bd2d0571bfabfe76dd
|
Provenance
The following attestation bundles were made for android_payload_dumper-2.3.0-py3-none-any.whl:
Publisher:
release.yml on manojanasuri16/payload_dumper
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
android_payload_dumper-2.3.0-py3-none-any.whl -
Subject digest:
6157fd296668ad0856e1a5d4ffb4bcc5799a32d60861fdd0b021e52cd3e82911 - Sigstore transparency entry: 1356577260
- Sigstore integration time:
-
Permalink:
manojanasuri16/payload_dumper@a9eacbfe4e924f8afa09af6056e0c7d74b9c92b7 -
Branch / Tag:
refs/tags/v2.3.0 - Owner: https://github.com/manojanasuri16
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@a9eacbfe4e924f8afa09af6056e0c7d74b9c92b7 -
Trigger Event:
push
-
Statement type: