Guia de desenvolvimento para enclaves de segurança baseada em virtualização (SBV)

Este guia de desenvolvimento descreve como criar, assinar e depurar um enclave de SBV básico.

Pré-requisitos

Para começar a usar os enclaves de SBV, você precisa atender aos seguintes requisitos:

  • Visualize e cumpra os requisitos do dispositivo na Visão geral de enclaves de SBV.
  • Visualize e cumpra os pré-requisitos de desenvolvimento na Visão geral de enclaves de SBV.
    • É recomendável instalar a carga de trabalho de desenvolvimento na área de trabalho com C++ por meio do instalador do Visual Studio. Ele instalará todas as ferramentas necessárias, incluindo o Software Development Kit do Windows (SDK do Windows).
  • Baixe o código de exemplo no GitHub. Ele demonstra o ciclo de vida de um enclave de SBV, incluindo como fazer chamadas de função para o enclave.
    • Cada enclave deve ter um aplicativo host. O código de exemplo contém uma solução do Visual Studio com dois projetos: o host de enclave e o enclave de teste.

Introdução

Depois de cumprir os pré-requisitos acima, você deve ser capaz de abrir o arquivo de solução da amostra VbsEnclave no Visual Studio e compilá-lo. Ele cria um aplicativo de teste junto com o enclave correspondente. No entanto, você não pode executar seu aplicativo com êxito até que o enclave seja assinado com um certificado válido.

Este guia detalha como criar um enclave de SBV básico em seu computador de desenvolvimento. As etapas para criar um enclave de SBV são:

  1. Gravar uma DLL de enclave de SBV e um aplicativo host correspondente
  2. Compilar a DLL e o host
  3. Assinar a DLL do enclave de SBV
  4. Depurar o enclave de SBV

Vamos começar entendendo o ciclo de vida de um enclave. As APIs do enclave são chamadas na seguinte ordem:

Diagrama ilustrando a ordem em que as APIs de enclaves de SBV são chamadas

Etapa 1: Escrever enclaves de SBV

Vamos examinar o código de exemplo e entender como escrever um aplicativo que emprega um enclave de SBV.

Escrever o host do enclave

Lembre-se de que uma DLL de enclave de SBV é simplesmente uma DLL e, portanto, requer um aplicativo host. O aplicativo host nada mais é do que um aplicativo do Windows padrão. Para empregar enclaves de SBV, o host deve usar APIs de enclave do Windows do cabeçalho enclaveapi.h. A inclusão de windows.h em seu aplicativo host fornecerá acesso a essas APIs.

Escrever a DLL para carregar em um enclave de teste

Consulte o código de exemplo no projeto Enclave de teste para seguir as etapas abaixo.

Em nosso enclave Amostra, criamos um enclave simples que aplica XOR na entrada com 0xDADAF00D e retorna o resultado. Vamos detalhar como fazemos isso:

  1. Comece incluindo winenclave.h. No código de exemplo, consulte Samples/VbsEnclave/Test enclave/precomp.h:

    #include <winenclave.h>
    

    winenclave.h é o arquivo de inclusão central para enclaves de SBV e inclui windows.h, ntenclv.h e winenclaveapi.h.

  2. Cada DLL carregada em um enclave requer uma configuração. Essa configuração é definida usando uma variável global const chamada __enclave_config do tipo IMAGE_ENCLAVE_CONFIG. No código de exemplo, consulte Samples/VbsEnclave/Test enclave/enclave.c:

    const IMAGE_ENCLAVE_CONFIG __enclave_config = {
        sizeof(IMAGE_ENCLAVE_CONFIG),
        IMAGE_ENCLAVE_MINIMUM_CONFIG_SIZE,
        IMAGE_ENCLAVE_POLICY_DEBUGGABLE,    // DO NOT SHIP DEBUGGABLE ENCLAVES TO PRODUCTION
        0,
        0,
        0,
        { 0xFE, 0xFE },    // family id
        { 0x01, 0x01 },    // image id
        0,                 // version
        0,                 // SVN
        0x10000000,        // size
        16,                // number of threads
        IMAGE_ENCLAVE_FLAG_PRIMARY_IMAGE
    };
    

    Observação

    É permitido apenas uma imagem primária por enclave. Se você carregar várias imagens primárias, a que for carregada primeiro será tratada como primária e as demais serão tratadas como dependências. Neste exemplo, não há dependências além das DLLs da plataforma de enclave.

  3. A função DllMain() é obrigatória e define o ponto de entrada no enclave. É chamado durante InitializeEnclave(). No código de exemplo, consulte Samples/VbsEnclave/Test enclave/enclave.c.

    BOOL
    DllMain(
        _In_ HINSTANCE hinstDLL,
        _In_ DWORD dwReason,
        _In_ LPVOID lpvReserved
    )
    {
        UNREFERENCED_PARAMETER(hinstDLL);
        UNREFERENCED_PARAMETER(lpvReserved);
    
        if (dwReason == DLL_PROCESS_ATTACH) {
            InitialCookie = 0xDADAF00D;
        }
    
        return TRUE;
    }
    
  4. Todas as funções dentro do enclave que são chamadas a partir do aplicativo host devem ser exportadas e ser do tipo LPENCLAVE_ROUTINE. A assinatura de função deverá ser assim:

    void* CALLBACK enclaveFunctionName(_In_ void* Context)
    

    No código de exemplo, consulte Samples/VbsEnclave/Test enclave/enclave.c.

    void*
    CALLBACK
    CallEnclaveTest(
        _In_ void* Context
    )
    {
        WCHAR String[32];
        swprintf_s(String, ARRAYSIZE(String), L"%s\n", L"CallEnclaveTest started");
        OutputDebugStringW(String);
    
        return (void*)((ULONG_PTR)(Context) ^ InitialCookie);
    }
    

    Observação

    Somente as funções exportadas pela imagem primária do enclave podem ser acessadas a partir do aplicativo host.

    Em seguida, você pode exportar a função usando o arquivo .DEF. No código de exemplo, consulte Samples/VbsEnclave/Test enclave/vbsenclave.def. Para obter mais informações, consulte Exportar de uma DLL usando arquivos DEF.

