实现内存完整性兼容代码
本部分介绍如何实现与内存完整性兼容的代码。
注意
内存完整性有时称为 虚拟机监控程序保护的代码完整性 (HVCI) 或 虚拟机监控程序强制实施的代码完整性,最初作为 Device Guard 的一部分发布。 除了在 组策略 或 Windows 注册表中查找内存完整性和 VBS 设置外,不再使用 Device Guard。
若要实现兼容的代码,请确保驱动程序代码执行以下操作:
- 默认选择加入 NX
- 将 NX API/标志用于内存分配 (NonPagedPoolNx)
- 不使用既可写又可执行的节
- 不尝试直接修改可执行系统内存
- 不使用内核中的动态代码
- 不以可执行文件的形式加载数据文件
- 节对齐方式是0x1000 (PAGE_SIZE) 的倍数。 例如,DRIVER_ALIGNMENT=0x1000
以下不保留供系统使用的 DDI 列表可能会受到影响:
使用 HLK 中的代码完整性测试来测试内存完整性驱动程序兼容性
有关相关系统基础安全测试的详细信息,请参阅 HyperVisor 代码完整性准备测试和内存完整性和 VBS。
有关相关设备基础测试的详细信息,请参阅 Device.DevFund 测试。
使用下表解释输出并确定需要更改哪些驱动程序代码来修复不同类型的内存完整性不兼容问题。
警告 | 救 赎 |
执行池类型 |
调用方指定了一个可执行池类型。 调用请求可执行内存的内存分配函数。 确保所有池类型都包含非可执行 NX 标志。 |
执行页面保护 |
调用方指定了一个可执行页面保护。 指定“不执行”页面保护掩码。 |
执行页面映射 |
调用方指定了一个可执行内存描述符列表 (MDL) 映射。 确保使用的掩码包含 MdlMappingNoExecute。 有关详细信息,请参阅 MmGetSystemAddressForMdlSafe |
执行-写入节 |
映像包含可执行和可写节。 |
节对齐失败 |
映像包含未对齐页面的节。 节对齐必须是 0x1000 (PAGE_SIZE) 的倍数。 例如,DRIVER_ALIGNMENT=0x1000 |
可执行节中的 IAT |
导入地址表 (IAT) 不应是内存的可执行节。 当 IAT 位于内存的只读和执行 (RX) 节时,会出现此问题。 这意味着 OS 将无法写入 IAT 来为引用的 DLL 设置正确的地址。 在代码链接中使用 /MERGE(合并节)选项时可能会出现这种情况。 例如,如果 .rdata(只读初始化数据)与 .text 数据(可执行代码)合并,IAT 可能最终位于内存的可执行节中。 |
不支持的重定位
在 Windows 10 版本 1507 到 Windows 10 版本 1607 中,由于使用地址空间布局随机化 (ASLR) 地址对齐和内存重定位可能会出现问题。 操作系统需要将链接器从其设置默认基址的地址重定位到 ASLR 分配的实际位置。 此重定位不能跨越页面边界。 例如,假设有一个 64 位地址值,该值在页面中从偏移量 0x3FFC 开始。 该地址值在偏移量 0x0003 处与下一页重叠。 在Windows 10版本 1703 之前,不支持这种类型的重叠重新定位。
当全局结构类型变量初始值设定项具有指向另一个全局变量的未对齐指针时,可能会发生这种情况,其布局方式使得链接器无法移动变量以避免跨页重定位。 链接器将尝试移动变量,但在某些情况下,它可能无法移动 (例如大型未对齐结构或大量未对齐结构) 。 在适当的情况下,应使用 /Gy (COMDAT) 选项组装模块,以允许链接器尽可能对齐模块代码。
#include <pshpack1.h>
typedef struct _BAD_STRUCT {
USHORT Value;
CONST CHAR *String;
} BAD_STRUCT, * PBAD_STRUCT;
#include <poppack.h>
#define BAD_INITIALIZER0 { 0, "BAD_STRING" },
#define BAD_INITIALIZER1 \
BAD_INITIALIZER0 \
BAD_INITIALIZER0 \
BAD_INITIALIZER0 \
BAD_INITIALIZER0 \
BAD_INITIALIZER0 \
BAD_INITIALIZER0 \
BAD_INITIALIZER0 \
BAD_INITIALIZER0
#define BAD_INITIALIZER2 \
BAD_INITIALIZER1 \
BAD_INITIALIZER1 \
BAD_INITIALIZER1 \
BAD_INITIALIZER1 \
BAD_INITIALIZER1 \
BAD_INITIALIZER1 \
BAD_INITIALIZER1 \
BAD_INITIALIZER1
#define BAD_INITIALIZER3 \
BAD_INITIALIZER2 \
BAD_INITIALIZER2 \
BAD_INITIALIZER2 \
BAD_INITIALIZER2 \
BAD_INITIALIZER2 \
BAD_INITIALIZER2 \
BAD_INITIALIZER2 \
BAD_INITIALIZER2
#define BAD_INITIALIZER4 \
BAD_INITIALIZER3 \
BAD_INITIALIZER3 \
BAD_INITIALIZER3 \
BAD_INITIALIZER3 \
BAD_INITIALIZER3 \
BAD_INITIALIZER3 \
BAD_INITIALIZER3 \
BAD_INITIALIZER3
BAD_STRUCT MayHaveStraddleRelocations[4096] = { // as a global variable
BAD_INITIALIZER4
};
在其他涉及使用汇编程序代码的情况下,也可能出现此问题。
驱动程序验证程序代码完整性
使用驱动程序验证程序代码完整性选项标志 (0x02000000) 启用额外的检查,以验证此功能的符合性。 若要从命令行启用此功能,请使用以下命令。
verifier.exe /flags 0x02000000 /driver <driver.sys>
若要在使用验证程序 GUI 的情况下选择此选项,请选择“为代码开发人员创建自定义 设置 () ”,选择“ 下一步”,然后选择“ 代码完整性检查”。
可以使用验证程序命令行 /query 选项显示当前驱动程序验证程序信息。
verifier /query