背景
某个设备配套的刷机程序是个Linux recovery kernel,刷机过程会先从U盘加载刷机脚本,仅在签名校验通过后才执行脚本。本文记录了分析和移除签名校验的方法。
分析
刷机程序是一个bzImage文件,从启动的输出来看,内部包含了一个initrd,在initrd中实现了读取U盘中的脚本和签名校验过程。
查看initrd内容
通过增加启动参数(cmdline)rdinit=/bin/sh,可以使Kernel启动后执行/bin/sh,而不是默认的/init程序,有了命令行接口后,就可以查看initrd的内容。
~ # busybox find /
/
/.ash_history
/init
/etc
/etc/shadow
/etc/passwd
/.gnupg
/.gnupg/trustdb.gpg
/.gnupg/secring.gpg
/.gnupg/pubring.gpg~
/.gnupg/pubring.gpg
/bin
/bin/kexec
/bin/gpg2
/bin/busybox
/bin/dd
/bin/umount
/bin/sleep
/bin/rmdir
/bin/rm
/bin/reboot
/bin/mount
/bin/mkdir
/bin/ls
/bin/cat
/bin/sh
/mnt
/sys
/proc
/dev
/dev/pts
/dev/loop0
/dev/tty0
/dev/console
~ # busybox find /
/
/.ash_history
/init
/etc
/etc/shadow
/etc/passwd
/.gnupg
/.gnupg/trustdb.gpg
/.gnupg/secring.gpg
/.gnupg/pubring.gpg~
/.gnupg/pubring.gpg
/bin
/bin/kexec
/bin/gpg2
/bin/busybox
/bin/dd
/bin/umount
/bin/sleep
/bin/rmdir
/bin/rm
/bin/reboot
/bin/mount
/bin/mkdir
/bin/ls
/bin/cat
/bin/sh
/mnt
/sys
/proc
/dev
/dev/pts
/dev/loop0
/dev/tty0
/dev/console
# cat /init
...
gpg2 --ignore-time-conflict --ignore-valid-from --verify $FLASH_FILE_SIG $FLASH_FILE
if [ $? -eq 0 ]; then
echo "PWR_LED 3" > /proc/BOARD_io
/bin/busybox sh $FLASH_FILE
if [ $? -eq 0 ]; then
echo "PWR_LED 1" > /proc/BOARD_io
echo "flash success..."
echo "Please unplug USB drive and power cycle system"
else
echo "PWR_LED 4" > /proc/BOARD_io
echo "flash failed..."
echo "Please try again or try another board"
fi
else
echo "PWR_LED 4" > /proc/BOARD_io
echo "flash failed..."
echo "Script verify failed"
fi
...
# cat /init
...
gpg2 --ignore-time-conflict --ignore-valid-from --verify $FLASH_FILE_SIG $FLASH_FILE
if [ $? -eq 0 ]; then
echo "PWR_LED 3" > /proc/BOARD_io
/bin/busybox sh $FLASH_FILE
if [ $? -eq 0 ]; then
echo "PWR_LED 1" > /proc/BOARD_io
echo "flash success..."
echo "Please unplug USB drive and power cycle system"
else
echo "PWR_LED 4" > /proc/BOARD_io
echo "flash failed..."
echo "Please try again or try another board"
fi
else
echo "PWR_LED 4" > /proc/BOARD_io
echo "flash failed..."
echo "Script verify failed"
fi
...
从initrd的内容来看,由/init调用gpg2对U盘中的刷机脚本执行签名校验,只有公钥集成在initrd中,没有私钥。
到这一步,我们已经清楚了签名校验的实现方法,并且也能使启动过程进入受控的命令行交互状态,其实已经可以手工操作跳过签名过程来刷机。
修改
每次手工操作的确太麻烦,那就来移除initrd中的签名校验过程吧。
从bzImage的结构来看,要想修改initrd,先要从bzImage中提取出vmlinux,再从vmlinux中提取出initrd。
1. 提取vmlinux
从bzImage中提取vmlinux比较简单,有现成的工具,位于Linux源代码中 scripts/extract-vmlinux
./scripts/extract-vmlinux bzImage > vmlinux
./scripts/extract-vmlinux bzImage > vmlinux
2. 提取initrd
initrd的格式可以是cpio archive,也可以是gzip、bzip2、lzma、xz或lzo压缩的,先用binwalk扫描一遍。
binwalk vmlinux
binwalk vmlinux
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 ELF, 32-bit LSB executable, Intel 80386, version 1 (SYSV)
3641536 0x3790C0 Linux kernel version "2.6.39 (ubuntu@ubuntu) (gcc version 4.9.3 20150311 (prerelease) (crosstool-NG 1.20.0) ) #24 SMP Fri Jun 7 14:32:37 CST 2019"
3922304 0x3BD980 CRC32 polynomial table, little endian
4318976 0x41E700 Unix path: /home/ubuntu/ce5300/barcelona_kernel/arch/x86/include/asm/desc.h
4321256 0x41EFE8 Unix path: /home/ubuntu/ce5300/barcelona_kernel/arch/x86/include/asm/i387.h
4322244 0x41F3C4 Unix path: /home/ubuntu/ce5300/barcelona_kernel/arch/x86/include/asm/processor.h
4323964 0x41FA7C Unix path: /x86/kernel/cpu/perf_event_intel.c
4324152 0x41FB38 Unix path: /x86/kernel/cpu/perf_event_intel_ds.c
4325960 0x420248 Unix path: /x86/kernel/cpu/mcheck/mce.c
4326820 0x4205A4 Unix path: /x86/kernel/cpu/mcheck/mce_intel.c
4327124 0x4206D4 Unix path: /x86/kernel/cpu/mcheck/therm_throt.c
4328480 0x420C20 Unix path: /x86/kernel/cpu/mtrr/generic.c
4329752 0x421118 Unix path: /x86/kernel/cpu/mtrr/cleanup.c
4329832 0x421168 Unix path: /x86/kernel/cpu/perfctr-watchdog.c
4336148 0x422A14 Unix path: /x86/kernel/apic/apic_noop.c
4336572 0x422BBC Unix path: /x86/kernel/apic/io_apic.c
434