Android P中的AVB校验(一)

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: Android P中的AVB校验(一)


1、avb校验库的主入口

avb校验功能主要是由external/avb/libavb库实现的,该库主要完成的工作包括各个分区镜像的校验签名验证,以及vbmeta数据的解析,包括了各种flags的处理以及dm-verity所需要的参数解析。avb校验库的主入口为:

avb_slot_verify(AvbOps* ops,
                 const char* const* requested_partitions,
                 const char* ab_suffix,
                 AvbSlotVerifyFlags flags,
                 AvbHashtreeErrorMode hashtree_error_mode,
                 AvbSlotVerifyData** out_data)

以高通平台为例,avb校验在一次启动过程中总共进行了两次,第一次是在bootloader中进行校验,通过上面的接口校验各个分区的根hash和签名。第二次是在上层init中进行的,这次校验也是调用相同的接口,可能个别传入的参数会有不同。(这里再前面的宏观篇讲的很清楚)

之所以在init中再做一次的原因是什么呢?

因为我们挂载分区和dm-verity相关的参数都在镜像的vbmeta结构中保存,因此可以使用该接口进行解析和挂载,但是该接口和校验功能是捆绑的,所以必须要再进行一次校验。

不管是在bootloader还是在上层init中,所有校验都是从vbmeta分区开始,然后读取其中包含的各个分区的信息,然后依次循环读取并校验其他的各个分区,所有vbmeta.img中包含的分区都必须校验通过才认为是校验成功。(有个链式的逻辑)

1、bootloader

首先上代码,看到代码别慌,前辈博客有说明,没有的话,我也有。

CONST CHAR8 *RequestedPartitionMission[] = {"boot", "dtbo", NULL};
CONST CHAR8 *RequestedPartitionRecovery[] = {"recovery", "dtbo", NULL};
if ((!Info->MultiSlotBoot) &&
         Info->BootIntoRecovery) {
   RequestedPartition = RequestedPartitionRecovery;
   NumRequestedPartition = ARRAY_SIZE (RequestedPartitionRecovery) - 1;
   if (Info->NumLoadedImages) {
     /* fastboot boot option, skip Index 0, as boot image already
      * loaded */
     RequestedPartition = &RequestedPartitionRecovery[1];
   }
} else {
   RequestedPartition = RequestedPartitionMission;
   NumRequestedPartition = ARRAY_SIZE (RequestedPartitionMission) - 1;
   if (Info->NumLoadedImages) {
     /* fastboot boot option, skip Index 0, as boot image already
      * loaded */
     RequestedPartition = &RequestedPartitionMission[1];
   }
}
if (Info->NumLoadedImages) {
  NumRequestedPartition--;
}
if (FixedPcdGetBool (AllowEio)) {
  VerityFlags = IsEnforcing () ? AVB_HASHTREE_ERROR_MODE_RESTART
                               : AVB_HASHTREE_ERROR_MODE_EIO;
} else {
  VerityFlags = AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE;
}
Result = avb_slot_verify (Ops, RequestedPartition, SlotSuffix, VerifyFlags,
                          VerityFlags, &SlotData);

avb校验是所有分区都要进行校验的,这里传入的RequestedPartition并不代表要校验的分区,进一步跟进可以发现最终和RequestedPartition只会对Hash descriptor的解析有影响,其他类型的descriptor解析方法都是一样的。

高通平台的vbmeta.img包含如下信息:

Minimum libavb version:   1.0
 Header Block:             256 bytes
 Authentication Block:     576 bytes
 Auxiliary Block:          3456 bytes
 Algorithm:                SHA256_RSA4096
 Rollback Index:           0
 Flags:                    0
 Release String:           'avbtool 1.1.0'
 Descriptors:
     Chain Partition descriptor:
       Partition Name:          system
       Rollback Index Location: 2
       Public key (sha1):       cdbb77177f731920bbe0a0f94f84d9038ae0617d
     Chain Partition descriptor:
       Partition Name:          recovery
       Rollback Index Location: 1
       Public key (sha1):       2597c218aae470a130f61162feaae70afd97f011
     Hash descriptor:
       Image Size:            35553280 bytes
       Hash Algorithm:        sha256
       Partition Name:        boot
       Salt:                  baa1ce5d7db69d1b3943a78b5b142ae4d77b4ed60b9885c8661e845172b29a13
       Digest:                ec7cb1ad89fed3104a03191434d5487b5b4acc78e6bfeb84d7178af40df7db75
       Flags:                 0
     Hashtree descriptor:
       Version of dm-verity:  1
       Image Size:            1056714752 bytes
       Tree Offset:           1056714752
       Tree Size:             8327168 bytes
       Data Block Size:       4096 bytes
       Hash Block Size:       4096 bytes
       FEC num roots:         2
       FEC offset:            1065041920
       FEC size:              8421376 bytes
       Hash Algorithm:        sha1
       Partition Name:        vendor
       Salt:                  abbf0829ed7bc08913b83f9a994a37ad2a85b5e9
       Root Digest:           39a22a035ebff2d339dc682603adedb91da01374
       Flags:                 0
     Hash descriptor:
       Image Size:            176641 bytes
       Hash Algorithm:        sha256
       Partition Name:        dtbo
       Salt:                  386837807aa5a7d9cbe51e7f768009f4e5fca5190af4b3e856a7c96a96c33e0a
       Digest:                dabdbe5be19c38a3428efd046182d215f8522ab7cd3804e84f196fe73e9052f7
       Flags:                 0