E é assim que você escreve uma DLL de enclave de VBS básico.

Etapa 2: Compilar enclaves de SBV

Agora que escrevemos nossa DLL de enclave de VBS, vamos compilá-la.

Compilar o host do enclave

Compilar o aplicativo host é o mesmo que compilar qualquer aplicativo do Windows, mas com a adição de onecore.lib à lista de dependências durante a vinculação.

Compilar a DLL de enclave de teste

Antes de podermos criar a DLL de enclave de teste, algumas alterações nas configurações do compilador e do vinculador são necessárias:

  1. O vinculador MSVC fornece um sinalizador /ENCLAVE que pega os detalhes de configuração do enclave. O sinalizador /ENCLAVE é incompatível com a vinculação incremental, então precisamos definir /INCREMENTAL:NO.

  2. [Somente configuração de depuração] /EDITANDCONTINUE é incompatível com /INCREMENTAL:NO, então usamos /Zi em vez de /ZI para Formato de informações de depuração no compilador.

  3. [Somente configuração de depuração] A configuração Verificações básicas de runtime precisa estar definida como Padrão. Não há suporte para verificações de erros de tempo de execução em enclaves de SBV.

  4. A assinatura digital de uma DLL de enclave deve ser verificada no momento do carregamento e requer a configuração do sinalizador /INTEGRITYCHECK no vinculador.

  5. As DLLs do enclave devem ser instrumentadas para a CFG (proteção de fluxo de controle), para a qual usamos o sinalizador /GUARD:MIXED no vinculador.

  6. Enclaves têm suas próprias versões de plataforma, inicialização, runtime e bibliotecas UCRT. Para garantir que não vinculemos as versões que não são de enclave, use o sinalizador /NODEFAULTLIB. Posteriormente, adicione as bibliotecas corretas em AdditionalDependencies. No código de exemplo, essas bibliotecas são encapsuladas sob a macro VBS_Enclave_Dependencies. A seguir estão as bibliotecas de enclave de SBV:

    1. libcmt.lib e libvcruntime.lib – Encontradas na pasta enclave com as ferramentas de compilação do Visual C++, consulte Arquivos .lib do CRT (C Runtime) e da STL (Biblioteca padrão) do C++.
    2. vertdll.lib e bcrypt.lib – Encontradas na pasta um com as bibliotecas do SDK do Windows.
    3. ucrt.lib – Encontrada na pasta ucrt_enclave com as bibliotecas do SDK do Windows.

Observação

Nenhuma outra biblioteca de plataforma é suportada em enclaves de SBV.

Em resumo, as seguintes alterações são exigidas:

Compilador (somente configuração de depuração):

  • Formato de informações de depuração: /Zi
  • Verificações básicas de runtime: Default

Vinculador:

  • /ENCLAVE
  • /NODEFAULTLIBS + AdditionalDependencies
  • /INCREMENTAL:NO
  • /INTEGRITYCHECK
  • /GUARD:MIXED

Agora você pode compilar a DLL do enclave.

Proteger com VEIID

VEIID (o utilitário de vinculação de ID de importação do enclave de SBV) é uma ferramenta no SDK do Windows que atualiza as tabelas de importação dentro de um enclave de SBV com IDs conhecidas para DLLs de plataforma. Isso melhora a segurança dos enclaves de SBV, impedindo que uma DLL mal-intencionada (assinada) com o mesmo nome de uma das DLLs de plataforma seja carregada.

