RK3288 Android 系统 boot.img 镜像解压方法

android 6.0 boot.img 解包

file

file指令可以显示文件类型,但是对于特定封装的文件无能为力,比如Android系统编译出的boot.img

$ file boot.img
boot.img: data

binwalk

binwalk 是一款非常强大的用于解析 bin 文件格式工具,可以通过扫描 bin 文件分析出文件的封装格式,列出 bin 文件中包含的不同类型的文件以及文件在bin中的偏移量。

  • 安装 binwalk
# 128M disk space will be used
sudo apt install binwalk

binwalk 可以用来分析 Android6boot.img.

$ binwalk boot.img 

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
8             0x8             gzip compressed data, from Unix, NULL date (1970-01-01 00:00:00)

从以上输出可知,boot.img实际上只有一个gzip压缩文件,但是有 8 个字节的偏移量。

通过 hexdump 指令可知这8个字节其实就是 KRNLT 字符串,对应Kernel.

$ hexdump -C boot.img|head -1
00000000  4b 52 4e 4c 54 9f 0f 00  1f 8b 08 00 00 00 00 00  |KRNLT...........|

dd

使用 binwalk 得知了 boot.img 格式,接下来就可以用 dd 去截取里面的 gzip 文件,当然要注意偏移量 8 个字节。

$ dd if=boot.img of=ramdisk.gz bs=1 skip=8
1023832+0 records in
1023832+0 records out
1023832 bytes (1.0 MB, 1000 KiB) copied, 0.798888 s, 1.3 MB/s

说明: 除了dd 外,也可以是用 binwalk -e boot.img 提取文件,解压后的文件以偏移量命名。

gunzip

此时得到的 ramdisk.gz 就是一个 gzip 压缩文件。

$ file ramdisk.gz 
ramdisk.gz: gzip compressed data, from Unix

接下来使用 gunzip 进行解压。

$ gunzip ramdisk.gz 

gzip: ramdisk.gz: decompression OK, trailing garbage ignored
$ ls
boot.img ramdisk

cpio

解压后的 ramdisk 是一个 cpio 压缩文件。

$ file ramdisk 
ramdisk: ASCII cpio archive (SVR4 with no CRC)

所以需要进一步解压。

$ mkdir tmp && cd tmp
$ cpio -i < ../ramdisk 
3586 blocks

至此 boot.img 中的ramdisk就完全解压出来了。

$ ls
charger                           init.rk30board.bootmode.emmc.rc     property_contexts
data                              init.rk30board.bootmode.unknown.rc  res
default.prop                      init.rk30board.environment.rc       rk30xxnand_ko.ko
dev                               init.rk30board.rc                   sbin
drmboot.ko                        init.rk30board.usb.rc               seapp_contexts
file_contexts                     init.rockchip.rc                    selinux_version
fstab.rk30board.bootmode.emmc     init.trace.rc                       sepolicy
fstab.rk30board.bootmode.unknown  init.usb.configfs.rc                service_contexts
init                              init.usb.rc                         sys
init.connectivity.rc              init.zygote32.rc                    system
init.environ.rc                   oem                                 ueventd.rc
init.rc                           proc                                ueventd.rk30board.rc

android 10.0 boot.img 解包

Android 10 的 boot.img 与Android6不太一样。

$ file boot.img 
boot.img: Android bootimg, kernel (0x10008000), ramdisk (0x11000000), second stage (0x10f00000), page size: 2048, cmdline (console=ttyFIQ0 androidboot.baseband=N/A androidboot.selinux=pe)

这里显示是 Android bootimg,包含内核、ramdisk, second stage, cmdline.

使用 binwalk 解析。

$ binwalk boot.img

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
0             0x0             Android bootimg, kernel size: 9021432 bytes, kernel addr: 0x10008000, ramdisk size: 17130613 bytes, ramdisk addr: 0x11000000, product name: ""
2084          0x824           Linux kernel ARM boot executable zImage (little-endian), load address: "0x00000000", end address: "0x0089A7F8"
110790        0x1B0C6         SHA256 hash constants, little endian
2842390       0x2B5F16        Certificate in DER format (x509 v3), header length: 4, sequence length: 5388
3314737       0x329431        Certificate in DER format (x509 v3), header length: 4, sequence length: 3
3682009       0x382ED9        Certificate in DER format (x509 v3), header length: 4, sequence length: 4355
4739912       0x485348        Cisco IOS microcode, for "Z"
7044069       0x6B7BE5        Certificate in DER format (x509 v3), header length: 4, sequence length: 676
7180231       0x6D8FC7        Certificate in DER format (x509 v3), header length: 4, sequence length: 108
7180235       0x6D8FCB        Certificate in DER format (x509 v3), header length: 4, sequence length: 132
7826614       0x776CB6        Cisco IOS microcode, for "8_MAXj"
7881005       0x78412D        Unix path: /505V/F505/F707/F717/P8'
9023488       0x89B000        gzip compressed data, from Unix, NULL date (1970-01-01 00:00:00)
26220684      0x190188C       Unix path: /thermal-zones/soc-thermal/trips/trip-point@0
26240000      0x1906400       PC bitmap, Windows 3.x format,, 550 x 550 x 8

可以看到很多段数据,内核、证书、hash、gzip文件、位图等。

unmkbootimg

由于是Android bootimg 文件,可以使用 unmkbootimg 工具解压该文件。

$ unmkbootimg -i boot.img
kernel written to 'kernel' (9021432 bytes)
ramdisk written to 'ramdisk.cpio.gz' (17130613 bytes)
second bootloader written to 'second_bootloader' (1298944 bytes)

