仮想化ベースのセキュリティ (VBS) エンクレーブの開発ガイド
この開発ガイドでは、基本的な仮想化ベースのセキュリティ (VBS) エンクレーブをビルド、署名、およびデバッグする方法について説明します。
前提条件
仮想化ベースのセキュリティ エンクレーブの使用を開始するには、次の要件を満たす必要があります:
- 仮想化ベースのセキュリティ エンクレーブの概要でデバイス要件を表示し、満たします。
- 仮想化ベースのセキュリティ エンクレーブの概要で開発前提条件を表示し、満たします。
- Visual Studio インストーラーを使用して、C++ を使用して Desktop 開発ワークロードをインストールすることをお勧めします。 Windows ソフトウェア開発キット (SDK) を含む必要なすべてのツールがインストールされます。
- GitHub からサンプル コードをダウンロードします。 これは、エンクレーブに関数呼び出しを行う方法を含む、仮想化ベースのセキュリティ エンクレーブのライフ サイクルを示しています。
- すべてのエンクレーブにはホスト アプリが必要です。 サンプル コードには、エンクレーブ ホストとテスト エンクレーブ – の 2 つのプロジェクトを含む Visual Studio ソリューションが含まれています。
作業の開始
上記の前提条件を満たすと、Visual Studio で 仮想化ベースのセキュリティEnclave サンプルからソリューション ファイルを開き、コンパイルできるようになります。 それにより、対応するエンクレーブと共にテスト アプリケーションが作成されます。 ただし、エンクレーブが有効な認定資格証で署名されるまで、アプリケーションを正常に実行することはできません。
このガイドでは、開発用マシンで基本的な 仮想化ベースのセキュリティ エンクレーブを構築する方法について詳しく説明します。 仮想化ベースのセキュリティ エンクレーブを構築する手順は次のとおりです:
- 仮想化ベースのセキュリティ エンクレーブ DLL と対応するホスト アプリケーションを記述する
- DLL とホストをコンパイルする
- 仮想化ベースのセキュリティ エンクレーブ DLL に署名する
- 仮想化ベースのセキュリティ エンクレーブをデバッグする
まず、エンクレーブのライフサイクルを理解しましょう。 エンクレーブ API は、次の順序で呼び出されます:
手順 1: 仮想化ベースのセキュリティ エンクレーブの書き込み
サンプル コードを調べて、仮想化ベースのセキュリティ エンクレーブを使用するアプリケーションを記述する方法を理解しましょう。
エンクレーブ ホストの書き込み
仮想化ベースのセキュリティ エンクレーブ DLL は単なる DLL であるため、ホスト アプリケーションが必要です。 ホスト アプリケーションは、標準の Windows アプリケーションに過ぎない。 仮想化ベースのセキュリティ エンクレーブを使用するには、ホストで enclaveapi.h ヘッダーの Windows エンクレーブ API を使用する必要があります。 ホスト アプリケーションに windows.h
を含めると、これらの API にアクセスできるようになります。
テスト エンクレーブに読み込む DLL の書き込み
Test エンクレーブ プロジェクトのサンプル コードを参照して、以下の手順を実行してください。
エンクレーブ サンプルでは、 0xDADAF00D
を使用して入力を XOR し、結果を返す単純なエンクレーブを作成します。 その方法を解説しましょう:
まず、
winenclave.h
を含めます。 サンプルコードでは、Samples/VbsEnclave/Test enclave/precomp.h
を参照してください:#include <winenclave.h>
winenclave.h
は 仮想化ベースのセキュリティ エンクレーブの中心的なインクルード ファイルであり、それ自体にはwindows.h
、ntenclv.h
、winenclaveapi.h
が含まれます。エンクレーブに読み込まれるすべての DLL には、構成が必要です。 この構成は、グローバル
const
変数、__enclave_config
という名前の、IMAGE_ENCLAVE_CONFIG型を使用して定義されます。 サンプルコードでは、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 };
Note
エンクレーブごとに存在させることができるプライマリ イメージは 1 つだけです。 複数のプライマリ イメージを読み込む場合、最初に読み込まれたイメージがプライマリ イメージとして扱われ、残りが依存関係として扱われます。 このサンプルにはエンクレーブ プラットフォーム DLL 以外の依存関係は存在しません。
DllMain()
関数は必須で、エンクレーブへのエントリ ポイントを定義します。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; }
ホスト アプリケーションから呼び出されるエンクレーブ内の関数はすべてエクスポートされ、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); }
Note
ホスト アプリケーションからアクセスできるのは、プライマリ エンクレーブ イメージによってエクスポートされた関数だけです。
その後、
.DEF
ファイルを使用して関数をエクスポートできます。 サンプルコードでは、Samples/VbsEnclave/Test enclave/vbsenclave.def
を参照してください。 詳細については、「DEFファイルを使用した DLL からのエクスポート」を参照してください。
これが、基本的な VBS エンクレーブ DLL の記述方法です。
手順 2: 仮想化ベースのセキュリティ エンクレーブのコンパイル
VBS エンクレーブ DLL を記述したので、コンパイルしてみましょう。
エンクレーブ ホストのコンパイル
ホスト アプリのコンパイルは、すべての Windows アプリケーションのコンパイルと同じですが、リンク中に依存関係のリストに onecore.lib
が追加されます。
テスト エンクレーブ DLL のコンパイル
テスト エンクレーブ DLL をビルドする前に、コンパイラとリンカーの構成にいくつかの変更が必要です。
MSVC リンカーは、エンクレーブ構成の詳細を選択する
/ENCLAVE
フラグを提供します。/ENCLAVE
フラグは増分リンクと互換性がないため、/INCREMENTAL:NO
を設定する必要があります。[デバッグ設定のみ]
/EDITANDCONTINUE
は/INCREMENTAL:NO
と互換性がないため、コンパイラのデバッグ情報形式では/ZI
の代わりに/Zi
を使用します。[デバッグ構成のみ] Basic ランタイム チェック構成を Default に設定する必要があります。 仮想化ベースのセキュリティ エンクレーブでは、ランタイム エラー チェックはサポートされていません。
エンクレーブ DLL のデジタル署名は読み込み時にチェックする必要があり、リンカーで
/INTEGRITYCHECK
フラグを設定する必要があります。リンカーで Control Flow Guard (CFG) の
/GUARD:MIXED
フラグを使用する ことに対してエンクレーブ DLL をインストルメント化する必要があります。エンクレーブには、プラットフォーム、スタートアップ、ランタイム、UCRT ライブラリの独自のバージョンがあります。 非エンクレーブ バージョンをリンクしないようにするためには、
/NODEFAULTLIB
フラグを使用してください。 その後、AdditionalDependencies
内に適切なライブラリを追加します。 サンプル コードでは、これらのライブラリは VBS_Enclave_Dependencies マクロの下にカプセル化されています。 仮想化ベースのセキュリティ エンクレーブ ライブラリを次に示します:libcmt.lib
およびlibvcruntime.lib
- Visual C++ ビルド ツールを使用したenclave
フォルダーにある、C ランタイム (CRT) および C++ 標準ライブラリ (STL) .lib ファイルを参照してください。vertdll.lib
andbcrypt.lib
- Windows SDK ライブラリを含むum
フォルダーにあります。ucrt.lib
- Windows SDK ライブラリを含むucrt_enclave
フォルダーにあります。
Note
仮想化ベースのセキュリティ エンクレーブ内では、他のプラットフォーム ライブラリはサポートされていません。
要するに、次の変更が必要です:
コンパイラ (デバッグ構成のみ):
- デバッグ情報の形式:
/Zi
- 基本ランタイムのチェック:
Default
リンカー:
/ENCLAVE
/NODEFAULTLIBS
+AdditionalDependencies
/INCREMENTAL:NO
/INTEGRITYCHECK
/GUARD:MIXED
これで、エンクレーブ DLL をコンパイルできます。
VEIID による保護
VEIID (仮想化ベースのセキュリティ エンクレーブ インポート ID バインド ユーティリティ) は、プラットフォーム DLL の既知の ID を使用して 仮想化ベースのセキュリティ エンクレーブ内のインポート テーブルを更新する Windows SDK のツールです。 これにより、プラットフォーム DLL の 1 つと同じ名前の悪意のある (署名された) DLL が読み込まれないようにすることで、仮想化ベースのセキュリティ エンクレーブのセキュリティが向上します。
サンプル コードでは、これはビルド後のイベントとして自動的に行われます。
Note
プラットフォーム DLL は別として、独自の非プライマリ DLL を使用しないことを強くお勧めします。 代わりに、エンクレーブ DLL 自体内のすべてのコードを保持します。
手順 3: VBS エンクレーブ DLL への署名
仮想化ベースのセキュリティ (VBS) エンクレーブが正常に読み込まれるには、署名する必要があります。 エンクレーブの署名には、エンクレーブの著者に関する情報が含まれています。 これは、エンクレーブの作成者 ID を派生させるために使用されます。 生産環境に署名する前に、エンクレーブにテスト署名できます。
テスト署名 - ローカル
各エンクレーブ署名証明書には、少なくとも 3 つの EKU が必要です:
コード署名 EKU -
1.3.6.1.5.5.7.3.3
エンクレーブ EKU -
1.3.6.1.4.1.311.76.57.1.15
著者 EKU - EKU の形式は
1.3.6.1.4.1.311.97.X.Y.Z
で、X
は999
より大きいです。テストでは、このパターンに一致する任意の著者 EKU を使用することを選択できます 生産環境では、生産証明書の一部として Author EKU が提供されます (生産署名の詳細については以下を参照)。
例:
1.3.6.1.4.1.311.97.814040577.346743379.4783502.105532346
開発中にエンクレーブ DLL に署名したい場合は、テスト署名を有効にしてください。 テスト署名を有効にすると、これら 3 つの EKU を含む認定資格証を作成し、それを使用してエンクレーブに署名できます。 New-SelfSignedCertificate コマンドレットを使用して、認定資格証を作成します。 エンクレーブ DLL はページ ハッシュ署名されている必要があることに注意してください。
Note
認定資格証を取得したら、ビルド後のイベントで署名プロセスを自動化できます。
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
エンクレーブ DLL が署名された状態で、テスト署名が有効になっている環境に読み込むことができます。
生産署名 – 信頼された署名 (以前の Azure コード署名)
エンクレーブの生産署名は、仮想化ベースのセキュリティ エンクレーブ証明書プロファイルを信頼された署名 内を通じて提供されます。 信頼された署名の使用方法の詳細については、ドキュメントを参照してください。
信頼された署名では、コマンド ラインでエンクレーブに署名することもできます。 これにより、Visual Studio でエンクレーブをビルドするときに、すぐに実行できる署名付きエンクレーブが出力されます。
手順 4: 仮想化ベースのセキュリティ (VBS) エンクレーブのデバッグ
通常、エンクレーブのメモリはデバッガで非表示となり、VTL0 から保護されます。 ただし、VBS エンクレーブ DLL をデバッグする場合は、開発中にデバッグ対象としてビルドできます。 エンクレーブは VTL1 ユーザー モード プロセスであるため、ユーザー モード デバッガーを使用してデバッグできます。
エンクレーブをデバッグ可能にするには:
- エンクレーブ DLL イメージの構成で、デバッグを許可する必要があります – これには、IMAGE_ENCLAVE_CONFIG の IMAGE_ENCLAVE_POLICY_DEBUGGABLE フラグを設定します。
- エンクレーブの作成中にデバッグを許可する必要があります – これは、ENCLAVE_VBS_FLAG_DEBUG フラグを ENCLAVE_CREATE_VBS_INFO 構造体で CreateEnclave 呼び出しに渡されることを設定することによって行われます。
エンクレーブをデバッグするには:
- エンクレーブ ホスト プロセスにユーザー モード デバッガーをアタッチします。
- ホスト プロセスがエンクレーブ イメージをメモリに読み込んだ後、エンクレーブ シンボルを再読み込みします。
- エンクレーブ内の関数にブレークポイントを設定します。 デバッガーは、そこにエンクレーブ呼び出しで割り込みます。
また、CreateEnclave、InitializeEnclave などのユーザー モード ブレークポイントで中断して、ntdll.dll
の対応するコードに、さらにステップ インすることもできます。
Note
運用環境で、デバッグ可能なエンクレーブを使用しないでください。
これで、ユーザーが最初の仮想化ベースのセキュリティ (VBS) エンクレーブをビルドしてデプロイできるようになりました。 ご不明な点については、Windows 開発者サポートにお問い合わせください。