Microdroide

Microdroid é um sistema operacional mini-Android que roda em um pVM. Você não precisa usar o Microdroid, você pode iniciar uma VM com qualquer sistema operacional. No entanto, os principais casos de uso para pVMs não são a execução de um sistema operacional independente, mas sim a oferta de um ambiente de execução isolado para executar uma parte de um aplicativo com garantias de confidencialidade e integridade mais fortes do que o Android pode fornecer.

Com os sistemas operacionais tradicionais, fornecer forte confidencialidade e integridade exige muito trabalho (muitas vezes duplicado), porque os sistemas operacionais tradicionais não se adaptam à arquitetura abrangente 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 de seu aplicativo no pVM, e a carga útil é construída na glibc. O aplicativo Android usa Bionic, a comunicação requer um protocolo personalizado sobre vsock e a depuração usando adb é um desafio.

O Microdroid preenche essas lacunas fornecendo uma imagem de sistema operacional pronta para uso, projetada para exigir o mínimo de esforço dos desenvolvedores para descarregar uma parte de seu aplicativo em um pVM. O código nativo é construído no Bionic, a comunicação acontece através do Binder e permite importar APEXes do Android e expõe um subconjunto da API do Android, como keystore para operações criptográficas com chaves apoiadas por hardware. No geral, os desenvolvedores devem considerar o Microdroid um ambiente familiar com as ferramentas às quais se acostumaram no sistema operacional Android completo.

Características

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

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

Microdroid não suporta:

  • APIs Java do Android nos pacotes android.\*

  • SystemServer e Zygote

  • Gráficos/IU

  • HALs

Arquitetura Microdroid

O Microdroid é semelhante ao Cuttlefish porque ambos possuem 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 de inicialização.
  • vendor_boot.img - Contém módulos de kernel específicos da VM, como virtio.
  • super.img - Consiste em partições lógicas de sistema e de fornecedor.
  • vbmeta.img – Contém metadados de inicialização verificados.

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

  • payload - Um conjunto de partições apoiadas por APEXes e APKs do Android
  • instance - Uma partição criptografada para persistir dados de inicialização verificados por instância, como salt por instância, chaves públicas APEX confiáveis ​​e contadores de reversão

Sequencia 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 no documento 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

Aqui está 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 verificar se ele é de uma fonte confiável (Google ou OEM).
    • Garante que o mesmo bootloader seja usado consistentemente em várias inicializações do mesmo pVM por meio do uso da imagem da instância. Especificamente, o pVM é inicializado inicialmente com uma imagem de instância vazia. pvmfw armazena a identidade do bootloader na imagem da instância e a criptografa. Portanto, na próxima vez que o pVM for inicializado com a mesma imagem da instância, o pvmfw descriptografará a identidade salva da imagem da instância e verificará se é a mesma que foi salva anteriormente. Se as identidades forem diferentes, o pvmfw se recusará a inicializar.

    O bootloader então inicializa o Microdroid.

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

  3. O bootloader verifica vbmeta e as partições encadeadas, como boot e super e, se for bem-sucedido, deriva os segredos do pVM do próximo estágio. Então, o Microdroid passa o controle para o kernel.

  4. Como a superpartição já foi verificada pelo bootloader (etapa 3), o kernel monta incondicionalmente a superpartição. Tal como acontece com o Android completo, a superpartição consiste em múltiplas partições lógicas montadas no dm-verity. O controle é então passado para o 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 Microdroid, que acessa a imagem da instância. O serviço gerenciador Microdroid descriptografa a imagem usando a chave passada no estágio anterior e lê as chaves públicas e contadores de reversão do APK e APEXes do cliente nos quais este pVM confia. Essas informações são usadas posteriormente por zipfuse e apexd quando eles montam o APK do cliente e solicitam APEXes, respectivamente.

  6. O serviço gerenciador Microdroid inicia 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 virtuais ( /dev/vdc1 , …), não de arquivos regulares ( /system/apex/*.apex ).

  8. zipfuse é o sistema de arquivos FUSE do Microdroid. zipfuse monta o APK do cliente, que é essencialmente um arquivo Zip como sistema de arquivos. Abaixo, o arquivo APK é passado como um dispositivo de bloco virtual pelo pVM com dm-verity, igual ao APEX. O APK contém um arquivo de configuração com uma lista de APEXes que o desenvolvedor do aplicativo solicitou para esta instância pVM. A lista é usada pelo apexd ao ativar APEXes.

  9. O fluxo de inicialização retorna ao serviço gerenciador do Microdroid. O serviço gerenciador então se comunica com VirtualizationService do Android usando o Binder RPC para que possa relatar eventos importantes, como travamento ou desligamento, e aceitar solicitações como o encerramento do pVM. O serviço gerenciador lê a localização do binário principal do 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 em AIDL) com acesso controlado pelo kernel do Android. AuthFS facilita funcionalidade semelhante para troca de arquivos entre endpoints mutuamente desconfiados através dos limites do pVM.

Fundamentalmente, 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 frontend, como um programa de leitura de arquivos executado em um pVM, detecte se o backend não confiável, normalmente o Android, violou o conteúdo do arquivo.

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

O transporte subjacente é atualmente baseado no Binder RPC, mas isso poderá mudar no futuro para otimizar o desempenho.

Gerenciamento de chaves

Os pVMs são fornecidos com uma chave de vedação estável adequada para dados persistentes protegidos e uma chave de atestado adequada para produzir assinaturas que são produzidas de forma verificável pelo pVM.

RPC do fichário

A maioria das interfaces do Android são expressas em AIDL , que é construído sobre o driver do kernel Binder Linux. Para suportar interfaces entre pVMs, o protocolo Binder foi reescrito para funcionar em soquetes, vsock no caso de pVMs. Operar sobre soquetes permite que as interfaces AIDL existentes do Android sejam usadas neste novo ambiente.

Para configurar a conexão, um terminal, como carga útil pVM, cria um objeto RpcServer , registra um objeto raiz e começa a escutar novas conexões. Os clientes podem se conectar a este servidor usando um objeto RpcSession , obter o objeto Binder e usá-lo exatamente como um objeto Binder é usado com o driver Binder do kernel.