SHA1 HASH MISMATCH!
  Expected : b73cd581ccb1ef4f50bad1fba9372d529391a894
  Got      : 67e49399c284be902dc217d564b039d222aabc8b


To rebuild this boot image, you can use the command:
  mkbootimg --base 0x10000000 --pagesize 2048 --cmdline 'console=ttyFIQ0 androidboot.baseband=N/A androidboot.selinux=permissive androidboot.wificountrycode=US androidboot.veritymode=enforcing androidboot.hardware=rk30board androidboot.console=ttyFIQ0 firmware_class.path=/vendor/etc/firmware init=/init' --kernel kernel --ramdisk ramdisk.cpio.gz --second second_bootloader -o boot.img

此时再看文件列表,解压出来3个文件。

$ ls
boot.img  kernel  ramdisk.cpio.gz  second_bootloader

针对 ramdisk.cpio.gz,从命名就知道这是通过cpio, gzip 双重压缩得到,所以只要逆向解压即可。

$ gunzip ramdisk.cpio.gz 
$ mkdir tmp && cd tmp
$ cpio -i < ../ramdisk.cpio 
69463 blocks

至此解压完毕。

$ ls
acct           etc                         plat_file_contexts         sepolicy
apex           first_stage_ramdisk         plat_property_contexts     storage
bin            init                        postinstall                sys
bugreports     init.rc                     proc                       system
cache          init.recovery.rk30board.rc  product                    tmp
charger        metadata                    product_file_contexts      ueventd.rc
config         mnt                         product_property_contexts  vendor
d              odm                         product_services           vendor_file_contexts
data           odm_file_contexts           prop.default               vendor_property_contexts
debug_ramdisk  odm_property_contexts       res
default.prop   oem                         sbin
dev            pcba                        sdcard

Android10 recovery.img 解包

安装 abootimg

apt install abootimg

解包 recovery.img

$ abootimg -x recovery.img
writing boot image config in bootimg.cfg
extracting kernel in zImage
extracting ramdisk in initrd.img
extracting second stage image in stage2.img

$ ls
bootimg.cfg  initrd.img  recovery.img  stage2.img  zImage

$ file *
bootimg.cfg:  ASCII text, with very long lines
initrd.img:   gzip compressed data, from Unix
recovery.img: Android bootimg, kernel (0x10008000), ramdisk (0x11000000), second stage (0x10f00000), page size: 2048, cmdline (console=ttyFIQ0 androidboot.baseband=N/A androidboot.selinux=pe)
stage2.img:   data
zImage:       data

解包 initrd.img

$ abootimg-unpack-initrd initrd.img 
39014 blocks
$ ls -F
bootimg.cfg  initrd.img  ramdisk/  recovery.img  stage2.img  zImage

多出来的 ramdisk/ 目录就是解包后的initrd.img .

也可以将initrd.img 重命名为initrd.gz, 然后通过gunzip和cpio进行解压。

boot.img 打包

android 6.0

  • SDK/mkimage.sh
if [ $IMG_TARGET == 'boot' ] || [ $IMG_TARGET == 'all' ];then
if [ $TARGET == $BOOT_OTA ]
then
    echo "make ota images... "
    echo -n "create boot.img with kernel... "
    [ -d $OUT/root ] && \
    mkbootfs $OUT/root | minigzip > $OUT/ramdisk.img && \
        truncate -s "%4" $OUT/ramdisk.img && \
    mkbootimg --kernel $OUT/kernel --ramdisk $OUT/ramdisk.img --second kernel/resource.img --output $OUT/boot.img && \
    cp -a $OUT/boot.img $IMAGE_PATH/
    echo "done."
else
    echo -n "create boot.img without kernel... "
    [ -d $OUT/root ] && \
    mkbootfs $OUT/root | minigzip > $OUT/ramdisk.img && \
        truncate -s "%4" $OUT/ramdisk.img && \
    rkst/mkkrnlimg $OUT/ramdisk.img $IMAGE_PATH/boot.img >/dev/null
    echo "done."
fi
fi #### boot

不考虑 OTA 情况下,Android6只会打包 ramdisk.img, 该镜像源文件位于 $OUT/root 目录。使用 mkbootfs 和 minigzip 生成 ramdisk.img, 然后通过 truncate 调整尺寸. 最后通过 RK 自带的工具 rkst/mkkrnlimg 添加特定的头部信息,也就是前面提到的 KRNL.

rkst/mkkrnlimg 会给输入文件添加两部分数据:

  1. 头部添加 KRNL.... 前缀 (8个字节)
  2. 尾部添加 CRC校验 (4个字节)

android 10.0

  • SDK/mkimage.sh
echo "create boot.img.... "
if [ "$BOARD_AVB_ENABLE" = "true" ]; then
cp -a $OUT/boot.img $IMAGE_PATH/boot.img
cp -a $OUT/boot-debug.img $IMAGE_PATH/boot-debug.img
else
echo "BOARD_AVB_ENABLE is false, make boot.img from kernel."
    mkbootimg --kernel $KERNEL_DEBUG --ramdisk $OUT/ramdisk.img --second kernel/resource.img --os_version $PLATFORM_VERSION --header_version $BOARD_BOOTIMG_HEADER_VERSION --os_patch_level $PLATFORM_SECURITY_PATCH --cmdline "$BOARD_KERNEL_CMDLINE" --output $OUT/boot.img && \
    cp -a $OUT/boot.img $IMAGE_PATH/boot.img
fi
echo "done."

从以上脚本可知,Android10里的打包使用的是通用的 mkbootimg 工具,所以使用 file 指令可以看出是 Android bootimg,所以解压也可以通过官方工具实现。

reference