欢迎光临
我们一直在努力

MTK安全启动分析13:LK 阶段的 Android Verified Boot (AVB) 校验

  LK 阶段的 Android Verified Boot (AVB) 校验

一、引言

Android Verified Boot 2.0(AVB,也称
libavb)是 Android 从 8.0 开始引入的一套启动完整性校验框架。在 MT8766/MT6761 平台上,AVB 的验签逻辑完全实现在 LK(Little Kernel)阶段,内核不再重复验签,仅通过 dm-verity 做运行时完整性保障。

本文将基于实际源码,从架构设计、代码实现、密钥管理、防回滚、错误处理、攻击面等维度,对 LK 中的 AVB 实现做深度分析。


二、整体架构:三层模型

MTK LK 的 AVB 实现分为三层:

┌───────────────────────────────────────────────────────────┐
│ 上层: load_vfy_boot.c (MTK 集成层) │
│ – 内存管理、分区预加载 │
│ – boot/recovery 选择 │
│ – cmdline/DTB 后处理 │
│ – 反回滚版本号记录 │
│ – boot state 管理 │
├───────────────────────────────────────────────────────────┤
│ 中间层: libavb (Google 官方库) │
│ – avb_slot_verify() 主流程 │
│ – load_and_verify_vbmeta() 递归验证 │
│ – load_and_verify_hash_partition() 哈希校验 │
│ – avb_vbmeta_image_verify() 签名验证 │
│ – chain partition 处理 │
├───────────────────────────────────────────────────────────┤
│ 底层: avb_hal.c (MTK 硬件抽象层) │
│ – 分区读写 (eMMC/UFS) │
│ – 公钥验证 (sec_set_pubk → TEE) │
│ – Rollback Index 读写 (OTP efuse) │
│ – 设备锁状态 │
│ – Persistent Value 存储 │
└───────────────────────────────────────────────────────────┘


三、顶层入口:load_vfy_boot() 详细分析

 3.1 调用链

mt_boot_init()
└── boot_linux_from_storage()
└── load_vfy_boot(BOOTIMG_TYPE_BOOT, CFG_BOOTIMG_LOAD_ADDR) [load_vfy_boot.c]

3.2 load_vfy_boot() 执行流 

uint32_t load_vfy_boot(uint32_t bootimg_type, uint32_t addr)
{
// [load_vfy_boot.c]

// 1. 构建请求分区列表
build_up_boot_partitions(boot_partitions, boot_partitions_sz);
// → 如果 vendor_boot 分区存在,追加到请求列表

// 2. 初始化 AVB 堆 (140MB 对齐 DRAM 保留区)
avb_heap_sz = AVB_HEAP_SZ; // 140 MB
avb_heap = mblock_reserve_ext(…, avb_heap_sz, …);

// 3. Authentication 检查
boot_authentication(bootimg_type);
// → 检查是否需要签名验证 (由安全策略决定)
// =0 时设置 AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR

// 4. dm-verity 状态检测
get_dm_verity_status(&dm_status);
if (dm_status)
avb_flag |= AVB_SLOT_VERIFY_FLAGS_RESTART_CAUSED_BY_HASHTREE_CORRUPTION;

// 5. 持久化存储初始化 (persist partition)
init_persist_value(&ops, PERSIST_PART_NAME, …);

// 6. 核心验证: avb_slot_verify()
avb_ret = avb_slot_verify(&ops,
boot_partitions, // 请求分区
get_suffix(), // A/B slot
avb_flag,
hashtree_error_mode,
&slot_data);

// 7. 验证后处理
if (avb_ret == AVB_SLOT_VERIFY_RESULT_OK) {
boot_post_processing(&ops, bootimg_type, slot_data);
// ├─ collect_rot_info() – 收集 Root of Trust
// ├─ prepare_kernel_dtb() – 准备 DTB
// ├─ avb_cmdline_postprocessing() – 写入 DTB
// └─ cmdline_append() – 追加 cmdline
g_boot_state = BOOT_STATE_GREEN;
}

// 8. 设备解锁状态覆盖
if (lock_state == LKS_UNLOCK)
g_boot_state = BOOT_STATE_ORANGE;

// 9. dm-verity 错误处理
dm_verity_handler(hashtree_error_mode);

// 10. 最终状态裁决
vboot_state_result(bootimg_type);
}

安全设计要点:

  • 140MB 堆空间用于加载分区数据,足以容纳整个 boot 镜像
  • avb_heap 使用 mblock_reserve_ext() 保留在物理内存中,不经过 LK 的普通堆分配,防止内存碎片
  • boot_authentication() 允许在非安全构建中跳过签名验证(ALLOW_VERIFICATION_ERROR 标志),但此时 g_boot_state 保持 RED

 四、核心引擎:avb_slot_verify() 深度剖析

4.1 主流程 

