Introduction: The Rise of Unified Images in Android
The concept of a Unified Kernel Image (UKI), popularized by projects like systemd-boot, consolidates the Linux kernel, initial ramdisk (initramfs), kernel command line, and sometimes the Device Tree Blob (DTB) into a single executable. While Android doesn’t strictly adhere to the systemd-boot UKI specification, its boot image formats have consistently aimed at unifying kernel and ramdisk components. Modern Android devices, especially those leveraging Generic Kernel Images (GKI) and A/B updates, further abstract the boot process, making component extraction a crucial skill for advanced customization, debugging, and security research.
This guide delves into the methodologies for reverse engineering these ‘unified’ Android boot images, focusing on extracting the core kernel and ramdisk components. Understanding this process is fundamental for anyone looking to build custom recoveries, flash custom kernels, or analyze low-level system behavior.
Understanding Android Boot Image Evolution
Historically, Android’s boot.img served as the primary unified image, containing the kernel, ramdisk, and an optional second stage bootloader. With Android 9 (Project Treble) and especially Android 11+ (GKI), the landscape shifted:
- Separate Vendor Boot Image: The
vendor_boot.imgnow encapsulates vendor-specific ramdisk components and the DTB. - Generic Kernel Image (GKI): The kernel itself often comes as a pre-built, standardized image, potentially bundled with its own DTB.
- Firmware Payloads: For A/B update devices, entire firmware packages are often distributed as a
payload.bin, containing numerous partitions, including the various boot-related images.
Our objective is to extract the kernel (often Image.gz-dtb or zImage) and the root filesystem (ramdisk.cpio.gz) from these consolidated or contained images, regardless of their specific Android boot image format.
Tools of the Trade
To successfully reverse engineer these images, you’ll need a Linux environment and the following tools:
binwalk: A fast, easy-to-use tool for analyzing, reverse engineering, and extracting firmware images.dd: A command-line utility for converting and copying files, invaluable for precise extraction by offset.hexdumporxxd: For viewing file contents in hexadecimal and ASCII, crucial for identifying magic numbers.magiskboot: A powerful utility from the Magisk project, capable of unpacking various Android boot image formats.cpioandgzip: For extracting and compressing ramdisks.payload-dumper-go(or similar): For extracting partitions from A/Bpayload.binarchives.
Step 1: Acquiring the Target Image
The first step is to obtain the relevant boot image. This could be:
- A standalone
boot.imgorvendor_boot.imgfrom a factory image. - An image extracted from a firmware archive (e.g., a
payload.binfrom an OTA update).
Extracting from payload.bin
If your firmware comes as a payload.bin, you’ll need to extract the individual partition images first. payload-dumper-go is an excellent tool for this:
./payload-dumper-go -p boot payload.bin
This command would extract the boot.img (and other specified partitions) into a subdirectory. If you need vendor_boot.img, replace boot with vendor_boot.
Step 2: Initial Analysis with binwalk
Once you have your target image (e.g., boot.img or vendor_boot.img), binwalk is your best friend for a quick overview. It can identify embedded filesystems, compressed data (like the kernel), and CPIO archives (the ramdisk).
binwalk -e boot.img
The -e flag attempts to extract known file types. A typical output for an Android boot image might look like this:
DECIMAL HEXADECIMAL DESCRIPTION--------------------------------------------------------------------------------0 0x0 Android boot image header0x400000 0x400000 Linux kernel ARM64 EFI executable (little-endian)0x400000 0x400000 gzip compressed data, maximum compression, original size unknown0x6000000 0x6000000 CPIO archive (SVR4 with no CRC)
From this output, we can deduce:
- The boot image header is at offset
0x0. - The compressed kernel (often
Image.gzorImage.gz-dtb) starts around0x400000. - The CPIO ramdisk starts around
0x6000000.
Note these offsets; they will be crucial for manual extraction.
Step 3: Dissecting the Image Manually (Offset-based Extraction)
Even if binwalk -e doesn’t fully extract everything, or if you prefer a more hands-on approach, manual extraction using dd is robust.
Identifying Kernel and Ramdisk Magic Numbers
Before using dd, it’s helpful to confirm the presence of kernel and ramdisk by their magic numbers (signatures) at their suspected offsets. For ARM64 kernels, the `zImage` or `Image.gz-dtb` often starts with `0x01646401` or `0x01666401` (little-endian, so `01 64 64 01` or `01 66 64 01` in hex dump). A CPIO archive typically begins with `0x070701` (`07 07 01 00` for ASCII newc or `0x71C7` for bin`).
Use hexdump to peek at the bytes:
hexdump -C boot.img | less
Scroll to the binwalk-identified offsets to verify.
Extracting the Kernel
Assuming the kernel starts at 0x400000 and its size is known (or estimated), we can use dd. You might need to experiment with count (block size * number of blocks) to get the full kernel without extra data. A safe starting point is to extract generously and then trim. For example, if the kernel is roughly 16-30MB:
dd if=boot.img of=kernel.gz bs=1 skip=$((0x400000)) count=$((20*1024*1024)) # 20MB extraction
If the kernel is compressed, you’ll then decompress it:
mv kernel.gz kernel.img.gzgunzip kernel.img.gz
The result, kernel.img, will be your uncompressed kernel image, potentially containing an embedded DTB.
Extracting the Ramdisk
The ramdisk is typically a gzipped CPIO archive. Using the offset from binwalk (e.g., 0x6000000) and an estimated size (typically 4-64MB for a gzipped CPIO):
dd if=boot.img of=ramdisk.cpio.gz bs=1 skip=$((0x6000000)) count=$((30*1024*1024)) # 30MB extraction
Now, decompress and extract the CPIO archive:
gunzip ramdisk.cpio.gzmkdir ramdisk_extractedcd ramdisk_extractedcpio -idm < ../ramdisk.cpio
You will now have the full root filesystem of the ramdisk in the ramdisk_extracted directory.
Step 4: Advanced Extraction with magiskboot
For Android boot images following standard formats (like boot.img and vendor_boot.img), magiskboot offers a robust and often simpler automated extraction method. It intelligently parses the boot image header and extracts components without requiring manual offset calculations.
magiskboot unpack boot.img
This command will typically create files such as:
kernel: The raw kernel image.ramdisk.cpio: The raw CPIO ramdisk archive.dtb: The Device Tree Blob (if separate).header: The boot image header information.recovery_dtbo: If a separate recovery DTBO partition exists.
If you’re dealing with a vendor_boot.img:
magiskboot unpack vendor_boot.img
This will yield vendor_ramdisk.cpio and dtb (if embedded).
Step 5: Analyzing Extracted Components
Analyzing the Kernel
Once you have the uncompressed kernel image (e.g., kernel.img), you can gather more information:
file kernel.imgstrings kernel.img | grep
Android Mobile Specs & Compare Directory
Are you researching mobile hardware properties, processor SoCs, GPU chipsets, or RAM configurations? Access our complete specs catalog to compare up to 5 devices side-by-side!
Compare Devices Specs →