Arm Trusted Firmware-A 安全启动分析

ATF代码:https://github.com/ARM-software/arm-trusted-firmware

1. 调试环境搭建

动态调试能够更快速地分析代码的执行流程,本文编译qemu平台的ATF,搭建调试环境。

主机环境:Ubuntu22.04 AArch64

参考资料:https://blog.csdn.net/qq_16106195/article/details/126708271

1.1 qemu

qemu是固件的运行平台。建议源码编译安装,直接apt install qemu安装qemu版本比较低,后续会遇到问题。

参考:https://hlyani.github.io/notes/openstack/qemu_make.html

1.2 U-boot

启动过程中的bl33为u-boot,在编译时需要指定BL33为u-boot.bin。

1
2
3
4
5
6
7
8
9
// 下载
git clone https://source.denx.de/u-boot/u-boot.git

// 编译
cd u-boot
export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-
make qemu_arm64_defconfig
make -j16

编译完成之后在u-boot根目录下生成u-boot.bin镜像文件。

1.3 mbed TLS库

在安全启动过程中验证镜像需要使用mbedtls库。

1
2
3
4
5
// 下载mbedtls
git clone https://github.com/Mbed-TLS/mbedtls.git
// 切换分支
cd mbedtls
git checkout mbedtls-3.4.0

**注意:**mbedtls在v3.6.0 v3.5.2 v3.5.1 v3.5.0 mbedtls-3.6.0 mbedtls-3.5.2 mbedtls-3.5.1 mbedtls-3.5.0版本中移除了hash_info模块,但是在新版本的atf代码中要求mbedtls版本为3.x,并且需要hash_info模块,建议使用mbedtls V3.4.0版本。

https://github.com/Mbed-TLS/mbedtls/issues/6993

https://github.com/Mbed-TLS/mbedtls/commit/13230a4ad3afd51bb935c23c908184f173c37c91

arm-trusted-firmware/drivers/auth/mbedtls/mbedtls_common.mk

1.4 编译支持Trusted Board Boot的镜像

在ATF代码中默认不支持Trusted Board Boot功能,如果编译支持Trusted Board Boot的镜像,需要开启TRUSTED_BOARD_BOOT和GENERATE_COT编译选项。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 安装dtc依赖
sudo apt-get install device-tree-compiler

// 下载ATF
git clone https://git.trustedfirmware.org/TF-A/trusted-firmware.git

// 编译ATF,指定BL33为u-boot.bin,开启TRUSTED_BOARD_BOOT和GENERATE_COT选项
export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-
MBEDTLS_DIR=../mbedtls \
make PLAT=qemu TRUSTED_BOARD_BOOT=1 GENERATE_COT=1 \
ARM_ROTPK_LOCATION=devel_rsa \
ROT_KEY=plat/arm/board/common/rotpk/arm_rotprivk_rsa.pem \
BL33=../u-boot/u-boot.bin all fip DEBUG=1 LOG_LEVEL=50

如果开启TRUSTED_BOARD_BOOT和GENERATE_COT编译选项,需要指定mbedtls库源码路径,因为镜像验证需要依赖mbedtls库。ARM_ROTPK_LOCATION选项指定使用位于plat/arm/board/common/rotpk/arm_rotpk_rsa_sha256.bin 中的默认哈希值;当GENERATE_COT = 1时使用ROT_KEY选项,用于指定包含PEM格式的ROT私钥的文件,并强制生成公钥哈希。

编译完成之后在arm-trusted-firmware/build/qemu/debug目录下生成bl1.bin、bl2.bin、bl31.bin和fip.bin镜像:

image-20240509170607388

fip.bin和bl1.bin、bl2.bin以及bl31.bin的关系:

  1. BL1 (Boot Loader Stage 1): BL1 是第一阶段的引导加载程序,也称为 bootROM。bl1固化在 SoC 的 ROM 中,并且是处理器启动后运行的第一个代码。BL1 的主要职责包括初始化处理器核心、设置内存控制器以及加载 BL2;
  2. FIP (Firmware Image Package): FIP 是一个封装容器,里面包含多个固件组件,如 BL2、BL30(引导加载程序的可选部分,用于初始化 GPU),BL31,BL32(安全引导可选部分,如 OP-TEE 等)和 BL33。fip.bin 文件是以上文件的打包版本,包含的镜像在设备引导过程中会被顺序加载;
  3. 在启动时,ROM 代码(BL1)会先执行,然后加载 fip.bin 中的其他组件,从而完成整个系统的引导过程。

参考:

https://trustedfirmware-a.readthedocs.io/en/v2.4/design/trusted-board-boot-build.html

https://www.cnblogs.com/arnoldlu/p/14301334.html

https://www.cnblogs.com/jianhua1992/p/16852776.html

1.5 qemu通过ATF加载u-boot

