AVB(Android Verified Boot)是 Android 系统的可信启动链,从 Bootloader 一路到用户空间的每个分区都要经过密码学验证,确保系统未被篡改。本文深入讲解 AVB 2.0 的设计思想、数据结构和验证流程。
一、为什么需要 Verified Boot?
想象一个攻击场景:
- 攻击者把你的手机拿到手
- 通过某种方式(可能是漏洞或物理刷写)替换了
/system/bin/su或修改了/system/etc/init.rc - 注入恶意代码:开机自动上传用户数据
如果系统没有完整性校验,这种攻击毫无障碍。AVB 的核心目标:让从 Bootloader 开始的每一行代码都被密码学验证过,任何修改都能被发现。
二、信任链的核心思想
[Hardware Root of Trust] eFuse 烧死的公钥哈希
↓ 验证
[Bootloader] 验证 vbmeta
↓ 验证
[vbmeta.img] 存放各分区的 hash 描述符
↓ 验证
[boot, system, vendor, dtbo, ...]
↓ dm-verity 运行时验证
[文件系统中每个 4KB 块]
链条上任何一环被篡改,后续环节都会校验失败,导致设备无法启动(或者进入只读保护模式)。
三、AVB 2.0 的核心组件
1. vbmeta 分区
vbmeta 是信任的"根",存放着所有其它分区的元数据(hash、签名、属性)。
布局:
+-----------------------------+
| AvbVBMetaImageHeader | 256 字节
+-----------------------------+
| Authentication Data | 公钥 + 签名
| - hash (SHA-512) |
| - signature (RSA / ED25519)|
+-----------------------------+
| Auxiliary Data |
| - Descriptors: |
| • Hash Descriptor | 保护小分区 (boot, dtbo)
| • Hashtree Descriptor | 保护大分区 (system, vendor)
| • Chain Partition Desc | 指向另一个 vbmeta
| • Property Descriptor | 附加属性 (com.android.build.system.fingerprint)
| • Kernel Cmdline Desc | 传给 Kernel 的额外 cmdline
+-----------------------------+
2. AvbVBMetaImageHeader 定义
c
// external/avb/libavb/avb_vbmeta_image.h
struct AvbVBMetaImageHeader {
uint8_t magic[4]; // "AVB0"
uint32_t required_libavb_version_major;
uint32_t required_libavb_version_minor;
uint64_t authentication_data_block_size;
uint64_t auxiliary_data_block_size;
uint32_t algorithm_type; // 见下表
uint64_t hash_offset;
uint64_t hash_size;
uint64_t signature_offset;
uint64_t signature_size;
uint64_t public_key_offset;
uint64_t public_key_size;
uint64_t public_key_metadata_offset;
uint64_t public_key_metadata_size;
uint64_t descriptors_offset;
uint64_t descriptors_size;
uint64_t rollback_index; // 防回滚版本号
uint32_t flags;
uint32_t rollback_index_location;
uint8_t release_string[48]; // "avbtool 1.3.0"
uint8_t reserved[80];
} __attribute__((packed));
3. 算法类型
c
typedef enum {
AVB_ALGORITHM_TYPE_NONE = 0, // 不签名(开发用)
AVB_ALGORITHM_TYPE_SHA256_RSA2048 = 1,
AVB_ALGORITHM_TYPE_SHA256_RSA4096 = 2,
AVB_ALGORITHM_TYPE_SHA256_RSA8192 = 3,
AVB_ALGORITHM_TYPE_SHA512_RSA2048 = 4,
AVB_ALGORITHM_TYPE_SHA512_RSA4096 = 5,
AVB_ALGORITHM_TYPE_SHA512_RSA8192 = 6,
} AvbAlgorithmType;
现代设备多用 SHA256_RSA4096,平衡了安全性与性能。
四、Hash Descriptor:小分区保护
对于像 boot, dtbo, vendor_boot 这种几十 MB 的小分区,直接做整体 SHA-256 即可。
c
struct AvbHashDescriptor {
AvbDescriptor parent_descriptor;
uint32_t image_size; // 分区大小
uint8_t hash_algorithm[32]; // "sha256"
uint32_t partition_name_len;
uint32_t salt_len;
uint32_t digest_len; // hash 长度 (32 for SHA-256)
uint32_t flags;
uint8_t reserved[60];
// 后面紧跟:
// partition_name (UTF-8)
// salt
// digest (SHA-256 of "salt + image")
};
验证流程
Bootloader 加载 boot 分区时:
c
bool verify_hash_partition(const char *name, void *data, size_t size,
const AvbHashDescriptor *desc) {
// 1. 计算 SHA-256(salt || data)
uint8_t computed[32];
SHA256_CTX ctx;
SHA256_Init(&ctx);
SHA256_Update(&ctx, desc->salt, desc->salt_len);
SHA256_Update(&ctx, data, size);
SHA256_Final(computed, &ctx);
// 2. 与 vbmeta 中记录的 digest 比较
return memcmp(computed, desc->digest, desc->digest_len) == 0;
}
五、Hashtree Descriptor:大分区保护(dm-verity)
system / vendor 这种几个 GB 的分区,要做整体 hash 太慢。AVB 使用 Merkle Hash Tree:
Root Hash <- 存在 vbmeta 中
/ | \
H0 H1 H2 <- Level 2
/|\ /|\ /|\
... ... ... ... ... ...
/ \
Leaf hash of 4KB block 0..N <- Level 0,每个 4KB 数据块一个 hash
每个 4KB 数据块都有一个 SHA-256 hash,这些 hash 再聚合成上层 hash,层层向上直到 root hash。这个 hash tree 存在分区数据之后。
Hashtree Descriptor 结构
c
struct AvbHashtreeDescriptor {
AvbDescriptor parent_descriptor;
uint32_t dm_verity_version;
uint64_t image_size; // 实际数据大小
uint64_t tree_offset; // hash tree 起始偏移
uint64_t tree_size; // hash tree 大小
uint32_t data_block_size; // 4096
uint32_t hash_block_size; // 4096
uint32_t fec_num_roots; // FEC 校验
uint64_t fec_offset;
uint64_t fec_size;
uint8_t hash_algorithm[32]; // "sha256"
uint32_t partition_name_len;
uint32_t salt_len;
uint32_t root_digest_len;
uint32_t flags;
// ...
};
运行时验证:dm-verity
dm-verity 是 Linux 内核的设备映射模块,它的工作方式:
应用程序读 system 上某个文件
↓
VFS → ext4/erofs
↓
dm-verity 拦截块设备读
↓
1. 读出请求的数据块
2. 计算它的 SHA-256
3. 向上找对应的 hash 块,比较
4. 如此层层向上,直到 root hash
5. 如果 root hash == vbmeta 中记录的 -> 数据有效
↓
应用拿到数据 (或 kernel panic / IO error)
每个数据块第一次读时都会走这套流程,但 hash 块会被缓存,所以稳定后开销很小。
Kernel cmdline
Bootloader 启动 kernel 时,会拼接 dm-verity 参数到 cmdline:
dm="1 vroot none ro 1,0 6291456 verity 1 PARTUUID=xxx PARTUUID=xxx 4096 4096 786432 786432 sha256 abcd...(root hash) feedface...(salt)"
Kernel 解析这串字符串,在 init 之前就设置好 dm-verity 设备。
六、Chain Partition Descriptor:信任链扩展
vbmeta 默认会一个个分区检查,但有些场景下,我们希望 vbmeta_vendor、vbmeta_system 各自独立(便于厂商单独升级)。
Chain Descriptor 就是用来"委托":
c
struct AvbChainPartitionDescriptor {
AvbDescriptor parent_descriptor;
uint32_t rollback_index_location;
uint32_t partition_name_len;
uint32_t public_key_len;
uint32_t flags;
uint8_t reserved[60];
// partition_name + public_key
};
它的语义是:"vendor_boot 分区的验证元数据请去 vbmeta_vendor 分区找,公钥是这个 RSA-4096 公钥"。
形成的信任图:
vbmeta (root)
/ | \
boot dtbo [chain: vbmeta_system]
↓
system, system_ext, product
七、回滚保护(Rollback Protection)
光验证签名还不够,假如旧版本 boot 有漏洞,攻击者把整个旧 boot.img(签名是合法的!)刷回来,绕过补丁怎么办?
AVB 通过 rollback_index 解决:每次升级版本递增,Bootloader 把"已见过的最大 index"存在 TEE 安全存储里(RPMB),启动时:
c
uint64_t current_index = vbmeta->rollback_index;
uint64_t stored_index = read_from_rpmb(slot);
if (current_index < stored_index) {
// 试图刷旧版本!拒绝!
return AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX;
}
if (current_index > stored_index) {
// 升级了,在启动成功后写回 RPMB
pending_update = current_index;
}
RPMB 是 eMMC/UFS 内的硬件级写保护区,即使 root 也无法绕过。
八、设备状态:LOCKED / UNLOCKED
AVB 状态机:
| 状态 | 屏幕颜色 | 含义 |
|---|---|---|
| GREEN | 无警告 | LOCKED,签名验证成功,使用 OEM 密钥 |
| YELLOW | 黄色 | LOCKED,使用用户自定义密钥 (avb_custom_key) |
| ORANGE | 橙色 | UNLOCKED,所有验证被跳过 |
| RED | 红色 | LOCKED,验证失败,无法启动 |
Bootloader 在每次启动时绘制对应警告。开发者在解锁状态下可以自由刷写,但失去 AVB 保护。
九、实操:用 avbtool 给镜像签名
Android 源码自带 avbtool(Python 写的,在 external/avb/)。
签名 boot.img
bash
# 给 boot.img 生成 hash 描述符
avbtool add_hash_footer \
--partition_name boot \
--partition_size 134217728 \
--image boot.img \
--algorithm SHA256_RSA4096 \
--key testkey_rsa4096.pem \
--rollback_index 1
给 system.img 生成 hashtree(dm-verity)
bash
avbtool add_hashtree_footer \
--partition_name system \
--partition_size 3221225472 \
--image system.img \
--algorithm SHA256_RSA4096 \
--key testkey_rsa4096.pem \
--hash_algorithm sha256 \
--rollback_index 1
生成 vbmeta
bash
avbtool make_vbmeta_image \
--output vbmeta.img \
--algorithm SHA256_RSA4096 \
--key testkey_rsa4096.pem \
--include_descriptors_from_image boot.img \
--include_descriptors_from_image system.img \
--include_descriptors_from_image vendor.img \
--include_descriptors_from_image dtbo.img \
--rollback_index 1
查看 vbmeta 内容
bash
avbtool info_image --image vbmeta.img
输出片段:
Minimum libavb version: 1.0
Header Block: 256 bytes
Authentication Block: 576 bytes
Auxiliary Block: 3520 bytes
Algorithm: SHA256_RSA4096
Rollback Index: 1
Descriptors:
Hash descriptor:
Image Size: 67108864 bytes
Hash Algorithm: sha256
Partition Name: boot
Salt: aabbcc...
Digest: 11223344...
Hashtree descriptor:
Image Size: 3221225472 bytes
Tree Offset: 3221225472
Tree Size: 25690112 bytes
Data Block Size: 4096
Hash Block Size: 4096
Hash Algorithm: sha256
Partition Name: system
...
十、C 代码:简化版 vbmeta 解析
c
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <arpa/inet.h>
#define AVB_MAGIC "AVB0"
struct AvbVBMetaImageHeader {
char magic[4];
uint32_t required_libavb_version_major;
uint32_t required_libavb_version_minor;
uint64_t authentication_data_block_size;
uint64_t auxiliary_data_block_size;
uint32_t algorithm_type;
uint64_t hash_offset;
uint64_t hash_size;
uint64_t signature_offset;
uint64_t signature_size;
uint64_t public_key_offset;
uint64_t public_key_size;
uint64_t public_key_metadata_offset;
uint64_t public_key_metadata_size;
uint64_t descriptors_offset;
uint64_t descriptors_size;
uint64_t rollback_index;
uint32_t flags;
uint32_t rollback_index_location;
char release_string[48];
uint8_t reserved[80];
} __attribute__((packed));
// AVB 头部全部是 big-endian,需要转换
#define BE64(x) (((uint64_t)htonl((uint32_t)(x)) << 32) | htonl((uint32_t)((x) >> 32)))
int main(int argc, char **argv) {
if (argc < 2) {
fprintf(stderr, "Usage: %s vbmeta.img\n", argv[0]);
return 1;
}
int fd = open(argv[1], O_RDONLY);
if (fd < 0) { perror("open"); return 1; }
struct AvbVBMetaImageHeader hdr;
if (read(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) {
perror("read");
return 1;
}
close(fd);
if (memcmp(hdr.magic, AVB_MAGIC, 4) != 0) {
fprintf(stderr, "Not a vbmeta image\n");
return 1;
}
printf("Magic: %.4s\n", hdr.magic);
printf("libavb version: %u.%u\n",
ntohl(hdr.required_libavb_version_major),
ntohl(hdr.required_libavb_version_minor));
printf("Algorithm type: %u\n", ntohl(hdr.algorithm_type));
printf("Auth block size: %lu\n", BE64(hdr.authentication_data_block_size));
printf("Aux block size: %lu\n", BE64(hdr.auxiliary_data_block_size));
printf("Rollback index: %lu\n", BE64(hdr.rollback_index));
printf("Release string: %s\n", hdr.release_string);
return 0;
}
十一、性能影响
AVB 的验证开销主要发生在两处:
- Bootloader 启动阶段:验证 vbmeta + boot/dtbo 的签名,几十毫秒
- 运行时 dm-verity:每读一个 4KB 块都要计算 hash
实测开销:
- 启动时间增加约 100~300 ms
- IO 性能下降约 13%(顺序读)、510%(随机读)
- CPU 占用增加约 1~2%
ARMv8 上 SHA-256 有硬件加速指令(SHA256H、SHA256H2),所以开销可以接受。
十二、踩坑实战
- 解锁 Bootloader 后 boot 报 vbmeta 红屏 :用
fastboot --disable-verity --disable-verification flash vbmeta - dm-verity corruption 死循环:某个块损坏会直接进 recovery 模式,需要重刷分区
- chain partition 的公钥要对:vbmeta 中的 chain 公钥必须和 vbmeta_system 的签名公钥一致
- rollback_index 不能乱降:降级前要从 RPMB 清除,否则永远启不来
- avbtool 版本要匹配:不同 Android 版本对应不同的 libavb,旧 avbtool 签不出新格式
十三、总结
AVB 2.0 是 Android 安全启动链的核心:
- vbmeta 作为信任根,串起所有分区
- Hash Descriptor / Hashtree Descriptor 分别处理小分区和大分区
- dm-verity 让 system 在运行时也持续被保护
- Rollback Index + RPMB 防止版本回退攻击
- Chain Partition 让信任链可以委托给子 vbmeta
理解 AVB 不仅对做 ROM 开发的工程师重要,做安全审计、应急响应也非常实用。下一篇我们继续讲 Fastboot 协议,看 Bootloader 是如何和电脑通信的。