Microdroid 是一个在 pVM 中运行的迷你 Android 操作系统。您不必使用 Microdroid,可以使用任何操作系统启动虚拟机。但是,pVM 的主要用例不是运行独立的操作系统,而是提供一个隔离的执行环境,用于运行应用的某一部分,与 Android 可以提供的保证相比,它具有更强的机密性和完整性保证。
对于传统的操作系统,提供强大的机密性和完整性需要相当多的工作(通常是重复的),因为传统的操作系统与总体 Android 架构不符。例如,使用标准的 Android 架构,开发者需要在 pVM 中实现安全加载和执行应用一部分的方法,并且有效负载是针对 glibc 构建的。Android 应用使用 Bionic,通信需要通过 vsock 使用自定义协议,并且使用 adb 进行调试具有挑战性。
Microdroid 通过提供现成的操作系统镜像来填补这些空白,该镜像旨在最大限度地减少开发者将应用的一部分卸载到 pVM 中所需的工作量。原生代码是针对 Bionic 构建的,通信通过 Binder 进行,并且它允许从宿主 Android 导入 APEX,并公开 Android API 的一个子集,例如用于使用硬件支持的密钥进行加密操作的密钥库。总的来说,开发者应该会发现 Microdroid 是一个熟悉的环境,其中包含他们在完整 Android 操作系统中已经习惯的工具。
功能
Microdroid 是 Android 的精简版本,其中包含一些特定于 pVM 的附加组件。Microdroid 支持
- NDK API 的子集(提供用于 Android 的 libc 和 Bionic 实现的所有 API)
- 调试功能,例如 adb、logcat、tombstone 和 gdb
- Verified Boot 和 SELinux
- 加载和执行二进制文件,以及嵌入在 APK 中的共享库
- 通过 vsock 的 Binder RPC 以及具有隐式完整性检查的文件交换
- 加载 APEX
Microdroid 不支持
`
android.*
` 包中的 Android Java APISystemServer 和 Zygote
图形/UI
HAL
Microdroid 架构
Microdroid 与 Cuttlefish 类似,因为两者都具有类似于标准 Android 的架构。Microdroid 由以下分区镜像组成,这些镜像组合在一个复合磁盘镜像中
- `
bootloader
` - 验证并启动内核。 - `
boot.img
` - 包含内核和 init ramdisk。 - `
vendor_boot.img
` - 包含 VM 特定的内核模块,例如 virtio。 - `
super.img
` - 由系统和供应商逻辑分区组成。 - `
vbmeta.img
` - 包含已验证启动元数据。
分区镜像随 Virtualization APEX 一起发布,并由 VirtualizationService
打包在复合磁盘镜像中。除了主操作系统复合磁盘镜像之外,VirtualizationService
还负责创建以下其他分区
payload
- 一组由 Android APEX 和 APK 支持的分区instance
- 一个加密分区,用于持久保存每个实例的已验证启动数据,例如每个实例的盐值、可信 APEX 公钥和回滚计数器
启动顺序
Microdroid 启动顺序在设备启动之后发生。“设备启动”在架构文档的 pVM 固件部分中讨论。图 1 显示了 Microdroid 启动顺序期间发生的步骤
以下是步骤说明
引导加载程序由 crosvm 加载到内存中,pvmfw 开始执行。在跳转到引导加载程序之前,pvmfw 执行两项任务
- 验证引导加载程序,以检查它是否来自可信来源(Google 或 OEM)。
- 通过使用实例镜像,确保同一引导加载程序在同一 pVM 的多次启动中一致使用。具体而言,pVM 最初使用空的实例镜像启动。pvmfw 将引导加载程序的身份存储在实例镜像中并对其进行加密。因此,下次使用同一实例镜像启动 pVM 时,pvmfw 会从实例镜像中解密已保存的身份,并验证它是否与之前保存的身份相同。如果身份不同,pvmfw 将拒绝启动。
然后,引导加载程序启动 Microdroid。
引导加载程序访问实例磁盘。与 pvmfw 类似,引导加载程序有一个实例磁盘驱动器,其中包含有关先前启动期间在此实例中使用的分区镜像的信息,包括公钥。
引导加载程序验证 vbmeta 和链接的分区(例如
boot
和super
),如果成功,则派生下一阶段的 pVM 密钥。然后,Microdroid 将控制权交给内核。由于超级分区已由引导加载程序验证(步骤 3),因此内核无条件地挂载超级分区。与完整 Android 一样,超级分区由通过 dm-verity 挂载的多个逻辑分区组成。然后,控制权传递给
init
进程,该进程启动各种原生服务。init.rc
脚本与完整 Android 的脚本类似,但针对 Microdroid 的需求进行了定制。init
进程启动 Microdroid 管理器,该管理器访问实例镜像。Microdroid 管理器服务使用从上一阶段传递的密钥解密镜像,并读取此 pVM 信任的客户端 APK 和 APEX 的公钥和回滚计数器。此信息稍后由zipfuse
和apexd
在挂载客户端 APK 和请求的 APEX 时使用。Microdroid 管理器服务启动
apexd
。apexd
将 APEX 挂载到/apex/<name>
目录。Android 和 Microdroid 挂载 APEX 的唯一区别在于,在 Microdroid 中,APEX 文件来自虚拟块设备 (/dev/vdc1
, …),而不是来自常规文件 (/system/apex/*.apex
)。zipfuse
是 Microdroid 的 FUSE 文件系统。zipfuse
挂载客户端 APK,它本质上是将 Zip 文件作为文件系统。在底层,APK 文件由 pVM 作为带有 dm-verity 的虚拟块设备传递,与 APEX 相同。APK 包含一个配置文件,其中包含应用开发者为此 pVM 实例请求的 APEX 列表。该列表由apexd
在激活 APEX 时使用。启动流程返回到 Microdroid 管理器服务。然后,管理器服务使用 Binder RPC 与 Android 的
VirtualizationService
通信,以便它可以报告重要事件(如崩溃或关机),并接受请求(例如终止 pVM)。管理器服务从 APK 的配置文件中读取主二进制文件的位置并执行它。
文件交换 (AuthFS)
Android 组件通常使用文件作为输入、输出和状态,并将它们作为文件描述符(AIDL 中的 ParcelFileDescriptor
类型)传递,访问权限由 Android 内核控制。AuthFS 为在跨 pVM 边界的互不信任的端点之间交换文件提供类似的功能。
从根本上讲,AuthFS 是一个远程文件系统,对各个访问操作进行透明的完整性检查,类似于 fs-verity
。这些检查允许前端(例如在 pVM 中运行的文件读取程序)检测不受信任的后端(通常是 Android)是否篡改了文件内容。
为了交换文件,后端 (fd\_server
) 使用每个文件的配置启动,指定它是用于输入(只读)还是输出(读写)。对于输入,前端强制内容与已知哈希匹配,并在访问时进行 Merkle 树验证。对于输出,AuthFS 在内部维护从写入操作观察到的内容的哈希树,并且可以在数据读回时强制执行完整性。
底层传输目前基于 Binder RPC,但将来可能会更改以优化性能。
密钥管理
pVM 配备了稳定的密封密钥(适用于保护持久性数据)和证明密钥(适用于生成可验证是由 pVM 生成的签名)。
Binder RPC
大多数 Android 接口都在 AIDL 中表达,AIDL 构建在 Binder Linux 内核驱动程序之上。为了支持 pVM 之间的接口,Binder 协议已被重写为通过套接字工作,对于 pVM,则为 vsock。通过套接字操作允许在此新环境中使用 Android 现有的 AIDL 接口。
为了建立连接,一个端点(例如 pVM payload)创建一个 RpcServer
对象,注册一个根对象,并开始侦听新连接。客户端可以使用 RpcSession
对象连接到此服务器,获取 Binder
对象,并像使用内核 Binder 驱动程序一样使用 Binder
对象。