因此受到影响的只有boot/dtbo/recovery,recovery这里是Chain Partition descriptor,但是解析recovery.img发现它校验数据也是生成的Hash,而不是Hashtree的方式。

RequestedPartition代表的是需要被校验的hash分区,只有其中包含的Hash分区,那么才会被校验。因此根据boot mode方式的不同,需要校验的hash分区也不同,正常启动需要校验:boot/dtbo,recovery启动需要校验:recovery/dtbo,而其他分区的校验方式是一样的。

2、init

针对init中的校验,由于我们已经boot成功到上层了,虽然该函数依然会轮询vbmeta包含的各个分区,但是此时kernel和dtbo都已经加载完毕了,没有必要在对Hash分区进一步进行校验了,所以我们可以传入RequestedPartition为空指针,这样init当检测到是Hash descriptor会直接return OK,并继续下一个分区的校验。

init中和AVB相关的主要是进行校验和挂载分区的功能,终极目的还是为了挂载,可以分开来看,有firststagemountsecondstagemount两种方式:(AVB源码系列有讲这个)

第一种方式是为了挂载vendor分区,需要通过libavb对system/vendor进行校验,system分区已经由kernel挂载过了,因此这里只需要利用dm-verity参数挂载vendor分区,因为vendor中包含了很多init rc文件,所以vendor必须要在这些rc执行之前被挂载上,所以必须要在第一阶段把vendor分区挂载起来,然后在init的第二阶段去执行init rc。

第二种方式是其他分区的挂载,主要是通过读取fstab中的挂载项进行挂载,这就和之前版本的android挂载方式一样了。

需要注意的是,由于第一阶段的挂载,我们没有办法通过fstab文件传递给init,因为fstab在vendor中,而vendor还没有被挂载起来,所以我们可以通过device tree的方式来传递fstab参数这样init在第一阶段通过读取device tree来获取要在第一阶段挂载的分区,也就是我们的vendor分区,由于avb和dm-verity是绑定的,所以还需要传入对应的vbmeta节点,目的是开启dm-verity功能,同时也会对vbmeta.img中存在的各个分区进行校验。

当然我们校验是传入RequestedPartition为空指针,以跳过Hash分区的校验,只进行其他分区的校验。

这里需要特别注意的是device tree中的vbmeta节点的配置,必须要和vbmeta.img中所包含的分区一致,因为这个匹配校验也会做,否则会报校验失败,从而无法启动android。

firmware: firmware {
    android {
        compatible = "android,firmware";
        vbmeta {
            compatible = "android,vbmeta";
            parts = "vbmeta,boot,system,vendor,dtbo,recovery";
        };
        fstab {
            compatible = "android,fstab";
            vendor {
                compatible = "android,vendor";
                dev = "/dev/block/platform/soc/8804000.sdhci/by-name/vendor";
                type = "ext4";
                mnt_flags = "ro,barrier=1,discard";
                fsmgr_flags = "wait,avb";
                status = "ok";
            };
        };
    };
};

这个vbmeta中包含"vbmeta,boot,system,vendor,dtbo,recovery"这几个分区,恰好和上面解析出来的一直,否则会报如下错误:

[ 11.254728] init: init first stage started!
[ 11.260572] init: Using Android DT directory /proc/device-tree/firmware/android/
[ 11.274173] init: [libfs_mgr]fs_mgr_read_fstab_default(): failed to find device default fstab
[ 11.463441] init: [libfs_mgr]by-name symlink not found for partition: 'recovery'
[ 11.471083] init: [libfs_mgr]avb_slot_verify failed, result: 2

这是一个去掉recovery分区的一个报错实例。

init调用libavb库的主入口函数如下:

AvbSlotVerifyResult FsManagerAvbOps::AvbSlotVerify(const std::string& ab_suffix,
                                                   AvbSlotVerifyFlags flags,
                                                   AvbSlotVerifyData** out_data) {
    // Invokes avb_slot_verify() to load and verify all vbmeta images.
    // Sets requested_partitions to nullptr as it's to copy the contents
    // of HASH partitions into handle>avb_slot_data_, which is not required as
    // fs_mgr only deals with HASHTREE partitions.
    const char* requested_partitions[] = {nullptr};
    // The |hashtree_error_mode| field doesn't matter as it only
    // influences the generated kernel cmdline parameters.
    return avb_slot_verify(&avb_ops_, requested_partitions, ab_suffix.c_str(), flags,
                           AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE, out_data);
}

可以看出,其传入的requested_partitions确实是为空nullptr,这样就会跳过Hash分区校验。