No código de exemplo, isso é feito automaticamente como um evento pós-build.

Observação

É altamente recomendável evitar o uso de suas próprias DLLs não primárias além das DLLs da plataforma. Em vez disso, mantenha todo o seu código dentro da própria DLL do enclave.

Etapa 3: Assinar DLLs de enclave

Os enclaves de SBV devem ser assinados para serem carregados com êxito. A assinatura em um enclave contém informações sobre o autor do enclave. Isso é usado para derivar o ID do autor para um enclave. Você pode assinar seu enclave para teste antes de assiná-lo para produção.

Assinatura para teste – local

Cada certificado de assinatura de enclave requer pelo menos 3 EKUs:

  1. Assinatura de código de EKU – 1.3.6.1.5.5.7.3.3

  2. Enclave de EKU – 1.3.6.1.4.1.311.76.57.1.15

  3. EKU de Autor – O EKU é da forma 1.3.6.1.4.1.311.97.X.Y.Z, onde X é maior que 999.

    Para teste, você pode optar por usar qualquer EKU de autor que corresponda a esse padrão. Para a produção, um EKU do autor será fornecido como parte do certificado de produção (veja mais detalhes sobre a assinatura da produção abaixo).

    Exemplo: 1.3.6.1.4.1.311.97.814040577.346743379.4783502.105532346

Se você quiser assinar sua DLL de enclave durante o desenvolvimento, habilite a assinatura para teste. Com a assinatura para teste habilitada, você pode criar um certificado contendo esses três EKUs e assinar seu enclave com ele. Você pode usar o cmdlet New-SelfSignedCertificate para criar um certificado. Observe que as DLLs do enclave devem ser assinadas por hash de página.

Observação

Depois de obter um certificado, você pode automatizar o processo de assinatura no evento de pós-build.

New-SelfSignedCertificate -CertStoreLocation Cert:\\CurrentUser\\My -DnsName "MyTestEnclaveCert" -KeyUsage DigitalSignature -KeySpec Signature -KeyLength 2048 -KeyAlgorithm RSA -HashAlgorithm SHA256 -TextExtension "2.5.29.37={text}1.3.6.1.5.5.7.3.3,1.3.6.1.4.1.311.76.57.1.15,1.3.6.1.4.1.311.97.814040577.346743379.4783502.105532346"
signtool sign /ph /fd SHA256 /n "MyTestEnclaveCert" vbsenclave.dll

Com a DLL do enclave assinada, agora você pode carregá-la em um ambiente que tenha a assinatura para teste habilitada.

Assinatura de produção – Assinatura Confiável (anteriormente Assinatura de Código do Azure)

A assinatura de produção para enclaves é fornecida por meio do perfil de certificado de enclave de SBV em Assinatura confiável. Para obter detalhes sobre como usar a Assinatura confiável, consulte a documentação.

A Assinatura confiável também permite que você assine seu enclave na linha de comando. Isso gera um enclave assinado pronto para ser executado quando você cria seu enclave no Visual Studio.

Etapa 4: Depurar enclaves de SBV

Normalmente, a memória de um enclave é oculta dos depuradores e protegida contra VTL0. No entanto, se você quiser depurar sua DLL de enclave de VBS, poderá compilá-la para ser depurada durante o desenvolvimento. Enclaves são um processo de modo de usuário VTL1 e, portanto, podem ser depurados com um depurador de modo de usuário.

Para tornar seu enclave depurável:

  1. A configuração da imagem da DLL de enclave precisa permitir a depuração. Isso é feito definindo o sinalizador IMAGE_ENCLAVE_POLICY_DEBUGGABLE em IMAGE_ENCLAVE_CONFIG.
  2. A depuração precisa ser permitida durante a criação do enclave – Isso é feito definindo o sinalizador ENCLAVE_VBS_FLAG_DEBUG na estrutura ENCLAVE_CREATE_VBS_INFO passada para a chamada CreateEnclave.

Para depurar seu enclave:

  1. Anexe o depurador de modo de usuário ao processo de host do enclave.
  2. Recarregue os símbolos do enclave depois que o processo de host tiver carregado a imagem do enclave na memória.
  3. Defina pontos de interrupção nas funções dentro do enclave. O depurador o interrompe em uma chamada de enclave.

Você também pode quebrar os pontos de interrupção do modo de usuário para CreateEnclave, InitializeEnclave, etc., que entram no código correspondente na ntdll.dll.

Observação

Nunca use enclaves depuráveis em ambientes de produção.

Com isso, agora você pode criar e implantar seu primeiro enclave de SBV. Se tiver alguma dúvida, entre em contato com nosso suporte ao desenvolvedor do Windows.

Visão geral de enclaves de SBV

Assinatura Confiável do Azure

cabeçalho enclaveapi.h

APIs disponíveis em enclaves de VBS