AvbSlotVerifyResult avb_slot_verify(
AvbOps* ops,
const char* const* requested_partitions, // 请求分区列表
const char* ab_suffix, // "_a" 或 "_b"
AvbSlotVerifyFlags flags,
AvbHashtreeErrorMode hashtree_error_mode,
AvbSlotVerifyData** out_data)
{
// [avb_slot_verify.c]

// 创建 slot_data 结构体 (包含所有验证结果)
slot_data = avb_calloc(sizeof(AvbSlotVerifyData));
slot_data->vbmeta_images = avb_calloc(… MAX_NUMBER_OF_VBMETA_IMAGES); // 32
slot_data->loaded_partitions = avb_calloc(… MAX_NUMBER_OF_LOADED_PARTITIONS); // 32

// 两条路径:
if (flags & AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION) {
// 路径A: 无独立 vbmeta 分区, 直接从 boot 分区找
for (每个请求分区)
load_and_verify_vbmeta(…, requested_partitions[n], …);
} else {
// 路径B: 标准路径, 从 vbmeta 分区开始
ret = load_and_verify_vbmeta(ops,
"vbmeta", // ← 从 vbmeta 分区加载
ab_suffix,
…);
}

// 处理验证结果:
// – 将 vbmeta digest 等信息注入 cmdline
// – 设置 rollback_indexes
// – 处理 flags(VERIFICATION_DISABLED/HASHTREE_DISABLED)
}

