/guard:ehcont (EH 継続メタデータを有効にする)

コンパイラによって EH 継続 (EHCONT) メタデータの生成を有効にします。

構文

$

解説

/guard:ehcont オプションを指定すると、コンパイラにより、バイナリのすべての有効な例外処理継続ターゲットの相対仮想アドレス (RVA) の並べ替えられたリストが生成されます。 これは、NtContinue および SetThreadContext 命令ポインターの検証に対して実行時に使用されます。 既定では、/guard:ehcont は無効になっており、明示的に有効にする必要があります。 このオプションを明示的に無効にするには、/guard:ehcont- を使用します。

/guard:ehcont オプションは、Visual Studio 2019 バージョン 16.7 以降で使用できます。 この機能は、64 ビット OS 上の 64 ビット プロセスでサポートされています。

制御フローの強制テクノロジ (CET) は、ハードウェア ベースのセキュリティ機能であり、Return-Oriented Programming (ROP) ベースの攻撃から保護します。 制御フローの整合性を適用するために、すべての呼び出し履歴に "シャドウ スタック" が保持されます。

ROP 攻撃を防ぐためにシャドウ スタックが使用可能になっている場合、攻撃者は他の悪用手法の使用に移行します。 使用できる手法の 1 つは、CONTEXT 構造体内の命令ポインター値を破損する方法です。 この構造体は、NtContinueRtlRestoreContextSetThreadContext などのスレッドの実行をリダイレクトするシステム呼び出しに渡されます。 CONTEXT 構造体はメモリに格納されます。 含まれている命令ポインターが破損すると、システム呼び出しによって、攻撃者が制御するアドレスに実行が転送される可能性があります。 現在、NTContinue は任意の継続ポイントで呼び出すことができます。 このため、シャドウ スタックが有効になっている場合は命令ポインターを検証する必要があります。

RtlRestoreContextNtContinue は、__except ブロックを含むターゲット フレームにアンワインドするために構造化例外処理 (SEH) 例外中に使用されます。 __except ブロックの命令ポインターは、命令ポインターの検証に失敗するため、シャドウ スタック上にあることが想定されません。 /guard:ehcont コンパイラ スイッチによって "EH 継続テーブル" が生成されます。 これには、バイナリ内のすべての有効な例外処理継続ターゲットの RVA の並べ替えられたリストが含まれています。 NtContinue は、まず、ユーザーが指定した命令ポインターのシャドウ スタックをチェックし、命令ポインターが見つからない場合は、命令ポインターを含むバイナリから EH 継続テーブルのチェックに進みます。 含まれているバイナリがテーブルでコンパイルされなかった場合は、レガシ バイナリとの互換性を確保するために、NtContinue を続行できます。 EHCONT データがないレガシ バイナリと、EHCONT データを含むがテーブル エントリがないバイナリとを区別することが重要です。 前者は、バイナリ内のすべてのアドレスを有効な継続ターゲットとして許可します。 後者は、バイナリ内のすべてのアドレスを有効な継続ターゲットとして許可しません。

バイナリの EH 継続ターゲット RVA を生成するには、コンパイラとリンカーの両方に /guard:ehcont オプションを渡す必要があります。 1 つの cl コマンドを使用してバイナリが作成されている場合、コンパイラはリンカーにオプションを渡します。 コンパイラはリンカーに /guard:cf オプションも渡します。 コンパイルとリンクを分けて実行する場合は、コンパイラとリンカーの両方のコマンドで、オプションを設定する必要があります。

/guard:ehcont を使用してコンパイルされたコードを、それを使用せずにコンパイルされたライブラリやオブジェクト ファイルにリンクできます。 リンカーは、次のシナリオで致命的なエラーを返します。

  • コード セクションに "ローカル アンワインド" がある。 詳細については、「try-finally ステートメント」の「異常終了」を参照してください。

  • EH (xdata) セクションにコード セクションへのポインターが含まれており、それらが SEH 用ではない。

  • ポインターは SEH 用だが、オブジェクト ファイルは COMDAT を生成するために関数レベルのリンク (/Gy) を使用してコンパイルされていない。

これらのシナリオでは、リンカーはメタデータを生成できないため致命的なエラーを返します。 つまり、例外をスローすると、実行時にクラッシュが発生する可能性があります。

COMDAT で見つかったが、/guard:ehcont を使用してコンパイルされていない SEH セクション情報の場合、リンカーは警告 LNK4291 を生成します。 この場合、リンカーはセクションに対して正しいが保守的なメタデータを生成します。 この警告を無視するには、/IGNORE (特定の警告を無視する) を使用します。

リンカーは、メタデータを生成できない場合に、次のいずれかのエラーを生成します。

  • LNK2046: module contains _local_unwind but was not compiled with /guard:ehcont

  • LNK2047: module contains C++ EH or complex EH metadata but was not compiled with /guard:ehcont.

バイナリに EHCONT データが含まれているかどうかを確認するには、バイナリの読み込み構成をダンプするときに、次の要素を探します。

e:\>link /dump /loadconfig CETTest.exe
...
            10417500 Guard Flags
...
                       EH Continuation table present      // EHCONT guard flag present
...
    0000000180018640 Guard EH continuation table
                  37 Guard EH continuation count          // May be 0 if no exception handling is used in the binary. Still counts has having EHCONT data.
...
    Guard EH Continuation Table                           // List of RVAs

          Address
          --------
           0000000180002CF5
           0000000180002F03
           0000000180002F0A
...

Visual Studio 開発環境でこのコンパイラ オプションを設定するには

  1. プロジェクトの [プロパティ ページ] ダイアログ ボックスを開きます。 詳細については、Visual Studio での C++ コンパイラとビルド プロパティの設定に関する記事を参照してください。

  2. [構成プロパティ]>[C/C++]>[コード生成] プロパティ ページを選択します。

  3. [Enable EH Continuation Metadata]\(EH 継続メタデータを有効にする\) プロパティを選びます。

  4. ドロップダウン コントロールで、EH 継続メタデータを有効にするには [はい] (/guard:ehcont) を選択し、無効にするには [いいえ] (/guard:ehcont-) を選択します。

関連項目

/guard (制御フロー ガードを有効にする)
MSVC コンパイラ オプション
MSVC コンパイラ コマンド ラインの構文