解析vbmeta数据中的Hash descriptor:

switch (desc.tag) {
   case AVB_DESCRIPTOR_TAG_HASH: {
     AvbSlotVerifyResult sub_ret;
     sub_ret = load_and_verify_hash_partition(ops,
                                              requested_partitions,
                                              ab_suffix,
                                              allow_verification_error,
                                              descriptors[n],
                                              slot_data);
     if (sub_ret != AVB_SLOT_VERIFY_RESULT_OK) {
       ret = sub_ret;
       if (!allow_verification_error || !result_should_continue(ret)) {
         goto out;
       }
     }
   } break;

load_and_verify_hash_partition中判断requested_partitions是否存在对应的分区名,不存在则返回OK跳转:

/* Don't bother loading or validating unless the partition was
   * requested in the first place.
   */
  found = avb_strv_find_str(requested_partitions,
                            (const char*)desc_partition_name,
                            hash_desc.partition_name_len);
  if (found == NULL) {
    ret = AVB_SLOT_VERIFY_RESULT_OK;
    goto out;
  }

load_and_verify_hash_partition中判断requested_partitions是否存在对应的分区名,不存在则返回OK跳转:

/* Don't bother loading or validating unless the partition was
   * requested in the first place.
   */
  found = avb_strv_find_str(requested_partitions,
                            (const char*)desc_partition_name,
                            hash_desc.partition_name_len);
  if (found == NULL) {
    ret = AVB_SLOT_VERIFY_RESULT_OK;
    goto out;
  }

2、禁止verification校验功能

通过我的另一篇文章《Android P 如何挂载system镜像到根目录》的介绍,大家应该会有所了解,vbmeta.img中保存有一个flags:

typedef enum {
   AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED = (1 << 0),
   AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED = (1 << 1)
 } AvbVBMetaImageFlags;

该flags会有两个bit,分别代表是否使能dm-verity和verification功能,本文介绍的AVB校验功能就是verification使能的时候才会进行。

我们可以通过修改此bit位来禁止AVB校验功能,利用fastboot重新刷写vbmeta.img:

fastboot --disable-verification flash vbmeta vbmeta.img

跳过AVB的代码如下所示,在函数avb_slot_verify中:

if (toplevel_vbmeta.flags & AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED) {
  /* Since verification is disabled we didn't process any
   * descriptors and thus there's no cmdline... so set root= such
   * that the system partition is mounted.
   */
  avb_assert(slot_data->cmdline == NULL);
  slot_data->cmdline =
      avb_strdup("root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)");
  if (slot_data->cmdline == NULL) {
    ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
    goto fail;
  }
} else {
  ...... //verification operation
}

这里发现禁止校验之后,函数就结束了,校验操作在else中进行的。

3、禁止dm-verity功能

两种方法:

adb disable-verity
fastboot --disable-verity flash vbmeta vbmeta.img

最终都是更新了vbmeta分区中保存的对应的flags来确定是否禁止dm-verity的。

目录
相关文章
|
6月前
|
安全 Linux Android开发
Android安全启动学习(一):AVB校验是什么?
Android安全启动学习(一):AVB校验是什么?
412 0
|
6月前
|
Android开发
Android P中的AVB校验(二)
Android P中的AVB校验(二)
181 0
|
6月前
|
安全 Android开发
Android AVB的校验宏观的两个阶段
Android AVB的校验宏观的两个阶段
175 0
|
6月前
|
存储 安全 Android开发
Android安全启动学习(三):AVB校验的内容、怎么校验、AVB的作用
Android安全启动学习(三):AVB校验的内容、怎么校验、AVB的作用
513 0
|
Android开发
【Android 逆向】ELF 文件格式 ( ELF 文件当前版本号 | 操作系统 ABI 信息 | ABI 版本 | 文件头校验 | 文件头长度信息 )
【Android 逆向】ELF 文件格式 ( ELF 文件当前版本号 | 操作系统 ABI 信息 | ABI 版本 | 文件头校验 | 文件头长度信息 )
300 0
【Android 逆向】ELF 文件格式 ( ELF 文件当前版本号 | 操作系统 ABI 信息 | ABI 版本 | 文件头校验 | 文件头长度信息 )
|
数据安全/隐私保护 Android开发
android 校验用户名密码手机邮箱身份证邮编等
android 校验用户名密码手机邮箱身份证邮编等
|
Android开发
Android 悬浮窗权限校验
原文:Android 悬浮窗权限校验 悬浮窗权限: 权限检验和请求: //检查是否已经授予权限,大于6.
1727 0
|
Android开发 数据安全/隐私保护 安全
|
5天前
|
搜索推荐 Android开发 开发者
探索安卓开发中的自定义视图:打造个性化UI组件
【10月更文挑战第39天】在安卓开发的世界中,自定义视图是实现独特界面设计的关键。本文将引导你理解自定义视图的概念、创建流程,以及如何通过它们增强应用的用户体验。我们将从基础出发,逐步深入,最终让你能够自信地设计和实现专属的UI组件。