首先需要准备u-boot.bin镜像,然后使用qemu运行bl1.bin镜像,并由bl1加载bl2镜像,bl2加载bl31镜像,最后通过层层加载,启动bl33固件,即u-boot.bin。

新建文件夹atf_load_u-boot,拷贝bl1.bin、bl2.bin、bl31.bin到当前目录下,拷贝u-boot.bin到当前目录下并重命名为bl33.bin:

1
2
3
4
mkdir atf_load_u-boot
cd atf_load_u-boot
cp ../arm-trusted-firmware/build/qemu/debug/*.bin ./
cp ../u-boot/u-boot.bin ./bl33.bin

启动镜像:

1
2
3
4
5
6
7
qemu-system-aarch64 \
-nographic -machine virt,secure=on \
-cpu cortex-a53 \
-smp 2 -m 2048 \
-d unimp \
-bios bl1.bin \
-semihosting-config enable=on,target=native

直接使用上述命令启动镜像会出现如下报错:

需要在gdb调试环境下使用restore命令加载fip镜像到内存中,具体原因见《1.6 gdb调试目标镜像》。

1.6 gdb调试目标镜像

使用qemu通过FIP方式启动ATF:

1
2
3
4
5
6
7
8
9
cd atf_load_u-boot 

qemu-system-aarch64 \
-nographic -machine virt,secure=on \
-cpu cortex-a53 \
-smp 2 -m 2048 \
-d unimp \
-bios bl1.bin \
-semihosting-config enable=on,target=native -s -S

-S:表示QEMU虚拟机会冻结CPU,直到远程的GDB输入相应控制命;
-s:表示在1234端口接受GDB的调试连接。

gdb附加调试并加载fip.bin镜像到内存:

1
2
3
cd arm-trusted-firmware/build/qemu/debug/
// 启动gdb调试
gdb bl1/bl1.elf
1
2
3
4
5
6
7
// 连接目标板
pwndbg> target remote localhost:1234
// 加载fip.bin到内存
pwndbg> restore fip.bin binary 0x00040000
// 继续运行
pwndbg> c
Continuing.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
sx1ang@sx1ang-vm > ~/WorkSpace/Soc/ATF/atf_load_u-boot > qemu-system-aarch64 \
-nographic -machine virt,secure=on \
-cpu cortex-a53 \
-smp 2 -m 2048 \
-d unimp \
-bios bl1.bin \
-semihosting-config enable=on,target=native -S -s
VERBOSE: Region: 0xe001000 - 0xe100000 has attributes 0xa
VERBOSE: Region: 0x0 - 0x6000 has attributes 0x2
VERBOSE: Region: 0x6000 - 0x9000 has attributes 0x42
mmap:
VA:0x0 PA:0x0 size:0x6000 attr:0x2 granularity:0x40000000
VA:0x6000 PA:0x6000 size:0x3000 attr:0x42 granularity:0x40000000
VA:0x0 PA:0x0 size:0x4000000 attr:0x2 granularity:0x40000000
VA:0x4000000 PA:0x4000000 size:0x4000000 attr:0x2 granularity:0x40000000
VA:0x8000000 PA:0x8000000 size:0x1000000 attr:0x8 granularity:0x40000000
VA:0x9000000 PA:0x9000000 size:0xc00000 attr:0x8 granularity:0x40000000
VA:0xe000000 PA:0xe000000 size:0x1000 attr:0x8 granularity:0x40000000
VA:0xe001000 PA:0xe001000 size:0xff000 attr:0xa granularity:0x40000000

VERBOSE: Translation tables state:
···

注意:默认从memmap中以FIP方式加载镜像,如果memmap中没有镜像,就会打印Firmware Image Package header check failed. 即FIP校验头失败;这种情况下qemu会使用半主机的方式读取镜像Using Semi-hosting IO,直接半主机方式读取原始镜像到内存地址中(实际上是通过读文件的方式),即不会再校验FIP镜像的header。因此如果需要FIP方式加载镜像,需要提前将镜像加载到memmap中。在gdb调试环境中,可以使用gdb的restore命令将fip镜像加载到内存中。

1
restore file binary start_addr end_addr

开始地址是在ATF qemu平台的代码中配置的:

1
2
#define PLAT_QEMU_FIP_BASE		0x00040000
#define PLAT_QEMU_FIP_MAX_SIZE 0x00400000

restore将镜像加载到内存之后再启动:

gdb detach之后会得到u-boot的shell:

gdb调试页面:

参考:

https://blog.csdn.net/a565499699/article/details/79294261

https://blog.csdn.net/zhangye3017/article/details/80382496

https://www.cnblogs.com/gnivor/p/15153133.html

https://liupzmin.com/2021/07/20/theory/stack-insight-02/

https://nanxiao.me/linux-pstack/

安全启动流程分析

代码入口