Android Verify Boot
Android 启动时验证
Table of contents
后台
启动时验证会尽力确保所有已执行代码均来自可信来源(通常是设备的 OEM),
以防受到攻击或损坏。它可建立一条从受硬件保护的信任根到引导加载程序,再
到 boot 分区和其他已验证分区(包括 system、vendor 和可选的 oem 分区)
的完整信任链。在设备启动过程中,无论是在哪个阶段,都会在进入下一个阶段
之前先验证下一个阶段的完整性和真实性。
除了确保设备运行的是安全的 Android 版本外,启动时验证还会检查是否存在
内置了回滚保护的正确 Android 版本。回滚保护可确保设备只会更新到更高的
Android 版本,从而帮助避免可能的漏洞持续存在。
除了验证操作系统外,启动时验证还允许 Android 设备将其完整性状态传达给
用户。
背景
Android 4.4 增加了对启动时验证和 dm-verity 内核功能的支持。这种验证功能
组合就是启动时验证 1。
以前的 Android 版本会在发现设备损坏时向用户发出警告,但仍然允许他们启
动设备;从 Android 7.0 开始,系统会严格强制执行启动时验证,从而使遭到
入侵的设备无法启动。Android 7.0 还增加了对前向纠错功能的支持,有助于更
可靠地防范非恶意数据损坏。
Android 8.0 及更高版本包含 Android 启动时验证 (AVB),AVB 是启动时验证
的一个参考实现,可与 Project Treble 配合使用。除了与 Treble 配合使用外,
AVB 还对分区脚本格式进行了标准化并增添了回滚保护功能。
设备状态
Table of contents
更改设备状态
信任根
o 可由用户设置的信任根
设备状态用于指明能够以多大的自由度将软件刷写到设备上,以及是否强制执行
验证。设备状态为 LOCKED 和 UNLOCKED。状态为 LOCKED 的设备禁止您将新软
件刷写到设备上,而状态为 UNLOCKED 的设备允许您进行修改。
当设备开机后,引导加载程序会先检查设备状态是 LOCKED 还是 UNLOCKED。如
果设备状态为 UNLOCKED,引导加载程序会向用户显示警告,然后继续启动,即
使加载的操作系统不是由信任根签名也是如此。
如果设备状态为 LOCKED,引导加载程序会完成验证启动中的步骤,验证该设备
的软件。只有在加载的操作系统是由信任根正确签名时,状态为 LOCKED 的设备
才会启动。如需了解详情,请参阅启动流程。
更改设备状态
如需更改设备状态,请使用 fastboot flashing [unlock | lock] 命令。为
了保护用户数据,只要设备状态发生变化,都会先清除 data 分区中的数据,并
会在删除数据之前要求用户确认。
当用户购买二手开发设备后,应该将设备状态从 UNLOCKED 改为 LOCKED。锁定
设备后,只要没有警告,用户应该就能确信设备处于设备制造商开发的状态。如
果开发者出于开发目的希望停用设备上的验证功能,应该将设备状态
从 LOCKED 改为 UNLOCKED。
信任根
信任根是用于为设备上存储的 Android 副本签名的加密密钥。信任根的不公开
部分只有设备制造商才知道,用于为旨在分发的每个 Android 版本签名。信任
根的公开部分嵌入在设备中并存储在一个不会被篡改的位置(通常是只读存储
区)。
加载 Android 时,引导加载程序会使用信任根来验证真实性。如需详细了解此
流程,请参阅验证启动。设备可能具有多个引导加载程序,因此可能有多个加密
密钥。
可由用户设置的信任根
设备可以根据需要选择允许用户配置信任根(例如,公钥)。设备可以使用此可
由用户设置的信任根(而非内置的信任根)进行启动时验证。这样,用户既可以
安装并使用自定义的 Android 版本,又不会牺牲启动时验证这项安全改进功能。
如果实现了可由用户设置的信任根,则应满足以下要求:
需要进行物理确认才能设置/清除可由用户设置的信任根。
可由用户设置的信任根只能由最终用户设置,而不能在出厂时或在最终用户获得设备之前的
任意中间点设置。
可由用户设置的信任根存储在防篡改的存储空间中。“防篡改”是指可以检测到 Android 数
据是否遭到篡改(例如,数据是否被覆盖或更改)。
如果设置了可由用户设置的信任根,则设备应该允许启动使用内置信任根或可由用户设置的
信任根签名的 Android 版本。
设备每次使用可由用户设置的信任根启动时,系统都应通知用户设备正在加载自定义的
Android 版本。如需查看警告屏幕示例,请参阅状态为 LOCKED 并已设置自定义密钥的设
备。
实现可由用户设置的信任根的一种方法是,将虚拟分区设置为仅当设备处
于 UNLOCKED 状态时才能刷写或清除。Google Pixel 2 设备使用此方法以及名
为 avb_custom_key 的虚拟分区。avbtool extract_public_key 命令会输出
此分区中数据的格式。以下示例展示了如何设置可由用户设置的信任根:
avbtool extract_public_key --key key.pem --output pkmd.bin
fastboot flash avb_custom_key pkmd.bin
可由用户设置的信任根可通过发出以下命令来清除:
fastboot erase avb_custom_key
验证启动
Table of contents
回滚保护
处理验证错误
对于要启动的 Android 版本中包含的所有可执行代码和数据,启动时验证均要
求在使用前以加密形式对其进行验证,其中包括内核(从 boot 分区加载)、设
备树(从 dtbo 分区加载)、system 分区和 vendor 分区等。
对于 boot 和 dtbo 这类仅读取一次的小分区,通常是通过将整个内容加载到内
存中,然后计算其哈希值来进行验证。接下来,系统会将计算出的哈希值与预期
哈希值进行比较。如果值不一致,则 Android 将无法加载。如需了解详情,请
参阅启动流程。
内存装不下的较大分区(如文件系统)可能会使用哈希树;在这种情况下,验证
流程会在将数据加载到内存的过程中持续进行。对于这种情况,系统会在运行时
计算哈希树的根哈希值,并将其与预期根哈希值进行比较。Android 包含用于验
证较大分区的 dm-verity 驱动程序。如果在某个时刻计算出的根哈希值与预期根
哈希值不一致,系统便不会使用相应数据,而且 Android 会出现错误。如需了
解详情,请参阅 dm-verity 损坏。
预期哈希值通常存储在每个经过验证的分区的末尾或开头、专用分区中,或同时
存储在以上两个位置。最重要的是,这些哈希值已由信任根以直接或间接的方式
签名。举个例子,AVB 实现就支持这两种方式;如需了解详情,请参阅 Android
启动时验证。
回滚保护
即使更新流程完全安全,攻击者仍可能会利用非永久性 Android 内核漏洞来手
动安装更易受攻击的旧版 Android 系统,重新启动进入易受攻击的版本,然后
通过该 Android 版本来安装永久性漏洞。在这种情况下,攻击者可通过这种漏
洞永久拥有相应设备,并可以执行任何操作(包括停用更新)。
防范这类攻击的保护措施称为“回滚保护”。“回滚保护”通常通过以下方式实现:
使用防篡改的存储空间来记录最新的 Android 版本,并在 Android 版本低于记
录的版本时拒绝启动 Android。系统通常会针对每个分区来跟踪版本。
如需详细了解 AVB 处理回滚保护的方式,请参阅 AVB README。
处理验证错误
验证在启动时(例如,如果在 boot 分区上计算出的哈希值与预期哈希值不一致)
或运行时(例如,如果 dm-verity 在 system 分区上遇到验证错误)都可能会失
败。如果验证在启动时失败,设备则无法启动,而且最终用户需要执行相关步骤
才能恢复设备使用。
如果验证在运行时失败,恢复流程就会更复杂一些。如果设备使用的是
dm-verity,则应在 restart 模式下进行配置。在 restart 模式下,如果遇到验
证错误,设备会立即重启,并设置特定标记以表明错误原因。引导加载程序应该
会注意到该标记,并将 dm-verity 切换为使用 I/O 错误 (eio) 模式并保持该模
式,直到安装新的更新为止。
在 eio 模式下启动时,设备会显示错误屏幕,以通知用户系统已检测到损坏,
而且设备可能无法正常使用。该屏幕会持续显示,直到用户将其关闭为止。
在 eio 模式下,如果遇到验证错误,dm-verity 驱动程序不会重启设备,而是会
返回 EIO 错误,并且相应的应用需要处理该错误。
这样做的目的是,让系统更新程序能够正常运行(以便安装不含损坏错误的新操
作系统),或者让用户能够从设备上获取尽可能多的数据。安装新的操作系统后,
引导加载程序会注意到新安装的操作系统,并切换回 restart 模式。
启动流程
Table of contents
Flow for A/B devices
Communicating Verified Boot state to users
LOCKED devices with custom root of trust
UNLOCKED devices
dm-verity corruption
o
o
o
建议的设备启动流程如下所示:
图 1. 启动时验证流程
适用于 A/B 设备的流程
如果设备使用的是 A/B,则启动流程略有不同。必须先使用启动控件 HAL 将要
启动的槽位标记为 SUCCESSFUL,然后再更新回滚保护元数据。
如果平台更新失败(未标记 SUCCESSFUL),A/B 堆栈便会回退至仍具有先前
Android 版本的其他槽位。不过,如果已设置回滚保护元数据,则之前的版本会
因回滚保护而无法启动。
将启动时验证状态传达给用户
确定设备的启动状态后,您需要将该状态传达给用户。如果设备没有任何问题,
则会继续运行,而不显示任何内容。启动时验证问题分为以下几类:
黄色:针对设有自定义信任根的已锁定设备的警告屏幕
橙色:针对未锁定设备的警告屏幕
红色 (EIO):针对 dm-verity 损坏的警告屏幕
红色(未找到操作系统):未找到有效的操作系统
具有自定义信任根的已锁定设备
黄色屏幕示例:
如果设备处于已锁定状态,已设置自定义的信任根,并且映像已使用该自定义信
任根进行签名,则会在每次启动时显示黄色屏幕。黄色屏幕会在 10 秒钟后关
闭,并且设备会继续启动。如果用户按下电源按钮,“按电源按钮即可暂停”文字
便会更改为“按电源按钮即可继续”,并且该屏幕永远都不会关闭,但设备可能会
调暗或关闭该屏幕以防老化。如果再次按下按钮,该屏幕便会关闭,并且手机会
继续启动。
对于十六进制数字,请使用用于验证的 libavb 表示形式公钥的 sha256 的前 8
位数字,例如 d14a028c。
建议的文字:
您的设备加载了不同的操作系统。
请在其他设备上访问此链接以了解详情:
g.co/ABH
ID:十六进制数字
power_settings_new 按电源按钮即可暂停
已解锁的设备
橙色屏幕示例: