Extract partition images from Android OTA payload.bin files
Project description
android-payload-dumper
A Python tool to extract partition images (.img) from Android OTA firmware payload.bin files.
Android OTA updates ship as a payload.bin file inside the firmware ZIP. This 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.
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
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.
Extract specific partitions only
payload-dumper payload.bin -p boot system vendor
Custom output directory
payload-dumper payload.bin -o extracted/
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
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] [-j WORKERS] [-V] payload
| Option | Description |
|---|---|
payload |
Path to the payload.bin file (required) |
-o, --output DIR |
Output directory for extracted images (default: output/) |
-p, --partitions NAME [NAME ...] |
Extract only the listed partitions |
-l, --list |
List all partitions in the payload and exit |
-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.1.0.tar.gz.
File metadata
- Download URL: android_payload_dumper-2.1.0.tar.gz
- Upload date:
- Size: 40.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9ac2a8c41be834af6b1d2db1f40bfd4abe0195e06de4464b360285f91d28c72e
|
|
| MD5 |
9eacd457b7005edbceb2525bc4180bd3
|
|
| BLAKE2b-256 |
28cfc521204c70df80a9aef0e62a42769589ce5dea8d7b05a957997c9428003b
|
Provenance
The following attestation bundles were made for android_payload_dumper-2.1.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.1.0.tar.gz -
Subject digest:
9ac2a8c41be834af6b1d2db1f40bfd4abe0195e06de4464b360285f91d28c72e - Sigstore transparency entry: 1351787907
- Sigstore integration time:
-
Permalink:
manojanasuri16/payload_dumper@f1f7e8bebcbced959a7ab695e7232e02482f30b1 -
Branch / Tag:
refs/tags/v2.1.0 - Owner: https://github.com/manojanasuri16
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@f1f7e8bebcbced959a7ab695e7232e02482f30b1 -
Trigger Event:
push
-
Statement type:
File details
Details for the file android_payload_dumper-2.1.0-py3-none-any.whl.
File metadata
- Download URL: android_payload_dumper-2.1.0-py3-none-any.whl
- Upload date:
- Size: 11.4 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 |
b1a88044777d28734a085c2d94ce577a8562c3662c94acaadd16c13a1a81d821
|
|
| MD5 |
9940a101a39c58b9a179f7191b60c9d8
|
|
| BLAKE2b-256 |
8a425a6f3d79315fe7d9bcb409f6fb6a90cb672c853633b98c845cdc24f1ce10
|
Provenance
The following attestation bundles were made for android_payload_dumper-2.1.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.1.0-py3-none-any.whl -
Subject digest:
b1a88044777d28734a085c2d94ce577a8562c3662c94acaadd16c13a1a81d821 - Sigstore transparency entry: 1351787968
- Sigstore integration time:
-
Permalink:
manojanasuri16/payload_dumper@f1f7e8bebcbced959a7ab695e7232e02482f30b1 -
Branch / Tag:
refs/tags/v2.1.0 - Owner: https://github.com/manojanasuri16
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@f1f7e8bebcbced959a7ab695e7232e02482f30b1 -
Trigger Event:
push
-
Statement type: