Microdroid

O Microdroid é um SO mini-Android executado em uma pVM. Não é necessário usar o Microdroid. Você pode iniciar uma VM com qualquer SO. No entanto, os principais casos de uso para pVMs não são a execução de um SO independente, mas sim oferecer um ambiente de execução isolado para executar uma parte de um app com garantias de confidencialidade e integridade mais fortes do que o Android pode oferecer.

Com sistemas operacionais tradicionais, fornecer confidencialidade e integridade fortes exige muito trabalho (geralmente duplicado), porque os sistemas operacionais tradicionais não se encaixam na arquitetura geral do Android. Por exemplo, com a arquitetura padrão do Android, os desenvolvedores precisam implementar um meio de carregar e executar com segurança parte do app na pVM, e o payload é criado no glibc. O app Android usa o Bionic, a comunicação exige um protocolo personalizado sobre vsock, e a depuração usando o adb é desafiadora.

O Microdroid preenche essas lacunas fornecendo uma imagem de SO pronta para uso, projetada para exigir o mínimo de esforço dos desenvolvedores para descarregar uma parte do app em uma pVM. O código nativo é criado no Bionic, a comunicação acontece pelo Binder, e ele permite importar APEXes do Android host e expõe um subconjunto da API Android, como o keystore para operações criptográficas com chaves com suporte de hardware. No geral, os desenvolvedores vão achar o Microdroid um ambiente familiar com as ferramentas a que se acostumaram no SO Android completo.

Recursos

O Microdroid é uma versão simplificada do Android com alguns componentes adicionais específicos para pVMs. O Microdroid oferece suporte a:

  • Um subconjunto de APIs do NDK (todas as APIs para a implementação do libc e do Bionic do Android são fornecidas)
  • Recursos de depuração, como adb, logcat, tombstone e gdb
  • Inicialização verificada e SELinux
  • Carregamento e execução de um binário, junto com bibliotecas compartilhadas, incorporadas em um APK
  • RPC do Binder sobre vsock e troca de arquivos com verificações de integridade implícitas
  • Carregamento de APEXes

O Microdroid não oferece suporte a:

  • APIs Java do Android nos pacotes android.\*

  • SystemServer e Zygote

  • Gráficos/interface

  • HALs

Arquitetura do Microdroid

O Microdroid é semelhante ao Cuttlefish, porque ambos têm uma arquitetura semelhante ao Android padrão. O Microdroid consiste nas seguintes imagens de partição agrupadas em uma imagem de disco composta:

  • bootloader : verifica e inicia o kernel.
  • boot.img : contém o kernel e o ramdisk init.
  • vendor_boot.img : contém módulos de kernel específicos da VM, como o virtio.
  • super.img : consiste em partições lógicas do sistema e do fornecedor.
  • vbmeta.img : contém metadados de inicialização verificados.

As imagens de partição são enviadas no APEX de virtualização e são empacotadas em uma imagem de disco composta por VirtualizationService. Além da imagem de disco composta do SO principal, VirtualizationService é responsável por criar estas outras partições:

  • payload : um conjunto de partições com suporte dos APEXes e APKs do Android
  • instance : uma partição criptografada para persistir dados de inicialização verificados por instância , como sal por instância, chaves públicas APEX confiáveis e contadores de reversão

Sequência de inicialização

A sequência de inicialização do Microdroid ocorre após a inicialização do dispositivo. A inicialização do dispositivo é discutida na seção Firmware da pVM do documento de arquitetura. A figura 1 mostra as etapas que ocorrem durante a sequência de inicialização do Microdroid:

Fluxo de inicialização seguro da instância do microdroid

Figura 1. Fluxo de inicialização seguro da instância do Microdroid

