Handling Android Images

Dealing with .img, sparse images, ramdisks.

Unpack boot.img

Get android_bootimg_tools and extract to ~/.local/bin:

wget https://storage.googleapis.com/google-code-archive-downloads/v2/code.google.com/android-serialport-api/android_bootimg_tools.tar.gz /tmp/tools.tar.gz
tar xzvf /tmp/tools.tar.gz -C ~/.local/bin/

Make sure ~/.local/bin is in your PATH.

You will now have the mkbootimg and unpackbootimg programs available.

Copy the boot.img you want to disect into your local folder and run mkdir boot/ && unpackbootimg -i boot.img -o boot/

You will now have the following files inside the boot/ folder:

  • boot.img-base
  • boot.img-cmdline
  • boot.img-pagesize
  • boot.img-ramdisk.gz
  • boot.img-zImage

The boot.img-zImage file is the kernel.

Source: stackoverflow.

Unpack ramdisk

This is only necessary if you want to change files inside your boot.img. If you only want to use a different kernel, you can skip this step.

cd into boot/ and unpack the ramdisk:

gunzip --to-stdout --uncompress boot.img-ramdisk.gz | cpio --extract \
  --make-directories --no-absolute-filenames

To re-pack the cpio archive, move the rootfs files into a subdirectory and run the following from there:

find . | cpio -o -H newc > ../boot.img-ramdisk

The -H newc option creates the SV4 archive type.

Then go up one directory and gzip-compress the archive:

gzip boot.img-ramdisk

The ramdisk has a special format named cpio. More information on Wikipedia and the manpage.

The exact format of the cpio archive is “SV4 with no CRC”. You can verify the archive type for future Android versions by doing:

$ gunzip --uncompress boot.img-ramdisk.gz
$ file boot.img-ramdisk
# boot.img-ramdisk: ASCII cpio archive (SVR4 with no CRC)

Re-pack boot.img

If you unpacked your ramdisk, first re-create a cpio archive and gzip-compress it again, see the previous step.

Use mkbootimg with the appropriate flags. To find out the values for cmdline, base and pagesize, take a look at the generated boot.img-* files, e.g. boot.img-base should contain a number like 80000000.

mkbootimg \
  --cmdline "$(cat boot.img-cmdline)" \
  --base "$(cat boot.img-base)" \
  --pagesize "$(cat boot.img-pagesize)" \
  --ramdisk "boot.img-ramdisk.gz" \
  --kernel "boot.img-zImage" \
  -o boot-repacked.img

For newer devices, the bootloader expects you to set an OS version and a security patch level in the bootimage. These values should match the system or vendor image values. The patch level corresponds to the AOSP Security bulletins, e.g. 2020-06-05.

export OS_VERSION=10
export OS_PATCH_LEVEL='2020-06-05'
mkbootimg \
  --cmdline
  --id \
  [...] \
  --os_version $OS_VERSION \
  --os_patch_level $OS_PATCH_LEVEL
  -o boot-repacked.img

If you are constructing a boot image from scratch, you also need to set kernel tags offset and ramdisk offset, which can be taken from the device BoardConfig.mk or PlatformConfig.mk.

export BOARD_KERNEL_TAGS_OFFSET=0x01E00000
export BOARD_RAMDISK_OFFSET=0x02000000
mkbootimg \
  [...] \
  --ramdisk_offset $BOARD_RAMDISK_OFFSET \
  --tags_offset $BOARD_KERNEL_TAGS_OFFSET \
  -o boot-repacked.img

Here are some example values:

CMDLINE="androidboot.bootdevice=7464900.sdhci msm_rtb.filter=0x3F \
  ehci-hcd.park=3 coherent_pool=8M sched_enable_power_aware=1 \
  user_debug=31 cgroup.memory=nokmem printk.devkmsg=on \
  androidboot.hardware=kagura buildvariant=userdebug \
  androidboot.selinux=permissive"
BASE="80000000" 
PAGESIZE="4096"

The newly packged boot image will be named boot-repacked.img (or whatever name you chose for -o).

Gotchas

In case you compiled your own kernel, don’t forget to think about .dtb files! Many older devices (e.g. the Xperia XZ) need the Device Tree Binaries (dtb) appended to the kernel zImage.

Edit source on Github