English | 中文
A Rust toolchain for unpacking, editing, repacking, and signing MediaTek (MTK) boot logo images. Exploits the CERT2 certificate verification vulnerability (CVE-2023-20696 / CVE-2025-20730) to bypass signature checks on the logo partition, allowing custom boot logos to be flashed onto supported MTK devices.
- Unpack — Extract all logo chunks from a
logo.binimage to PNG files (known resolutions) or.rgbafiles (unknown resolutions) - Pack — Rebuild a logo image from edited PNG/BMP/JPEG/GIF/TGA/PNM/WebP files with automatic resolution fitting
- Install — Inject a fake CERT2 certificate to bypass signature verification (legacy & modern modes)
- Build — Pack + install in one step, producing a ready-to-flash image
- Verify — Auto-detect and verify the injected certificate and hash values
| Mode | CVE | Flag | Mechanism |
|---|---|---|---|
| Legacy (default) | CVE-2023-20696 | (none) | BIT_STRING(original_DER) + 0xA0(hash_override) prepended before CERT2 blob. Old libsec (bypass_mode=1) enters the BIT STRING payload to find the original 0x30 SEQUENCE. |
| Modern | CVE-2025-20730 | --modern |
0xA0(hash_override) prepended as a top-level sibling before the original CERT2 DER. New libsec reads the override block directly. |
Both modes are idempotent — re-running install on an already-signed image strips the previous injection and re-injects cleanly.
| Input (pack) | Output (unpack) |
|---|---|
| PNG, JPEG, BMP, GIF, TGA, PNM, WebP, RGBA | PNG (known resolution), .rgba (unknown) |
All pixel data is processed as BGRA32 (4 bytes/pixel), matching the format used by modern MTK devices. Input images of any resolution are automatically center-cropped and/or padded with opaque black to fit the target chunk size — no scaling.
cargo build --releaseThe binary will be at target/release/mtk-logo-toolkit.
Extract all chunks from a logo image:
mtk-logo-toolkit unpack logo.bin -o output_dir/Files are named NN_WxH.png (e.g. 14_57x64.png) where NN is the chunk index.
Rebuild a logo image from edited chunks:
mtk-logo-toolkit pack output_dir/ logo.bin -o logo.packed.binOnly files matching NN_*.ext are used as replacements. Chunks without a replacement file are kept verbatim from the template.
Inject a fake certificate (legacy mode by default):
mtk-logo-toolkit install logo.bin -o logo.signed.binModern mode:
mtk-logo-toolkit install logo.bin --modern -o logo.signed.binPack + install in a single command:
mtk-logo-toolkit build output_dir/ logo.bin -o logo.signed.binWith modern bypass:
mtk-logo-toolkit build output_dir/ logo.bin --modern -o logo.signed.binAuto-detect and verify the injected certificate:
mtk-logo-toolkit verify logo.signed.binsrc/
├── main.rs — CLI entry point (clap derive)
├── part_header.rs — part_hdr_t binary format parsing & rebuilding
├── asn1_der.rs — ASN.1 DER TLV encoding/decoding (pure Rust)
├── image_codec.rs — Image loading, PNG writing, BGRA↔RGBA, zlib, fit
├── logo_payload.rs — Logo payload unpack/pack operations
└── cert_inject.rs — CERT2 fake certificate injection & verification
A logo.bin file is a linear chain of part_hdr_t partitions. Each partition starts with a 512-byte header (magic 0x58881688) followed by its data, padded to the alignment boundary. A typical image contains three partitions:
| Partition | Content | img_type |
|---|---|---|
logo |
zlib-compressed logo chunks (BGRA32 pixel data) | 0x00000000 |
cert1 |
ASN.1 DER certificate blob (RSA-PSS signature) | 0x02000000 |
cert2 |
ASN.1 DER certificate blob (image hashes + signature) | 0x02000002 |
The logo partition's data has this internal layout:
[count: u32] [total: u32] [offsets: u32 × count] [zlib BGRA blocks...]
count(u32 LE) — number of logo chunkstotal(u32 LE) — total payload size (header + all chunk data)offsets[N](u32 LE each) — byte offset of each chunk within the payloadchunk[i]— a zlib stream that decompresses toW × H × 4bytes of BGRA pixel data
All pixel data is stored in BGRA32 format (4 bytes/pixel): Blue, Green, Red, Alpha — each 8 bits. This is the native format used by modern MTK bootloaders (MT6765 and newer).
Note: Older MTK devices (MT6580/MT6739 and earlier) use RGB565 (2 bytes/pixel, R5G6B5). This tool does not currently support RGB565.
Each logo chunk is independently compressed with zlib deflate at level 9 (best compression), matching the standard MTK build pipeline (zpipe -l 9). Logo images are highly compressible due to solid-color backgrounds and simple graphics — typical compression ratios are under 2%.
When an input image does not match the target chunk resolution, it is adapted using center-crop + black-pad (no scaling):
- Decode the source image to RGBA
- If larger than target: center-crop to
target_w × target_h - If smaller than target: center on a
target_w × target_hcanvas, padded with opaque black (B=0, G=0, R=0, A=255) - Convert RGBA → BGRA and compress
This guarantees every chunk's decompressed size is exactly target_w × target_h × 4.
The CERT2 blob is an ASN.1 DER-encoded certificate containing image header hash and image content hash fields (identified by OIDs 2.16.886.2454.2.4 and 2.16.886.2454.2.1). The injection prepends a crafted 0xA0 block containing OID + BIT STRING pairs with recomputed hashes for the current image content, causing the preloader's DER parser to read the injected hashes instead of the original ones.
| Bytes | Resolution |
|---|---|
| 18,432,000 | 1440×3200 |
| 17,971,200 | 1440×3120 |
| 17,510,400 | 1440×3040 |
| 17,049,600 | 1440×2960 |
| 16,588,800 | 1440×2880 |
| 14,745,600 | 1440×2560 |
| 13,234,560 | 1220x2712 |
| 10,886,400 | 1080×2520 |
| 10,627,200 | 1080×2460 |
| 10,402,560 | 1080x2408 |
| 10,368,000 | 1080×2400 |
| 10,108,800 | 1080×2340 |
| 9,936,000 | 1080x2300 |
| 9,849,600 | 1080×2280 |
| 9,331,200 | 1080×2160 |
| 8,294,400 | 1080×1920 |
| 4,608,000 | 720×1600 |
| 4,492,800 | 720×1560 |
| 4,377,600 | 720×1520 |
| 4,147,200 | 720×1440 |
| 3,686,400 | 720×1280 |
| 249,200 | 178×350 |
| 49,444 | 263×47 |
| 36,864 | 72×128 |
| 15,984 | 54×74 |
| 14,592 | 57×64 |
| 11,520 | 45×64 |
| 7,056 | 42×42 |
| 2,104 | 263×2 |
The table lists decompressed byte counts (BGRA32) and their corresponding dimensions. Image chunk purpose is determined by position/index within logo.bin, not by resolution.
- pwnage24mtk — CERT2 injection logic inspiration (independent Rust rewrite)
- cannon_logo_maker — Logo unpack/pack pipeline (same author, predecessor Python project)
See NOTICE for details.
Copyright (C) 2025 ELF-RC. Licensed under the GNU General Public License v3.0 or later. See COPYING for the full license text.