Confira uma explicação das etapas:

  1. O bootloader é carregado na memória pelo crosvm e o pvmfw começa a ser executado. Antes de pular para o bootloader, o pvmfw executa duas tarefas:

    • Verifica o bootloader para conferir se ele é de uma fonte confiável (Google ou OEM).
    • Garante que o mesmo bootloader seja usado de forma consistente em várias inicializações da mesma pVM usando a imagem da instância. Especificamente, a pVM é inicializada com uma imagem de instância vazia. O pvmfw armazena a identidade do bootloader na imagem da instância e a criptografa. Assim, na próxima vez que a pVM for inicializada com a mesma imagem de instância, o pvmfw vai descriptografar a identidade salva da imagem de instância e verificar se ela é a mesma que foi salva anteriormente. Se as identidades forem diferentes, o pvmfw se recusará a inicializar.

    O bootloader inicializa o Microdroid.

  2. O bootloader acessa o disco da instância. Semelhante ao pvmfw, o bootloader tem uma unidade de disco de instância com informações sobre imagens de partição usadas nessa instância durante inicializações anteriores, incluindo a chave pública.

  3. O bootloader verifica o vbmeta e as partições encadeadas, como boot e super, e, se for bem-sucedido, deriva os segredos da pVM da próxima fase. Em seguida, o Microdroid entrega o controle ao kernel.

  4. Como a superpartição já foi verificada pelo bootloader (etapa 3), o kernel monta incondicionalmente a superpartição. Assim como no Android completo, a superpartição consiste em várias partições lógicas montadas no dm-verity. O controle é transmitido ao processo init, que inicia vários serviços nativos. O script init.rc é semelhante ao do Android completo, mas adaptado às necessidades do Microdroid.

  5. O processo init inicia o gerenciador do Microdroid, que acessa a imagem da instância. O serviço do gerenciador do Microdroid descriptografa a imagem usando a chave transmitida da fase anterior e lê as chaves públicas e os contadores de reversão do APK e dos APEXes do cliente em que essa pVM confia. Essas informações são usadas mais tarde por zipfuse e apexd quando eles montam o APK do cliente e os APEXes solicitados, respectivamente.

  6. O serviço do gerenciador do Microdroid inicia o apexd.

  7. apexd monta os APEXes nos diretórios /apex/<name>. A única diferença entre como o Android e o Microdroid montam APEXes é que, no Microdroid, os arquivos APEX vêm de dispositivos de bloco virtual (/dev/vdc1, …), não de arquivos normais (/system/apex/*.apex).

  8. zipfuse é o sistema de arquivos FUSE do Microdroid. zipfuse monta o APK do cliente, que é essencialmente um arquivo ZIP como um sistema de arquivos. Por baixo, o arquivo APK é transmitido como um dispositivo de bloco virtual pela pVM com dm-verity, assim como o APEX. O APK contém um arquivo de configuração com uma lista de APEXes que o desenvolvedor de apps solicitou para essa instância de pVM. A lista é usada pelo apexd ao ativar APEXes.

  9. O fluxo de inicialização retorna ao serviço do gerenciador do Microdroid. O serviço do gerenciador se comunica com o VirtualizationService do Android usando o RPC do Binder para que ele possa informar eventos importantes, como falha ou desligamento, e aceitar solicitações, como encerrar a pVM. O serviço do gerenciador lê o local do binário principal no arquivo de configuração do APK e o executa.

Troca de arquivos (AuthFS)

É comum que os componentes do Android usem arquivos para entrada, saída e estado e os transmitam como descritores de arquivo (tipo ParcelFileDescriptor no AIDL) com acesso controlado pelo kernel do Android. O AuthFS facilita funcionalidades semelhantes para trocar arquivos entre endpoints mutuamente desconfiados em limites de pVM.

Fundamentalmente, o AuthFS é um sistema de arquivos remoto com verificações de integridade transparentes em operações de acesso individuais, semelhante ao fs-verity. As verificações permitem que o front-end, como um programa de leitura de arquivos executado em uma pVM, detecte se o back-end não confiável, normalmente o Android, adulterou o conteúdo do arquivo.

Para trocar arquivos, o back-end (fd\_server) é iniciado com a configuração por arquivo, especificando se ele é destinado à entrada (somente leitura) ou saída (leitura e gravação). Para entrada, o front-end exige que o conteúdo corresponda a um hash conhecido, além de uma árvore de Merkle para verificação no acesso. Para saída, o AuthFS mantém internamente uma árvore de hash do conteúdo observado nas operações de gravação e pode impor a integridade quando os dados são lidos novamente.

O transporte subjacente é baseado no RPC do Binder, mas isso pode mudar no futuro para otimizar a performance.

Gerenciamento de chaves

As pVMs recebem uma chave de vedação estável adequada para proteger dados persistentes e uma chave de atestado adequada para produzir assinaturas que são verificavelmente produzidas pela pVM.

RPC do Binder

A maioria das interfaces do Android é expressa em AIDL, que é criada com base no driver do kernel do Binder Linux. Para oferecer suporte a interfaces entre pVMs, o protocolo do Binder foi reescrito para funcionar em soquetes, vsock no caso de pVMs. A operação em soquetes permite que as interfaces AIDL atuais do Android sejam usadas nesse novo ambiente.

Para configurar a conexão, um endpoint, como o payload da pVM, cria um objeto RpcServer, registra um objeto raiz e começa a ouvir novas conexões. Os clientes podem se conectar a esse servidor usando um objeto RpcSession, receber o objeto Binder e usá-lo exatamente como um objeto Binder é usado com o driver do Binder do kernel.