secureboot入门-6安卓AVB安全启动基础
安全启动的第一部分就是ATF里面但是到了BL33uboot的时候之后就是第二部分linux里面了。但是linux的启动需要很多额外的小东西例如dtb分区、system分区、vendor分区、oem分区等比较复杂。所以从uboot之后的安全启动就可以借鉴Android现成的AVBAndroid Verified Boot机制。1. Android镜像基础1.1 分区介绍cache.img缓存镜像用于存储系统或用户应用产生的临时数据。vendor.img包含所有不可分发给 Android 开源项目 (AOSP) 的二进制文件。如果没有专有信息则可以省略此分区。misc.imgmisc 分区供恢复映像使用存储空间不能小于 4KB。userdata.imguserdata 分区包含用户安装的应用和数据包括自定义数据。vbmeta.img用于安全验证bootloader验证vbmeta的签名再用vbmeta的key以及hash值验证dtbo/boot/system/vendor。后面AVB要用system.imgandroid系统镜像系统镜像是地址ROM最常使用的一个镜像用于存储Android系统的核心文件System.img就是设备中system目录的镜像里面包含了Android系统主要的目录和文件。一般这些文件是不允许修改的。关于android的recovery.imgrecovery分区的镜像一般用作系统恢复刷机。boot.imgLinux内核镜像Android系统中通常会把zImage 内核镜像uImage文件 和ramdisk.img打包到一起生成一个boot.img镜像文件放到boot分区由bootloader来引导启动其启动过程本质也是和分开的uImageramdisk.img类似只不过把两个镜像按照一定的格式合并为一个镜像而已。ramdisk.img内存磁盘镜像是根文件系统android启动时首先加载ramdisk.img镜像并挂载到/目录下并进行了一系列的初始化动作包括创建各种需要的目录初始化console开启服务等尽管ramdisk.img需要放在Linux内核镜像boot.img中但却属于Android源代码的一部分。实际上Android的编译系统生成了三部分内容android 9以后boot.img仅包含正常启动内核。recovery.img包含恢复内核和恢复 ramdisk.img。system.img包含原始 system.img 和 ramdisk.img 的合并内容boot.imgAndroid系统中通常会把zImage 内核镜像uImage文件 和ramdisk.img打包到一起生成一个boot.img镜像文件放到boot分区由bootloader来引导启动其启动过程本质也是和分开的uImageramdisk.img类似只不过把两个镜像按照一定的格式合并为一个镜像而已。现在是将ramdisk.img与system.img放到一起了。1.2 启动流程bootloader会从boot分区开始启动。Boot分区的格式是固定的首先是2K或者4K的 文件头后面跟着用gzip压缩过的内核再后面是ramdisk根文件系统然后是第二阶段的载入程序可选recovery.imgrecovery分区的镜像一般用作系统恢复注zImage文件通过mkimage命令给zImage文件加上了64个字节的数据头得到uImage文件这样才能被u-boot识别BootLoader的启动通常分为两个阶段。其实Bootloader主要的必须的作用只有一个就是把操作系统映像文件拷贝到RAM中去然后跳转到它的入口处去执行我们称之为启动加载模式该过程没有用户的介入是它正常工作的模式。它的步骤如下Stage1:硬件设备初始化。为stage2的执行及随后内核的执行准备好基本的硬件环境 为加载stage2 准备ram空间。为了获得更好的执行速度通常吧stage2加载到ram中执行 复制stage2的代码到ram中 设置好堆栈 跳转到stage2的c程序入口Stage2初始化本阶段要使用的硬件设备 检测系统内存映射 将内核映像和根文件系统映像从flash读到ram中 为内核设置启动参数 调用内核Kernel负责启动各个子系统例如CPU调度子系统和内存管理子系统等等。Kernel启动完成之后就会将Ramdisk镜像安装为根系统并且在其中找到一个init文件将其启动为第一个进程。init进程启动就意味着系统进入到用户空间执行了这时候各种用户空间运行时以及守护进程就会被加载起来。最终完成整个系统的启动过程。android加载这3个镜像文件后会把 system和 userdata分别加载到ramdisk文件系统 中的system和 data目录下。1.3 A/B分区Android从7.0开始引入新的OTA升级方式A/B System Updates这里将其叫做A/B系统。A/B系统就是设备上有A和B两套可以工作的系统用户数据只有一份为两套系统共用简单来讲可以理解为一套系统分区另外一套为备份分区。其系统版本可能一样也可能不一样其中一个是新版本另外一个旧版本通过升级将旧版本也更新为新版本。当然设备出厂时这两套系统肯定是一样的。AVB 的设计旨在与 A/B 配合使用要求描述符中存储的任何分区名称中都不得使用 A/B 后缀。以下是具有两个插槽的示例请注意不同插槽之间的回滚索引有何不同 - 对于插槽 A回滚索引为 [42, 101]而对于插槽 B回滚索引为[43, 103]。系统会在非易失的存储中保存着一份“slot metadata”用它来管理当前要从哪个slot启动“slot metadata”记录了上一次开机是从哪个slot启动每个slot可以尝试启动的次数每个slot是否成功启动过的状态每个slot是否已经被认为是损坏的状态根据“slot metadata”于是在U-boot下就有如下状态机用于决定这次要启动哪个slot如下图int ab_select_slot(struct blk_desc *dev_desc, struct disk_partition *part_info, bool dec_tries){2. AVB简介2.1 AVB介绍上面提到了安卓的镜像分区那么这些镜像就需要进行安全校验了就是下面我们要介绍的AVBAndroid Verified Boot。这么多分区怎么校验google想到了这样的设计通过增加一个独立的分区这个分区包括了其他分区的重要校验信息只要保证这个vbmeta的足够安全uboot去验证作为根那么vbmeta中包含的其他分区的信息也就足够安全。uboot初始化完成后按照AVB标准流程加载、验证和启动Linux内核镜像boot.img然后Linux基于dm-verity机制对根文件系统system.img进行分块验证和访问。校验分为两类小分区可以整个加载到内存里面计算hash值进行校验例如boot和dtbo这类大分区内存装不下这么大的分区例如文件系统这时候需要使用hash树进行分解校验一部分一部分加载到内存利用树状结构进行校验。dm-verity驱动就是干这个事情的可以启动时校验也可以运行时校验。AVB2.0被用于启动引导此用法添加一个“vbmeta.img”镜像。public key被编译到bootloader中用于校验vbmeta数据vbmeta.img包含应由此public key验证的签名。vbmeta.img包含用于验证的public key但只有bootloader验证过vbmeta.img才会可信就好比认证一样包含可信public key和签名。因此我们在AVB中有两个重要key一个验证vbmeta.img的OEM key一个验证其他分区(boot/system/vendor)的verity key。当然可以使用OEM key作为verity key。我们知道OEM key用于在bootloader阶段验证vbmeta.img。这还不够我们必须验证其他分区vbmeta.img包含的public key用于此目的。就像avb1.0中verity key一样此public key用于验证system、vendor分区和boot分区。这里有些不同之处avb1.0使用OEM key验证boot分区使用verity key验证system/vendor分区但avb2.0使用OEM key验证vbmeta.img并使用其中包含的public key验证其他分区(system/vendor/boot等)。启动时bootloader将验证两个分区一个是使用OEM key验证vbmeta.img一个是使用vbmeta.img所包含的public key验证boot分区而system/vendor分区由init/fs_mgr来验证(使用vbmeta.img所包含的public key)。(注意从1.0到2.0的顺序变化)总结就是Uboot验证vbmeta分区vbmeta验证boot.imguboot阶段启动内核和init进程init进程拉起来dm-verity驱动使用Hash树来运行时验证其他分区。uboot中已经支持开源的libavb库只需要调用接口就可以进行验证vbmeta.img和boot.img2.2 VBMeta分区AVB2.0增加了一个vbmeta分区对应的vbmeta.img由make_vbmeta_image工具编译生成的其主要包含如下三大部分vbmeta image header(256Bytes)authentication datahashsignature auxiliary data public key public key metadata descriptorshashdescriptors hashtree descriptors chain partition descriptorsvbmeta分区保存了受保护分区的所有信息每个被avb2.0保护的分区后面都有一个vbmeta结构。vbmeta结构中包含多个描述符(和其他元数据)并且所有这些数据都被加密签名。受保护的分区可以配置为hash分区或者chain(链式)分区hash分区hash校验用hash描述符中hash(保存在vbmeta分区的vbmeta结构里)验证目标分区看目标生成的保存我这里的一样不chain分区key校验用chain分区描述符中的public key(保存在vbmeta分区的vbmeta结构里)验证目标分区vbmeta结构的完整性(vbmeta被private key签名)VBMeta 摘要是所有 VBMeta 结构的摘要包括根结构例如在分区中vbmeta和链接分区中的所有 VBMeta 结构。此摘要可以在构建时使用计算avbtool calculate_vbmeta_digest也可以在运行时使用avb_slot_verify_data_calculate_vbmeta_digest()函数计算。它也可以在内核命令行上设置为androidboot.vbmeta.digestavb_slot_verify()有关确切详细信息请参阅文档。此摘要可与加载的操作系统中的用户空间一起使用libavb以验证加载的 vbmeta 结构的真实性。如果信任根和/或存储的回滚索引仅在引导加载程序中运行时可用则这很有用。此外如果 VBMeta 摘要包含在硬件支持的证明数据中则依赖方可以提取摘要并将其与已知良好操作系统的摘要列表进行比较如果找到则可以为应用程序正在运行的设备提供额外的保证。2.2.1 chain分区key校验AVB 中使用的中心数据结构是VBMeta 结构。此数据结构包含许多描述符和其他元数据所有这些数据都经过加密签名。描述符用于图像哈希、图像哈希树元数据和所谓的链式分区key0来自bootloader的oem_pubk其中主vbmeta分区在哈希描述符中保存boot分区的哈希对于system和vendor分区哈希表在文件系统之后主vbmeta分区在哈希表描述符中保存哈希表的root hash、salt和offset。这里想起前面的哈希树没–roothash因为vbmeta分区中的vbmeta结构是以密码方式签名的所以bootloader可以检测签名并验证它是有key0的所有者(例如通过嵌入key0的公共部分)创建的从而信任于boot、system和vendor。链式分区描述符用于委托权限——它包含委托权限的分区名称以及该特定分区上的签名所信任的public key。bootsystemvendorvbmetavbmetakey0key0bootsystemvendor2.2.2 hash分区hash校验链式分区描述符用于委派权限- 它包含委派权限的分区的名称以及受信任的公钥可用于此特定分区上的签名在此设置中xyz分区具有用于完整性检查的哈希树。哈希树后面是 VBMeta 结构其中包含带有哈希树元数据根哈希、盐、偏移量等的哈希树描述符并且此结构已用 签名key1。最后在分区的末尾是一个页脚其中包含 VBMeta 结构的偏移量。此设置允许引导加载程序使用链分区描述符来查找分区末尾的页脚使用链分区描述符中的名称这反过来有助于定位 VBMeta 结构并验证它是否由key1使用key1_pub存储在链分区描述符中的签名。至关重要的是因为有一个带有偏移量的页脚所以xyz可以更新分区而无需vbmeta对分区进行任何更改。VBMeta 结构非常灵活允许任何分区的哈希描述符和哈希树描述符存在于该vbmeta分区、用于完整性检查的分区通过链分区描述符或任何其他分区通过链分区描述符中。这允许广泛的组织和信任关系。链接分区不需要使用页脚 - 允许链接分区指向 VBMeta 结构位于开头的分区例如就像分区一样vbmeta。这对于整个组织拥有的分区的所有哈希和哈希树描述符都存储在专用分区中的用例非常有用例如vbmeta_google。在此示例中的哈希树描述system符位于vbmeta_google分区中这意味着引导加载程序根本不需要访问该分区如果将分区作为逻辑分区进行管理例如通过LVM 技术或类似技术system这将很有帮助。system2.2.3 AVBkeyAVB key 是一对非对称密钥。私钥用来签名公钥用来验证。如关系如图所示2.3 dm-veritydevice-mapper-verity简称dm-verity用于大分区例如文件系统的校验。1、能不能将多个硬盘映射成一个逻辑的硬盘那样我们程序就不用关心复杂的地址问题了也不用关系是哪个device了DM-raid技术RAID全称为独立磁盘冗余阵列(Redundant Array of Independent Disks)2、将某个地址段的数据进行加密只有授权方式才可访问比如FDE。DM-crypt技术3、访问存储介质上的数据时校验下是否被篡改过。DM-verity技术。DM就是Device-Mapper的缩写也就说上述的想法都可以基于Device Mapper实现Device Mapper可不仅仅实现了这些还包括LVM2、DM-multipach等。dm-verity是内核子系统的Device Mapper中的一个子模块所以在介绍dm-verity之前先要介绍一下Device Mapper的基础知识。Device Mapper为Linux内核提供了一个从逻辑设备到物理设备的映射框架通过它用户可以定制资源的管理策略。当前Linux中的逻辑卷管理器如LVM2Linux Volume Manager 2、EVMSEnterprise Volume Mageagement System、dmraid等都是基于该机制实现的。一堆陌生词汇扫盲点安排上了Device Mapper有三个重要的概念映射设备Mapped Device、映射表、目标设备Target Device映射设备是一个逻辑块设备用户可以像使用其他块设备那样使用映射设备。映射设备通过映射表描述的映射关系和目标设备建立映射。对映射设备的读写操作最终要映射成对目标设备的操作。而目标设备本身不一定是一个实际的物理设备它可以是另一个映射设备如此反复循环理论上可以无限迭代下去。映射关系本质上就是表明映射设备中的地址对应到哪个目标设备的哪个地址。无限套娃Device Mapper是一个灵活的架构映射设备映射一个或多个目标设备每个目标设备属于一个类型类型不同对I/O处理不同构造目标设备的方法也不同。映射设备可以映射多个不同类型的目标设备。dm-verity类型的设备需要两个底层设备一个是数据设备顾名思义是用来存储数据实际上就是要保障完整性的设备另一个是哈希设备用来存储哈希值在校验数据设备完整性时需要。2.4 Hash树哈希树hash treeMerkle tree在密码学及计算机科学中是一种树形数据结构每个叶节点均以数据块的哈希作为标签而除了叶节点以外的节点则以其子节点标签的加密哈希作为标签 。哈希树能够高效、安全地验证大型数据结构的内容。哈希树的顶部为顶部哈希top hash亦称根哈希root hash或主哈希master hash。只要任一叶节点有变化根哈希都会变。在比特币区块里所有交易都按照Merkle Tree的格式组织起来再跟区块头里的hashMerkleTreeRoot对应起来就可以保证本区块交易信息的不可篡改。2.5 回滚保护AVB 包含回滚保护用于防范已知的安全漏洞。每个 VBMeta 结构都嵌入了一个回滚索引如下所示这些数字被称为rollback_index[n]随着安全漏洞的发现和修复每个图像的数字都会增加。此外设备将最后看到的回滚索引存储在防篡改存储中并且这些被称为stored_rollback_index[n]。回滚保护是指让设备拒绝图像除非rollback_index[n]stored_rollback_index[n]所有n并且让设备随时间增加。具体如何做到这一点将在更新存储的回滚索引stored_rollback_index[n]部分讨论。参考https://developer.aliyun.com/article/1411144?spma2c6h.14164896.0.0.624047c5tzrw2iscm20140722.S_community文章1411144._.ID_1411144-RL_Android安全启动学习-LOC_searchUNDcommunityUNDitem-OR_ser-V_3-P0_1https://android.googlesource.com/platform/external/avbhttps://wx.comake.online/doc/doc/SigmaStarDocs-SSD238X-Android-20240712/platform/Android/bootflow.html后记学的越多不知道的越多忘记的越多。这么多“多”如果事无巨细把这些知识都一次学习到位都记到脑子里面是不现实的就像这篇博客笔者也是参考了很多资料并不是完全自己写的只能算是学习别人的技术所以自己第一遍写出来也是有“无力感”自己也没完全100%理解和考证。但是如果有朝一日要调试这块功能可以投入巨量的时间把代码从头到尾捋一遍而且把文章中的这些描述汉字或者官网https://android.googlesource.com/platform/external/avb 都研究N遍那未尝不能成为专家。真是那句“好书要读多遍才能消化”。对于业余选手掌握到什么程度比较合适–画出一个原理图甚至就是逻辑图。就像你的方案去参加评审一堆架构和领导去评审他们真的懂你的技术么他们或许不懂但是他们都是懂逻辑啊你的流程图中肯定不能有逻辑破绽或者他们强大的各方面都思考到的能力从各个维度来进行评价。俗话说一拍脑袋只要觉得合理什么都敢干第一性原则乱拳打死老师傅或许这也是创新。这里也涉及一个内功功力的问题技术到一定程度就是大道相通有最基础的理论基础其他都是衍生。从各种技术和代码中抽取出来的应该就是方案能画出来讲出来对比其他方案优缺点都列出来就是功力高深啊所谓“大道至简”。“啥都懂一点啥都不精通干啥都能干干啥啥不是专业入门劝退堪称程序员杂家”。欢迎各位有自己公众号的留言申请转载纯干货持续更新欢迎分享给朋友、点赞、收藏、在看、划线和评论交流“那路谈OS与SoC嵌入式软件”欢迎关注个人文章汇总https://thatway1989.github.io