关键设计决策: 两条路径的存在是为了兼容两种分区布局:

  • 传统布局:vbmeta 独立分区 → 路径 B
  • BOOT_SYSROOT:vbmeta 数据嵌入 boot 分区末尾(BOOT_BUILD_SYSTEM_ROOT_IMAGE = true)→ 路径 A
  • 4.2 load_and_verify_vbmeta() — 递归验证核心

    这是整个 AVB 最核心的函数,递归地从主 vbmeta 开始,沿着 chain descriptor 遍历所有子 vbmeta:

    static AvbSlotVerifyResult load_and_verify_vbmeta(
    AvbOps* ops,
    const char* const* requested_partitions,
    const char* ab_suffix,
    AvbSlotVerifyFlags flags,
    bool allow_verification_error,
    uint32_t rollback_index_location,
    const char* partition_name, // 要验证的分区名
    size_t partition_name_len,
    const uint8_t* expected_public_key, // chain 指定的公钥 (NULL = 主 vbmeta)
    size_t expected_public_key_length,
    AvbSlotVerifyData* slot_data,
    AvbAlgorithmType* out_algorithm_type,
    AvbCmdlineSubstList* additional_cmdline_subst)
    {
    // [avb_slot_verify.c]

    // Step 1: 判断是否为 main vbmeta
    is_main_vbmeta = (rollback_index_location == 0);

    // Step 2: 判断是否需要查找 AVB Footer
    // vbmeta/vbmeta_xxx 分区 → 从头开始 (no footer)
    // boot/recovery 等分区 → 末尾找 AVB Footer
    look_for_vbmeta_footer = true;
    if (partition_name starts with "vbmeta")
    look_for_vbmeta_footer = false;

    // Step 3: 构造完整分区名 (加上 A/B suffix)
    snprintf(full_partition_name, "vbmeta_a");

    // Step 4: 读取 vbmeta 数据
    // 有 footer: 先读 footer, 从 footer.vbmeta_offset 读取
    // 无 footer: 从 offset 0 读取最多 64KB
    if (look_for_vbmeta_footer) {
    ops->read_from_partition(ops, full_partition_name, -AVB_FOOTER_SIZE, …);
    if (footer 有效)
    vbmeta_offset = footer.vbmeta_offset;
    }
    ops->read_from_partition(ops, full_partition_name, vbmeta_offset, vbmeta_size, …);

    // Step 5: 签名验证 (RSA + SHA256/SHA512)
    vbmeta_ret = avb_vbmeta_image_verify(vbmeta_buf, vbmeta_num_read,
    &pk_data, &pk_len);

    // Step 6: 公钥验证
    if (expected_public_key != NULL) {
    // chain partition: 比对公钥是否匹配 chain descriptor 中的公钥
    if (expected_public_key match pk_data) → OK
    else → AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED
    } else {
    // main vbmeta: 调用 ops->validate_vbmeta_public_key()
    // → 进入 avb_hal_verify_public_key() [见第五章]
    }

    // Step 7: Rollback Index 检查
    ops->read_rollback_index(ops, rollback_index_location, &stored_rollback_index);
    if (vbmeta_header.rollback_index < stored_rollback_index)
    → AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX

    // Step 8: 处理 descriptors
    for (n = 0; n < num_descriptors; n++) {
    switch (descriptors[n].tag) {

    case AVB_DESCRIPTOR_TAG_HASH:
    // → 验证分区数据哈希 [见4.3]
    load_and_verify_hash_partition(…);
    break;

    case AVB_DESCRIPTOR_TAG_CHAIN_PARTITION: //vbmeta_system/vendor
    // → 递归调用自身
    if (!is_main_vbmeta) // 只允许 main vbmeta 包含 chain
    → ERROR_INVALID_METADATA
    load_and_verify_vbmeta(ops,
    chain_partition_name, // "vbmeta_system"/"vbmeta_vendor"
    chain_public_key, // 子 vbmeta 专用公钥
    chain_desc.rollback_index_location, // 独立 rollback 位置
    …);
    break;

    case AVB_DESCRIPTOR_TAG_KERNEL_CMDLINE:
    // 收集 cmdline 片段
    break;

    case AVB_DESCRIPTOR_TAG_HASHTREE:
    // dm-verity 描述符,LK 不做验证,交给内核
    break;
    }
    }
    }

    安全要点——chain 的限制:

    // [avb_slot_verify.c]
    // Only allow CHAIN_PARTITION descriptors in the main vbmeta image.
    if (!is_main_vbmeta) {
    ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
    goto out;
    }

    只有 main vbmeta 可以包含 chain descriptor,子 vbmeta 发现 chain descriptor 直接报错。这防止了 chain 的深层嵌套攻击。

    子 vbmeta 的 flags 也必须为 0:

    // [avb_slot_verify.c]
    if (vbmeta_header.flags != 0) {
    ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
    }

    防止子 vbmeta 设置危险的 flags(如 VERIFICATION_DISABLED)。

    4.3 Hash Partition 验证

     当 descriptor 类型为 AVB_DESCRIPTOR_TAG_HASH 时,执行分区数据完整性校验:

    static AvbSlotVerifyResult load_and_verify_hash_partition(
    AvbOps* ops, …) // [avb_slot_verify.c:270-443]
    {
    // 解析 Hash Descriptor
    avb_hash_descriptor_validate_and_byteswap(…);
    // ├─ partition_name: 分区名 (如 "boot")
    // ├─ hash_algorithm: "sha256" / "sha512"
    // ├─ salt: 随机盐值 (防御彩虹表)
    // ├─ image_size: 预期的镜像大小
    // └─ digest: 预期的哈希值

    // 如果分区不在请求列表中, 跳过
    if (partition not in requested_partitions) return OK;

    // 加载完整分区数据
    load_full_partition(ops, part_name, image_size, &image_buf, …);

    // 计算哈希: SHA256(salt || partition_data)
    if (strcmp(hash_algorithm, "sha256") == 0) {
    avb_sha256_init(&sha256_ctx);
    avb_sha256_update(&sha256_ctx, salt, salt_len);
    avb_sha256_update(&sha256_ctx, image_buf, image_size);
    digest = avb_sha256_final(&sha256_ctx);
    } else if (strcmp(hash_algorithm, "sha512") == 0) {
    // 同样, SHA512(salt || data)
    }

    // 比对哈希
    if (avb_safe_memcmp(digest, expected_digest, digest_len) != 0) {
    → AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION
    // "Hash of data does not match digest in descriptor."
    }
    }

    安全要点——allow_verification_error 下的放宽:

    // [avb_slot_verify.c]
    if (allow_verification_error) {
    image_size = 整个分区大小; // 而不是 descriptor 中的 image_size
    }

    当设备解锁后允许验证错误时,加载整个分区而不是 descriptor 指定的范围,使 "fastboot flash boot" 刷不同大小的镜像也能启动。
     


     五、底层 HAL:avb_hal.c 深度分析

     5.1 AvbOps 结构体 [avb_hal.c] 

    AvbOps ops = {
    .read_from_partition = avb_hal_read_from_partition,
    .get_preloaded_partition = avb_hal_get_preloaded_partition, // 可选
    .write_to_partition = avb_hal_write_to_partition, // 用于 persist
    .validate_vbmeta_public_key = avb_hal_verify_public_key, // 公钥验证
    .read_rollback_index = avb_hal_read_rollback_index, // rollback
    .write_rollback_index = avb_hal_write_rollback_index, // rollback
    .read_is_device_unlocked = avb_hal_read_is_device_unlocked,
    .get_unique_guid_for_partition = avb_hal_get_unique_guid_for_partition,
    .get_size_of_partition = avb_hal_get_size_of_partition,
    .read_persistent_value = avb_hal_read_persistent_value,
    .write_persistent_value = avb_hal_write_persistent_value,
    .validate_public_key_for_partition = avb_hal_validate_public_key_for_partition,
    };

    5.2 公钥验证:信任锚点 [avb_hal.c:206-263]

    这是 AVB 信任链的根——编译时嵌入的公钥:
     

    avb_hal_verify_public_key() 的实现:

    #ifndef __AVBKEY__
    #define __AVBKEY__
    #define AVB_PUBK_SZ 256
    #define AVB_PUBK 0xC6, 0x55, 0x51, 0xDD, 0x32, 0x24, 0xA2, 0xE0, 0x0E, 0xBC, 0x7E, 0xFD, 0xBD, 0xA2, 0x53, 0x80, \\
    0x58, 0x69, 0x7E, 0xF5, 0x4A, 0x40, 0x87, 0x95, 0x90, 0x54, 0x59, 0x3D, 0x55, 0xCA, 0xFF, 0x36, \\
    0x34, 0x1A, 0xFA, 0xE1, 0xE0, 0x90, 0x2A, 0x1A, 0x32, 0x68, 0x5B, 0xF3, 0xDF, 0xAD, 0x0B, 0xF9, \\
    0xB1, 0xD0, 0xF7, 0xEA, 0xAB, 0x47, 0x1F, 0x76, 0xBE, 0x1B, 0x98, 0x4B, 0x67, 0xA3, 0x62, 0xFA, \\
    0xDF, 0xE6, 0xB5, 0xF8, 0xEE, 0x73, 0x16, 0x5F, 0xB8, 0xB1, 0x82, 0xDE, 0x49, 0x89, 0xD5, 0x3D, \\
    0xD7, 0xA8, 0x42, 0x99, 0x81, 0x75, 0xC8, 0xD8, 0x84, 0x7B, 0xBD, 0x54, 0xA8, 0x22, 0x64, 0x44, \\
    0xBC, 0x34, 0x06, 0x10, 0x3C, 0x89, 0xC2, 0xD1, 0xF3, 0x2C, 0x03, 0x65, 0x91, 0xB1, 0xA0, 0xD1, \\
    0xC8, 0x21, 0x56, 0x15, 0x99, 0x48, 0x20, 0x27, 0x74, 0xEF, 0x01, 0x7A, 0x76, 0xA5, 0x0B, 0x6B, \\
    0xFD, 0xE3, 0xFA, 0xED, 0x0D, 0xF9, 0x0F, 0x7A, 0x41, 0xFA, 0x76, 0x05, 0x37, 0x49, 0xFE, 0x34, \\
    0x4F, 0x4B, 0x01, 0x49, 0xE4, 0x98, 0xF7, 0x89, 0x8E, 0xCD, 0x36, 0xAA, 0x39, 0x1D, 0xA9, 0x7D, \\
    0x5D, 0x6B, 0x5A, 0x52, 0xD1, 0x75, 0x69, 0xA8, 0xDF, 0x7C, 0xDE, 0x1C, 0x1B, 0xF9, 0xD9, 0x19, \\
    0x5B, 0xB7, 0x47, 0x4C, 0xB9, 0x70, 0x2E, 0xAD, 0xE5, 0xD6, 0x88, 0x7C, 0xED, 0x92, 0x6E, 0x46, \\
    0x08, 0x10, 0xB5, 0x76, 0x03, 0x3E, 0x09, 0xAC, 0x4D, 0xB6, 0x2C, 0xCD, 0x12, 0x00, 0xBD, 0xD4, \\
    0xA7, 0x03, 0xD3, 0x1B, 0x91, 0x08, 0x23, 0x36, 0x5B, 0x11, 0xFE, 0xAF, 0x59, 0x69, 0xB3, 0x3C, \\
    0x88, 0x24, 0x37, 0x2D, 0x61, 0xBA, 0xC5, 0x99, 0x51, 0x18, 0x97, 0xF9, 0x23, 0x42, 0x96, 0x9F, \\
    0x87, 0x2E, 0xCD, 0xB2, 0x4D, 0x5F, 0xA9, 0x24, 0xF2, 0x45, 0xDA, 0xE2, 0x65, 0x26, 0x26, 0x4D
    #endif /*__AVB_KEY__ */

    // 编译时从 key 文件生成并链接
    uint8_t g_avb_key[AVB_PUBK_SZ] = {AVB_PUBK};
    uint8_t g_avb_recovery_key[AVB_RECOVERY_PUBK_SZ] = {AVB_RECOVERY_PUBK};

    avb_hal_verify_public_key() 的实现:

    AvbIOResult avb_hal_verify_public_key(AvbOps *ops, …) {
    // 1. 提取 keysize(检查 key 结构头和大小)
    pubk_sz = avb_htobe32(key_hdr->key_num_bits) / 8;
    if (pubk_sz != AVB_PUBK_SZ) {
    // key 大小不匹配 → 不受信任
    *out_is_trusted = FALSE;
    goto end;
    }

    // 2. 注入 TEE: sec_set_pubk(pubk, pubk_sz) ← 关键操作
    // 将公钥传递给 TEE (ATF/TrustZone), TEE 后续用此 key
    // 验证其他内容的签名
    sec_set_pubk(pubk, pubk_sz);

    // 3. 比对编译时公钥
    if (memcmp(g_avb_key, pubk, pubk_sz) == 0) {
    *out_is_trusted = TRUE; // 匹配 → 信任
    } else {
    // 不匹配:

    MTK_SECURITY_YELLOW_STATE_SUPPORT 时:
    *out_is_trusted = TRUE; // 仍信任但标记 YELLOW
    g_vb_custom_key_exist = 1; // 自定义 key 标志

    非 YELLOW_STATE 时:
    *out_is_trusted = FALSE; // 不信任 → 验证失败
    }
    }

    安全设计分析:

    场景

    out_is_trusted

    g_boot_state

    结果

    key 完全匹配g_avb_key

    TRUE

    GREEN

    信任,正常启动

    key 不匹配 + YELLOW_SUPPORT

    TRUE

    YELLOW

    信任,显示黄色警告

    key 不匹配 + !YELLOW_SUPPORT

    FALSE

    RED

    拒绝启动

    key 大小异常

    FALSE

    RED

    拒绝启动

    关键攻击面: g_avb_key 在编译时嵌入到 LK 镜像中。如果攻击者能替换 LK 镜像的公钥(但 LK 本身被 Preloader 的签名保护),就可以用自己的 key 签名 vmbata。这正是链式信任要解决的问题——LK 自身被 Preloader 用 OTP 中的根公钥验证。

    5.3 分区读写 [avb_hal.c]

    AvbIOResult avb_hal_read_from_partition(AvbOps *ops, …) {
    // offset < 0 → 从分区末尾偏移(读取 AVB Footer)
    get_abs_offset(partition, offset, &abs_offset);
    partition_read(partition, abs_offset, buffer, num_bytes);
    }

    AvbIOResult avb_hal_get_preloaded_partition(AvbOps *ops, …) {
    // 检查分区是否被预加载(提前读入内存加速)
    get_preload_partition_addr_sz(partition, &preload_addr, &preload_sz);
    if (成功) {
    *out_pointer = preload_addr; // 直接指向预加载地址,零拷贝
    }
    }

    PRELOAD_PARTITION_SUPPORT 是一个优化:在进入 AVB 之前,Preloader 或 LK 早期阶段已经将 vbmeta/boot 分区预加载到内存中,AVB 层通过零拷贝直接访问,避免重复读取 eMMC。

    5.4 Rollback Index 读取:OTP efuse 操作

    AvbIOResult avb_hal_read_from_partition(AvbOps *ops, …) {
    // offset < 0 → 从分区末尾偏移(读取 AVB Footer)
    get_abs_offset(partition, offset, &abs_offset);
    partition_read(partition, abs_offset, buffer, num_bytes);
    }

    AvbIOResult avb_hal_get_preloaded_partition(AvbOps *ops, …) {
    // 检查分区是否被预加载(提前读入内存加速)
    get_preload_partition_addr_sz(partition, &preload_addr, &preload_sz);
    if (成功) {
    *out_pointer = preload_addr; // 直接指向预加载地址,零拷贝
    }
    }

    写入操作(avb_hal_write_rollback_index)是空的:

    AvbIOResult avb_hal_write_rollback_index(AvbOps *ops, …) {
    /* otp update process is not implemented here. */
    return AVB_IO_RESULT_OK;
    }

    这意味着 OTP 版本号的更新不是在 AVB 验证过程中进行的,而是由 load_vfy_boot.c 中的 record_avb_version()(
    load_vfy_boot.c
    )在验证通过后通过 set_avb_otp_ver() 写入。

    Rollback Index 位置映射:

    Location

    OTP Group

    用途

    0

    AVB_GROUP (rollback_index[0])

    主 vbmeta 版本号

    1

    RECOVERY_GROUP (rollback_index[1])

    recovery 镜像版本号

    其他

    暂不支持

    5.5 设备锁状态 

    AvbIOResult avb_hal_read_is_device_unlocked(AvbOps *ops, bool *out_is_unlocked) {
    get_lock_state(&lock_state);

    switch (lock_state) {
    case LKS_DEFAULT: // 出厂默认
    case LKS_MP_DEFAULT: // MP 默认
    case LKS_LOCK: // 锁定
    *out_is_unlocked = FALSE;
    case LKS_UNLOCK: // 解锁
    *out_is_unlocked = TRUE;
    }
    }

    解锁检测影响两个行为:

  • avb_slot_verify() 内:解锁状态下 ALLOW_VERIFICATION_ERROR 使验证错误不阻断启动
  • load_vfy_boot() 末尾:解锁时 g_boot_state 被强制设为 ORANGE
  • 5.6 Partition 专用公钥验证 

    AvbIOResult avb_hal_validate_public_key_for_partition(…) {
    // 目前只支持 recovery 分区
    if (memcmp(partition, "recovery", strlen("recovery")) != 0)
    return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;

    // recovery rollback_index_location 固定为 1
    *out_rollback_index_location = 1;

    // 用 recovery 专用公钥验证
    pubk_sz = key_hdr->key_num_bits / 8;
    if (memcmp(g_avb_recovery_key, pubk, pubk_sz) == 0)
    *out_is_trusted = TRUE;
    }

    安全意义: recovery 分区使用独立的公钥 g_avb_recovery_key,与主系统分离。这在生产环境中可以让 recovery 镜像由不同的团队签名,或者在 recovery 模式下使用不同的密钥策略。

    六、完整描述符处理流程

    ├── descriptor[1]: AVB_DESCRIPTOR_TAG_CHAIN_PARTITION
    │ ├── partition_name: "vbmeta_system"
    │ ├── public_key: <system 专用公钥> ← 通常由 OEM 持有
    │ ├── rollback_index_location: 2
    │ │
    │ └── load_and_verify_vbmeta("vbmeta_system", system_pubkey, location=2)
    │ ├── ① avb_vbmeta_image_verify() ← 验签名
    │ ├── ② public_key 匹配 chain 指定的公钥 ← 确认是预期 key
    │ ├── ③ rollback_index[2] vs OTP ← 防回滚
    │ │
    │ └── descriptor[0]: AVB_DESCRIPTOR_TAG_HASHTREE
    │ └── 分区: system → dm-verity root hash

    ├── descriptor[2]: AVB_DESCRIPTOR_TAG_CHAIN_PARTITION
    │ ├── partition_name: "vbmeta_vendor"
    │ ├── public_key: <vendor 专用公钥> ← 通常由 SoC 厂商持有
    │ ├── rollback_index_location: 3
    │ │
    │ └── load_and_verify_vbmeta("vbmeta_vendor", vendor_pubkey, location=3)
    │ ├── ① avb_vbmeta_image_verify() ← 验签名 (跟 system 完全一样)
    │ ├── ② public_key 匹配 chain 指定的公钥 ← 用 vendor 的公钥
    │ ├── ③ rollback_index[3] vs OTP ← 独立的 rollback slot
    │ │
    │ └── descriptor[0]: AVB_DESCRIPTOR_TAG_HASHTREE
    │ └── 分区: vendor → dm-verity root hash

    Hashtree descriptor 的特殊处理

    // [avb_slot_verify.c:932-970]
    switch (desc.tag) {
    case AVB_DESCRIPTOR_TAG_HASH:
    load_and_verify_hash_partition(…);
    break;

    case AVB_DESCRIPTOR_TAG_HASHTREE:
    // LK 不做 hashtree 验证——这由内核 dm-verity 在运行时完成
    // LK 只需要验证 vbmeta 本身签名,确保 root hash 可信
    break;

    }

    设计原理: 如果 LK 对 system 分区(可能 2GB+)做完整哈希树验证,将极大延长启动时间。AVB 采用"验证签名 → 信任 root hash → 内核 dm-verity 按需校验"的模型:

  • LK 验证 vbmeta 签名 → 确认 root hash 未被篡改
  • LK 将 root hash 传递给内核
  • 内核 dm-verity 在运行时对每个块按需验证哈希树

  • 七、Boot State 裁决与错误处理

    7.1 状态转换图

    ┌──────────┐
    │ 启动开始 │
    │ RED │ ← 初始值
    └─────┬─────┘

    AVB 验证通过?
    ├── YES ─→ ┌──────────┐
    │ │ GREEN │
    │ └─────┬─────┘
    │ │
    │ 设备已解锁?
    │ ├── YES ─→ ORANGE (覆盖 GREEN)
    │ └── NO → 保持 GREEN

    └── NO → 设备已解锁?
    ├── YES ─→ ORANGE (允许继续)
    └── NO → RED (不能启动)

    特殊路径:
    YELLOW_STATE_SUPPORT + 自定义 key → YELLOW
    dm-verity corruption → 显示红色警告或重启

     7.2 handle_vboot_state() 

    uint32_t handle_vboot_state(uint32_t bootimg_type)
    {
    // 1. 打印启动状态
    print_boot_state(); // 输出到串口日志

    // 2. 显示警告画面 (如果状态非 GREEN)
    show_warning(partition_name);
    // GREEN → 不显示
    // ORANGE → "Your device is unlocked…"
    // YELLOW → "Your device is using a custom key…"
    // RED → "Your device is corrupt…"

    // 3. 将状态写入 cmdline
    set_boot_state_to_cmdline(); // androidboot.verifiedbootstate=green
    }

    7.3 dm-verity 错误处理 

    void dm_verity_handler(AvbHashtreeErrorMode hashtree_error_mode)
    {
    get_dm_verity_status(&status);

    // OTA 后首次启动 → 清除之前的错误记录
    if (check_ota_result() == TRUE) {
    clear_dm_verity_status();
    }

    // 发现 dm-verity 损坏
    if (status == DM_VERITY_GENERAL_ERROR ||
    hashtree_error_mode == AVB_HASHTREE_ERROR_MODE_EIO) {
    show_dm_verity_error();
    // → 显示 "Your device is corrupt."
    // → 等待按键或 5 秒后关机
    }
    }

    Hashtree Error Mode 的选择:

    void dm_verity_handler(AvbHashtreeErrorMode hashtree_error_mode)
    {
    get_dm_verity_status(&status);

    // OTA 后首次启动 → 清除之前的错误记录
    if (check_ota_result() == TRUE) {
    clear_dm_verity_status();
    }

    // 发现 dm-verity 损坏
    if (status == DM_VERITY_GENERAL_ERROR ||
    hashtree_error_mode == AVB_HASHTREE_ERROR_MODE_EIO) {
    show_dm_verity_error();
    // → 显示 "Your device is corrupt."
    // → 等待按键或 5 秒后关机
    }
    }

    MANAGED_RESTART_AND_EIO 状态机:

    第一次 dm-verity 损坏:
    ─→ 重启 → system 无法挂载 → OTA/Recovery
    ─→ persist 中记录 "dm-verity corruption"

    第二次启动 (检测到 persist 标记):
    ─→ 不重启 → 返回 EIO 给文件系统
    ─→ 显示 "Your device is corrupt."

    八、防回滚机制深度分析

    8.1 OTP 版本更新流程

    // [load_vfy_boot.c] record_avb_version()
    uint32_t record_avb_version(AvbSlotVerifyData *slot_data)
    {
    // 取所有 rollback_index 中的最小值
    min_ver = slot_data->rollback_indexes[0];
    for (i = 0; i < AVB_MAX_NUMBER_OF_ROLLBACK_INDEX_LOCATIONS; i++) {
    if (i == RECOVERY_ROLLBACK_INDEX) continue; // recovery 特殊处理
    if (rollback_indexes[i] < min_ver)
    min_ver = rollback_indexes[i];
    }

    // recovery 版本写入独立 OTP group
    if (rollback_indexes[RECOVERY_ROLLBACK_INDEX] 非初始值) {
    set_avb_otp_ver(RECOVERY_GROUP, rollback_indexes[RECOVERY_ROLLBACK_INDEX]);
    }

    // 主版本号写入 AVB OTP group
    set_avb_otp_ver(AVB_GROUP, (uint32_t)min_ver);
    }

    OTP (One-Time Programmable) efuse 特性:

    • 只能从 0 → 1(或 fuse 只能烧断)
    • 版本值只能增大,不能减小
    • 一旦写入,永久生效

    8.2 防降级攻击的保证

    攻击者尝试将系统从 v50 降级到 v30:

    1. OTP 中已存储 min_ver = 50
    2. 旧镜像的 vbmeta rollback_index = 30
    3. avb_slot_verify() 发现 30 < 50
    4. → AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX
    5. → 验证失败 → 设备处于 RED 状态 → 拒绝启动 ✓

    攻击者尝试绕过:
    – 修改 vbmeta 中的 rollback_index → 签名失效 ✓
    – 物理熔断 OTP → 不可逆,无法恢复 ✓
    – 绕过整个 AVB → LK 签名验证阻止修改后的 LK ✓

    8.3 Recovery 版本的特殊处理

    #ifdef MTK_OTP_FRAMEWORK_V2
    // 将 recovery 版本写入独立的 recovery OTP group
    set_avb_otp_ver(RECOVERY_GROUP, recovery_version);
    #else
    // 不分离: 将 recovery 版本纳入 min_ver 计算
    if (recovery_version < min_ver)
    min_ver = recovery_version;
    #endif

    MTK_OTP_FRAMEWORK_V2 将 recovery 的防回滚版本号写入独立的 OTP region,避免 recovery 的低版本拉低整个系统的 min_ver,导致设备即使刷了高版本 system 也无法启动。

    九、后处理与内核传递

    9.1 boot_post_processing()

    static uint32_t boot_post_processing(AvbOps *ops, uint32_t bootimg_type,
    AvbSlotVerifyData *slot_data)
    {
    // 1. 收集 Root of Trust 信息
    collect_rot_info(slot_data);
    // → MTK 实现: 通过 SMC 调用将 vbmeta digest 等传递给 TEE

    // 2. 重定位 boot 镜像到目标地址
    for (每个加载的分区) {
    memcpy(boot_load_addr, slot_data->loaded_partitions[i].data, …);
    }

    // 3. 准备 DTB
    load_bootinfo_bootimg(boot_load_addr);
    prepare_kernel_dtb();

    // 4. 追加 cmdline
    cmdline_append(CMDLINE_ROOT_SYSTEM_COMMON);

    // 5. 检查用户验证设置 (allow verification off)
    avb_user_verification_get(ops, suffix, &out_verification_enabled);

    // 6. ⭐ 将 AVB 元数据写入 DTB
    if (out_verification_enabled) {
    avb_cmdline_postprocessing(slot_data->cmdline, get_kernel_fdt());
    // → 从 cmdline 提取 vbmeta.hash_alg, vbmeta.size, vbmeta.digest
    // → 写入 /firmware/android/ 节点
    }

    // 7. 追加 slot 信息 (A/B)
    cmdline_append("androidboot.slot_suffix=_a");
    }

    9.2 DTB 写入

    static uint32_t avb_cmdline_postprocessing(char *cmdline, void *fdt)
    {
    // 1. 提取 avb 参数
    avb_extract_from_cmdline(cmdline, "androidboot.vbmeta.hash_alg");
    avb_extract_from_cmdline(cmdline, "androidboot.vbmeta.size");
    avb_extract_from_cmdline(cmdline, "androidboot.vbmeta.digest");

    // 2. 创建 /firmware/android 节点 (如不存在)
    fdt_add_subnode(fdt, 0, "firmware");
    fdt_add_subnode(fdt, firmware_off, "android");

    // 3. 写入 DTB
    fdt_setprop_string(fdt, android_off, "vbmeta.hash_alg", "sha256");
    fdt_setprop_string(fdt, android_off, "vbmeta.size", "4096");
    fdt_setprop_string(fdt, android_off, "vbmeta.digest", "<64-byte hex>");
    }

    内核启动后,可以通过 DTB 的 /firmware/android/ 节点直接读取 vbmeta digest,用于判断当前启动链是否可信。

    十、攻击面分析

    10.1 潜在攻击向量

    攻击向量

    可行性

    防护

    替换 vbmeta 镜像

    签名验证会失败 (RSA-2048)

    替换 boot 镜像

    hash descriptor 校验会失败

    降级 vbmeta 到旧版本

    rollback index OTP 保护

    物理读取 OTP

    OTP 设计上允许读取,但改写不可逆

    解锁 bootloader 绕过

    可行

    设计如此 — ORANGE 状态会显示警告

    替换 LK 绕过 AVB

    Preloader 验证 LK 签名

    物理篡改 eMMC 分区数据

    重启后会被检测到 → RED 状态

    10.2 MTK 专有签名与 AVB 的双重验证

    // mt_boot.c
    #if MTK_FORCE_VERIFIED_BOOT_SIG_VFY
    // 非 MTK 签名: AVB 验证在 boot_linux_from_storage 中完成
    g_boot_state = BOOT_STATE_RED;
    #else
    // MTK 专有签名 + AVB 双重验证
    if (0 != sec_boot_check(0)) // ← MTK img_auth_stor()
    g_boot_state = BOOT_STATE_RED;
    boot_linux_from_storage(); // ← AVB avb_slot_verify()
    #endif

    当 MTK_FORCE_VERIFIED_BOOT_SIG_VFY 未定义时,boot 分区必须同时通过 MTK 专有签名和 AVB 签名才能启动,提供了双重保险。

    10.3 竞争条件与 TOCTOU

    验证攻击面上的 TOCTOU(Time-of-Check-Time-of-Use):

    时间点:
    T1: load_vfy_boot() 从 eMMC 读取 boot 分区并验证 hash
    T2: DDoS/温度攻击 ← 理论上可以物理攻击,但极难
    T3: boot_linux() 跳转到已验证的 kernel 地址

    MTK 的缓解措施:
    – boot 分区加载到 DRAM 后,验证和启动使用同一内存地址
    – 验证通过后设置 g_boot_state,启动时不再重读分区
    – WDT 防卡死验证

    十一、总结与安全评估

    优势

  • 链式信任完整:BootROM → Preloader(MTK签名验证) → LK(MTK签名+AVB) → Kernel(dm-verity)
  • 公钥分离:vbmeta_system/vendor 可用不同私钥 (chain_public_key)
  • recovery 专用密钥:g_avb_recovery_key 独立于 g_avb_key
  • 防回滚双 OTP group:AVB_GROUP + RECOVERY_GROUP
  • TEE 交互:sec_set_pubk() 将公钥注入 TEE,防止内核态读取
  • 错误恢复机制:dm-verity MANAGED_RESTART_AND_EIO 避免无限重启
  • 局限性

  • YELLOW_STATE_SUPPORT 放宽:MTK_SECURITY_YELLOW_STATE_SUPPORT 下,不匹配的公钥仍被信任(out_is_trusted = TRUE),仅依赖用户看到警告
  • recovery 支持不完善:avb_hal_validate_public_key_for_partition() 写死为 "recovery",且 out_rollback_index_location 固定为 1
  • AVB2.0 代码分支:#if !defined(MTK_AVB20_SUPPORT) 下的 MTK 专有签名和 AVB 双路径增加了测试覆盖复杂度
  • ROT 收集为空:collect_rot_info() 被定义为 __weak 且函数体为空——需要 MTK 特定实现填充
  • 资源限制:MAX_NUMBER_OF_VBMETA_IMAGES = 32、MAX_NUMBER_OF_LOADED_PARTITIONS = 32——理论上 chain 深度有限
  • 总体结论

    MTK 的 LK 阶段 AVB 实现遵循 Google libavb 标准规范,在 HAL 层增加了 MTK 特有的 TEE 集成、双 OTP 防回滚、recovery 专用密钥等增强。其安全强度完全取决于 Preloader 对 LK 镜像是如何进行签名验证的——如果 Preloader 信任链被攻破,AVB 层再强的校验也会被替换后的 LK 绕过。在 Preloader 信任链完整的条件下,LK 的 AVB 为 Android 系统提供了密码学级别的启动完整性保护。
     
     

    赞(0)
    未经允许不得转载:171主机测评 » MTK安全启动分析13:LK 阶段的 Android Verified Boot (AVB) 校验
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址