基于虚拟化的安全性 (VBS) Enclave 开发指南

适用于:✅ Windows 11 内部版本 26100.2314 或更高版本 ✅ 的 Windows Server 2025 或更高版本

此开发指南介绍如何构建、签名和调试基本 VBS enclave。

先决条件

若要开始使用 VBS Enclave,需要满足以下要求:

  • 查看 VBS Enclave 概述中的设备要求并满足设备要求。
  • 查看 VBS Enclave 概述中的开发先决条件并满足先决条件。
    • 建议通过 Visual Studio 安装程序安装 C++ 桌面开发工作负载。 它将安装所有必要的工具,包括 Windows 软件开发工具包 (Windows SDK)。
  • 从 GitHub 下载示例代码。 它演示 VBS Enclave 的生命周期,包括如何对 Enclave 进行函数调用。
    • 每个 Enclave 都必须有一个主机应用。 示例代码包含一个 Visual Studio 解决方案,其中包含两个项目:Enclave 主机测试 Enclave

入门

满足上述先决条件后,应该能够从 Visual Studio 中的“VbsEnclave”示例打开解决方案文件,并对其进行编译。 它将创建一个测试应用程序以及相应的 enclave。 但是,在 enclave 使用有效证书签名之前,无法成功运行应用程序。

本指南详细介绍了如何在开发计算机上构建基本 VBS enclave。 构建 VBS enclave 的步骤包括:

  1. 编写 VBS enclave DLL 和相应的主机应用程序
  2. 编译 DLL 和主机
  3. 对 VBS enclave DLL 进行签名
  4. 调试 VBS enclave

首先,一起来了解 enclave 的生命周期。 enclave API 按以下顺序调用:

关系图,说明了 VBS enclave API 的调用顺序

步骤 1:编写 VBS enclave

让我们来检查示例代码,并了解如何编写使用 VBS enclave 的应用程序。

编写 enclave 主机

请记住,VBS enclave DLL 只是一个 DLL,因此它需要主机应用程序。 主机应用程序只不过是一个标准 Windows 应用程序。 要使用 VBS enclave,主机必须使用 enclaveapi.h 标头中的 Windows enclave API。 在主机应用程序中包含 windows.h 将提供对这些 API 的访问权限。

编写 DLL 并加载到测试 enclave 中

请参阅“测试 enclave”项目中的示例代码,并遵循以下步骤。

在 enclave 示例中,我们创建了一个简单的 enclave,它使用 0xDADAF00D 对输入进行 XOR 并返回结果。 让我们来细分如何操作:

  1. 首先包含 winenclave.h。 在示例代码 中,请参阅 Samples/VbsEnclave/Test enclave/precomp.h

    #include <winenclave.h>
    

    winenclave.h 是 VBS enclave 的中心包含文件,它本身包括 windows.hntenclv.hwinenclaveapi.h

  2. enclave 中加载的每个 DLL 都需要进行配置。 此配置是使用名为 __enclave_config 且类型为 IMAGE_ENCLAVE_CONFIG 的全局 const 变量定义的。 在示例代码 中,请参阅 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
    };
    

    注意

    每个 enclave 只能有一个主映像。 如果加载多个主映像,则第一个加载的映像将视为主要映像,其余映像将视为依赖项。 在此示例中,除了 enclave 平台 DLL 之外,没有其他依赖项。

  3. DllMain() 函数是必需函数,其定义了 enclave 中的入口点。 次函数在 InitializeEnclave() 期间调用。 在示例代码 中,请参阅 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. 从主机应用程序调用的任何 enclave 内的函数都必须导出,并且类型应为 LPENCLAVE_ROUTINE。 函数签名如下所示:

    void* CALLBACK enclaveFunctionName(_In_ void* Context)
    

    在示例代码 中,请参阅 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);
    }
    

    注意

    只有主 enclave 映像导出的函数才能从主机应用程序访问。

    然后,可以使用 .DEF 文件导出函数。 在示例代码 中,请参阅 Samples/VbsEnclave/Test enclave/vbsenclave.def。 有关详细信息,请参阅 使用 DEF 文件从 DLL 导出

这就是编写基本 VBS enclave DLL 的方式。

步骤 2:编译 VBS enclave

编写 VBS enclave DLL 后,让我们对其进行编译。

编译 enclave 主机

编译主机应用与编译任何 Windows 应用程序相同,但在链接时在依赖项列表中添加了 onecore.lib

编译测试 enclave DLL

