Android P中的AVB校验

news/2024/11/29 19:40:54/

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中包含的分区都必须校验通过才认为是校验成功。

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.0Header Block:             256 bytesAuthentication Block:     576 bytesAuxiliary Block:          3456 bytesAlgorithm:                SHA256_RSA4096Rollback Index:           0Flags:                    0Release String:           'avbtool 1.1.0'Descriptors:Chain Partition descriptor:Partition Name:          systemRollback Index Location: 2Public key (sha1):       cdbb77177f731920bbe0a0f94f84d9038ae0617dChain Partition descriptor:Partition Name:          recoveryRollback Index Location: 1Public key (sha1):       2597c218aae470a130f61162feaae70afd97f011Hash descriptor:Image Size:            35553280 bytesHash Algorithm:        sha256Partition Name:        bootSalt:                  baa1ce5d7db69d1b3943a78b5b142ae4d77b4ed60b9885c8661e845172b29a13Digest:                ec7cb1ad89fed3104a03191434d5487b5b4acc78e6bfeb84d7178af40df7db75Flags:                 0Hashtree descriptor:Version of dm-verity:  1Image Size:            1056714752 bytesTree Offset:           1056714752Tree Size:             8327168 bytesData Block Size:       4096 bytesHash Block Size:       4096 bytesFEC num roots:         2FEC offset:            1065041920FEC size:              8421376 bytesHash Algorithm:        sha1Partition Name:        vendorSalt:                  abbf0829ed7bc08913b83f9a994a37ad2a85b5e9Root Digest:           39a22a035ebff2d339dc682603adedb91da01374Flags:                 0Hash descriptor:Image Size:            176641 bytesHash Algorithm:        sha256Partition Name:        dtboSalt:                  386837807aa5a7d9cbe51e7f768009f4e5fca5190af4b3e856a7c96a96c33e0aDigest:                dabdbe5be19c38a3428efd046182d215f8522ab7cd3804e84f196fe73e9052f7Flags:                 0

因此受到影响的只有boot/dtbo/recovery,recovery这里是Chain Partition descriptor,但是解析recovery.img发现它校验数据也是生成的Hash,而不是Hashtree的方式。 RequestedPartition代表的是需要被校验的hash分区,只有其中包含的Hash分区,那么才会被校验。因此根据boot mode方式的不同,需要校验的hash分区也不同,正常启动需要校验:boot/dtbo,recovery启动需要校验:recovery/dtbo,而其他分区的校验方式是一样的。

init

针对init中的校验,由于我们已经boot成功到上层了,虽然该函数依然会轮询vbmeta包含的各个分区,但是此时kernel和dtbo都已经加载完毕了,没有必要在对Hash分区进一步进行校验了,所以我们可以传入RequestedPartition为空指针,这样init当检测到是Hash descriptor会直接return OK,并继续下一个分区的校验。
init中和AVB相关的主要是进行校验和挂载分区的功能,终极目的还是为了挂载,可以分开来看,有firststagemount和secondstagemount两种方式,第一种方式是为了挂载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;}

禁止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中进行的。

禁止dm-verity功能

两种方法:

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

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


http://www.ppmy.cn/news/382814.html

相关文章

Android P 如何挂载system镜像到根目录

Android O/P 版本以来&#xff0c;谷歌加入了A/B system的特性&#xff0c;此时ramdisk和system是一起放在同一个system.img镜像中的。而系统起来之后也就不存在system分区了&#xff0c;而是直接把system镜像挂载到/根目录上。那么这个操作是怎么进行的呢&#xff1f; system.…

[转载型] Neutron 系列 (17): Neutron 分布式虚拟路由【上】

http://www.aboutyun.com/forum.php?modviewthread&tid16860&highlightneutron%2B%2B%CF%B5%C1%D0 1.路由的相关知识有哪些&#xff1f; 2.Neutron 的传统和 DVR Router是什么&#xff1f; 3.DVR的功能有哪些&#xff1f; Neutron 作为 OpenStack 一个基础性关键服务&…

[连载型] Neutron 系列 (17): Neutron 分布式虚拟路由【上】

问题导读&#xff1a; 1.路由的相关知识有哪些&#xff1f; 2.Neutron 的传统和 DVR Router是什么&#xff1f; 3.DVR的功能有哪些&#xff1f; Neutron 作为 OpenStack 一个基础性关键服务&#xff0c;高可用性&#xff08;HA&#xff09;和扩展性是它的基本需求之一。对 neut…

DVR

eutron 系列 (17): Neutron 分布式虚拟路由【上】 [复制链接] 电梯直达 楼主 发表于 2016-1-4 14:56:36 | 只看该作者 |只看大图 大数据系列零基础由入门到实战视频大优惠 本帖最后由 eying 于 2016-1-13 16:59 编辑 问题导读&#xff1a; 1.路由的相关知识有哪些&#x…

Neutron 分布式虚拟路由(Neutron Distributed Virtual Routing)

本系列会分析OpenStack 的高可用性&#xff08;HA&#xff09;概念和解决方案&#xff1a; &#xff08;1&#xff09;OpenStack 高可用方案概述 &#xff08;2&#xff09;Neutron L3 Agent HA - VRRP &#xff08;虚拟路由冗余协议&#xff09; &#xff08;3&#xff09;…

错误 解决“Unknown class in Interface Builder file”

1. 在任意一个源文件中使用一下这个class&#xff0c;比如NSLog(”%”, classA); 2. 在project的Info里面的Link Flag处&#xff0c;增加-all_load 转载于:https://www.cnblogs.com/bandy/archive/2012/04/20/2459326.html

华云数据携首款通用型云操作系统安超OS™正式发布

当前数字化和智能化正在不断改变企业的发展方式&#xff0c;云计算已成为企业数字化转型的必然选择&#xff0c;而系统云化是个复杂的系统工程&#xff0c;涉及到繁多场景需求&#xff0c;务必需要构建一个软硬件资源统一管理、调度实现数据中心资源整合、智能调性、业务优化的…

VC++编译错误信息

VC编译错误信息总结 0、 error C2180: controlling expression has type void 定义方式不应该为void类型&#xff1b; 1、fatal error C1010: unexpected end of file while looking for precompiled header directive。 寻找预编译头文件路径时遇到了不该遇到的文件尾。(一般…