/guard:ehcont(启用异常处理延续元数据)

允许编译器生成 EH 延续 (EHCONT) 元数据。

语法

]$

备注

/guard:ehcont 选项使编译器生成二进制文件的所有有效异常处理延续目标的相对虚拟地址 (RVA) 的排序列表。 它在运行时用于 NtContinueSetThreadContext 指令指针验证。 默认情况下,/guard:ehcont 处于关闭状态,必须显式启用。 若要显式禁用此选项,请使用 /guard:ehcont-

/guard:ehcont 选项在 Visual Studio 2019 版本 16.7 及更高版本中可用。 64 位操作系统上的 64 位进程支持该功能。

控制流强制技术 (CET) 是一种基于硬件的安全功能,可防止基于返回的编程 (ROP) 的攻击。 它为每个调用堆栈维护一个“影子堆栈”以强制控制流完整性。

当影子堆栈可用于防止 ROP 攻击时,攻击者会继续使用其他漏洞利用技术。 他们可能使用的一种技术是破坏 CONTEXT 结构内的指令指针值。 该结构被传递到重定向线程执行的系统调用中,例如 NtContinueRtlRestoreContextSetThreadContextCONTEXT 结构存储在内存中。 破坏它包含的指令指针会导致系统调用将执行转移到攻击者控制的地址。 目前,可以使用任何延续点调用 NTContinue。 这就是为什么在启用阴影堆栈时验证指令指针至关重要。

RtlRestoreContextNtContinue 用于在结构化异常处理 (SEH) 异常展开期间展开到包含 __except 块的目标帧。 __except 块的指令指针不应该在影子堆栈上,因为它会导致指令指针验证失败。 /guard:ehcont 编译器开关生成一个“EH 延续表”。 它包含二进制文件中所有有效异常处理延续目标的 RVA 的排序列表。 NtContinue 首先检查用户提供的指令指针的影子堆栈,如果在其中找不到指令指针,它会继续从包含指令指针的二进制文件中检查 EH 连续表。 如果包含的二进制文件未与表一起编译,则为了与旧版二进制文件兼容,允许 NtContinue 继续。 区分没有 EHCONT 数据的旧二进制文件和包含 EHCONT 数据但没有表条目的二进制文件很重要。 前者允许二进制文件中的所有地址作为有效的延续目标。 后者不允许二进制文件中的任何地址作为有效的延续目标。

/guard:ehcont 选项必须同时传递给编译器和链接器,才能为二进制文件生成 EH 延续目标 RVA。 如果你的二进制文件是使用单个 cl 命令生成的,则编译器会将该选项传递到链接器。 编译器还会将 /guard:cf 选项传递给链接器。 如果你分别编译和链接,则必须同时在编译器和链接器命令上设置这些选项。

你可以将使用 /guard:ehcont 编译的代码链接到没有它编译的库和目标文件。 链接器在以下任一情况下返回致命错误:

  • 代码部分具有“本地展开”。 有关详细信息,请参阅 try-finally 语句中的“异常终止”。

  • EH (xdata) 部分包含指向代码部分的指针,它们不适用于 SEH。

  • 指针用于 SEH,但未使用函数级链接 (/Gy) 编译目标文件以生成 COMDAT。

链接器返回一个致命错误,因为它无法在这些场景中生成元数据。 这意味着抛出异常可能会导致运行时崩溃。

对于在 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. 选择“启用 EH 延续元数据”属性。

  4. 在下拉列表控件中,选择“是 (/guard:ehcont)”可启用 EH 延续元数据,选择“否 (/guard:ehcont-)”可禁用。

另请参阅

/guard(启用控制流保护)
MSVC 编译器选项
MSVC 编译器命令行语法