在构建测试 enclave DLL 之前,需要对编译器和链接器配置进行一些更改:

  1. MSVC 链接器提供 /ENCLAVE 标志,用于选取 enclave 配置详细信息。 /ENCLAVE 标志与增量链接不兼容,因此需要设置 /INCREMENTAL:NO

  2. [仅调试配置] /EDITANDCONTINUE/INCREMENTAL:NO 不兼容,因此我们在编译器中使用 /Zi 而不是 /ZI 作为 调试信息格式

  3. [仅调试配置]基本运行时检查配置需要设置为默认值。 VBS enclave 中不支持运行时错误检查。

  4. 必须在加载时检查 enclave DLL 的数字签名,并且需要在链接器中设置 /INTEGRITYCHECK 标志。

  5. 必须检测 enclave DLL 的,为此我们在链接器中使用了 /GUARD:MIXED 标志。

  6. enclave 有自己的平台、启动、运行时和 UCRT 库版本。 为了确保我们不会链接非 enclave 版本,请使用 /NODEFAULTLIB 标志。 随后,在 AdditionalDependencies 下添加正确的库。 在示例代码中,这些库封装在“VBS_Enclave_Dependencies”宏下。 以下是 VBS enclave 库:

    1. libcmt.liblibvcruntime.lib – 在 enclave 文件夹中找到 Visual C++ 构建工具,请参阅 C 运行时 (CRT) 和 C++ 标准库 (STL) .lib 文件
    2. vertdll.libbcrypt.lib – 在 um 文件夹中找到 Windows SDK 库。
    3. ucrt.lib – 在 ucrt_enclave 文件夹中找到 Windows SDK 库。

注意

VBS enclave 中不支持其他平台库。

总之,需要进行以下更改:

编译器(仅调试配置):

  • 调试信息格式:/Zi
  • 基本运行时检查:Default

链接器:

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

现在可以编译 enclave DLL。

使用 VEIID 进行保护

VEIID(VBS enclave 导入 ID 绑定实用工具)是 Windows SDK 中的工具,它使用平台 DLL 的已知 ID 更新 VBS enclave 中的导入表。 通过阻止加载与平台 DLL 同名的恶意(已签名)DLL,可以提高 VBS enclave 的安全性。

在示例代码中,此操作作为生成后事件自动完成。

注意

除平台 DLL 之外,强烈建议避免使用自己的非主要 DLL。 而是将所有代码保留在 enclave DLL 本身内。

步骤 3:对 VBS enclave DLL 进行签名

必须对 VBS enclave 进行签名才能成功加载。 enclave 上的签名包含有关 enclave 作者的信息。 这用于派生 enclave 的作者 ID。 在对 enclave 进行生产签名之前,可以对其进行测试签名。

测试签名 - 本地

每个 enclave 签名证书至少需要 3 个 EKU:

  1. 代码签名 EKU - 1.3.6.1.5.5.7.3.3

  2. Enclave EKU - 1.3.6.1.4.1.311.76.57.1.15

  3. 作者 EKU – EKU 的窗体为1.3.6.1.4.1.311.97.X.Y.Z,其中 X 大于 999

    对于测试,可以选择使用与此模式匹配的任何作者 EKU。 对于生产,作者 EKU 将作为生产证书的一部分提供(有关生产签名的更多详细信息如下所示)。

    示例: 1.3.6.1.4.1.311.97.814040577.346743379.4783502.105532346

如果要在开发 enclave DLL 时对其进行签名,请启用测试签名。 启用测试签名后,可以创建包含这三个 EKU 的证书,并使用该证书对 enclave 进行签名。 使用 New-SelfSignedCertificate cmdlet 创建证书。 请注意,Enclave DLL 必须为页哈希签名。

注意

获得证书后,可以在生成后事件中自动执行签名过程。

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

对 enclave DLL 进行签名后,现在可以将其加载到已启用测试签名的环境中。

生产签名 – 受信任签名(旧称 Azure 代码签名)

enclave 的生产签名是通过受信任签名中的 VBS enclave 证书配置文件提供的。 有关如何使用受信任签名的详细信息,请参阅文档

受信任签名还允许在命令行上对 enclave 进行签名。 在 Visual Studio 中构建 enclave 时,这会输出已准备好运行的已签名 enclave。

步骤 4:调试 VBS enclave

通常,enclave 的内存对于调试器来说是隐藏的,并且受 VTL0 保护。 但是,如果要调试 VBS enclave DLL,可以在开发期间进行构建,以便其进行调试。 enclave 是 VTL1 用户模式过程,因此可以使用用户模式调试器进行调试。

要让 enclave 可调试:

  1. enclave DLL 映像配置需要允许调试 – 这是通过在 IMAGE_ENCLAVE_CONFIG 中设置 IMAGE_ENCLAVE_POLICY_DEBUGGABLE 标志来完成的。
  2. 在 enclave 创建过程中需要允许调试 – 这通过设置传递给 CreateEnclave 调用的 ENCLAVE_CREATE_VBS_INFO 结构中的 ENCLAVE_VBS_FLAG_DEBUG 标志来完成。

要调试 enclave,请执行以下操作:

  1. 将用户模式调试器附加到 enclave 主机进程。
  2. 在主机进程在内存中加载 enclave 映像后,重新加载 enclave 符号。
  3. 在 enclave 中的函数上设置断点。 调试器在 enclave 调用时中断它。

还可以中断 CreateEnclaveInitializeEnclave 等的用户模式断点,进一步在 ntdll.dll 中单步执行相应的代码。

注意

切勿在生产环境中使用可调试的 enclave。

现在,可以借助其构建并部署第一个 VBS enclave。 如有任何疑问,请联系 Windows 开发人员支持部门

VBS enclave 概述

Azure 受信任签名

enclaveapi.h 标头

VBS enclave 中提供的 API