Driver compatibility with memory integrity and VBS

Memory integrity is a virtualization-based security (VBS) feature available in Windows 10, Windows 11, and Windows Server 2016 and later. Memory integrity and VBS improve the threat model of Windows and provide stronger protections against malware trying to exploit the Windows kernel. VBS uses the Windows hypervisor to create an isolated virtual environment that becomes the root of trust of the OS that assumes the kernel can be compromised. Memory integrity is a critical component that protects and hardens Windows by running kernel mode code integrity within the isolated virtual environment of VBS. Memory integrity also restricts kernel memory allocations that could be used to compromise the system, ensuring that kernel memory pages are only made executable after passing code integrity checks inside the secure runtime environment, and executable pages themselves are never writable.

Note

Memory integrity is sometimes referred to as hypervisor-protected code integrity (HVCI) or hypervisor enforced code integrity, and was originally released as part of Device Guard. Device Guard is no longer used except to locate memory integrity and VBS settings in Group Policy or the Windows registry.

Memory integrity is turned on by default on clean installs of Windows 10 in S mode and Windows 11 on compatible hardware as described in memory integrity enablement. On other systems that don't meet the memory integrity auto-enablement requirements, customers can opt in using any of the methods described in how to enable memory integrity.

Application compatibility

Although compatibility with memory integrity has been a requirement for all drivers since the Windows 10 Anniversary Update (1607), some applications and hardware device drivers may still be incompatible. This incompatibility can cause devices or software to malfunction and in rare cases may result in a boot failure (blue screen). Such issues may occur after memory integrity protection has been turned on or during the enablement process itself. If you're an application developer and want to validate that your drivers and software packages are compatible with memory integrity, follow these steps.

Some examples where we have observed incompatibilities with memory integrity include:

  • Anti-cheat solutions with gaming
  • 3rd party input methods
  • 3rd party banking password protection

We worked hard to mitigate impacted experiences, so if an incompatibility exists for a boot-critical driver, memory integrity protection will be silently turned off if it had been auto-enabled. If you encounter incompatibilities with other apps, we advise that you check for updates for the specific app and version encountering the issue before turning off memory integrity protection.

How to build compatible drivers

Since memory pages and sections can never be writable and executable, the first step is to ensure a clear separation of data and code and not to attempt to directly modify code pages.

  • Opt-in to NX by default
  • Use NX APIs/flags for memory allocation - NonPagedPoolNx
  • Don't use sections that are both writable and executable
  • Don't attempt to directly modify executable system memory
  • Don't use dynamic code in kernel
  • Don't load data files as executable
  • Section Alignment must be a multiple of 0x1000 (PAGE_SIZE). E.g. DRIVER_ALIGNMENT=0x1000

Use the latest version of the WDK and Visual Studio to produce compatible drivers when using default settings.

How to verify driver compatibility with memory integrity

There are three steps to verify driver compatibility:

  1. Use Driver Verifier (see section below) with the Code Integrity compatibility checks enabled.
  2. Test the driver on a system with memory integrity enabled.
  3. Run the HyperVisor Code Integrity Readiness Test in the Windows HLK.

Driver Verifier compatibility checks

Driver Verifier has a Code Integrity option flag (0x02000000) to enable extra checks that validate compliance with memory integrity. To enable this from the command line, use the following command:

verifier.exe /flags 0x02000000 /driver <driver.sys>

To choose this option if using the verifier GUI, choose Create custom settings (for code developers), choose Next , and then choose Code integrity checks.

For success, there is no output. The below is an example for a failure output.

Driver Verifier: Enabled for DvCi.sys, flags 0x2000000, build 16299, key o4Dbg8OVJqaBYlqWQ2QvRH

\*\*\*\*\*\*\*\*\*\*\* Verifier Detected a Code Integrity Issue \*\*\*\*\*\*\*\*\*\*\*\*

\*\* The caller 0xFFFFF8094B9852F0 specified an executable page protection 0x40.

\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*

\*\*\* Verifier assertion failed \*\*\*

(B)reak, (I)gnore, (W)arn only, (R)emove assert? B

Test the driver with memory integrity enabled

Although Windows will turn memory integrity on by default for most systems, there are several reasons that may prevent that from happening. To turn on memory integrity, see How to turn on memory integrity. Then, test the functionality of your driver. Be sure to exercise all code paths in your driver to ensure your driver doesn't perform operations at runtime that are incompatible with memory integrity.

HLK testing (Desktop and Server)

The HLK test HyperVisor Code Integrity Readiness Test must pass for drivers to be approved for Microsoft signing. Memory integrity-compatible drivers are required for both Desktop and Server Editions. The HLK test is a basic test written to make sure that memory integrity-compatible drivers are correctly loaded and run by the OS.

Although simply passing the HLK test is sufficient for a Microsoft signature for the driver, we strongly recommend thorough functional testing with memory integrity enabled. For example, there might be incorrectly-coded memory allocations violating NX protections that cause failures that wouldn't be caught by the test. The driver author should thoroughly test the driver while keeping memory integrity enabled.

During driver development and during HLK testing, memory integrity may need to be disabled, as it can prevent the driver from loading.

The HLK Hypervisor Code Integrity Readiness Test is required as part of the Windows Server Assurance AQ and the flags to enable Code Integrity checks are also set while enabling driver verifier during other HLK tests.

FAQs

What about existing drivers? Do I need to re-build these drivers to get them to work with Windows 10?

It depends. Many drivers will already be compatible. If using standard settings with the old versions of the WDK and Visual Studio, a known issue is that the INIT section is marked as RWX. In Windows 10, however, the W will automatically be stripped, so if this is the only issue then the driver will be compatible.

How do I verify that memory integrity is enabled?

The simplest method is to run the System Information app (msinfo32). Look for the following line: "Virtualization-based security Services Running". It should report: "Hypervisor enforced Code Integrity". There is also a WMI interface for checking using management tools, see Validate enabled VBS and memory integrity features.

Memory integrity can also be checked in the Windows Security app at Settings > Update & Security > Windows Security > Device security > Core isolation details > Memory integrity. For more information, see KB4096339.

Can I verify that memory integrity is enabled programmatically from kernel in order to alter driver behavior?

Yes, you can use NtQuerySystemInformation: https://msdn.microsoft.com/library/windows/desktop/ms724509(v=vs.85).aspx

The SYSTEM_CODEINTEGRITY_INFORMATION structure has a 0x400 value exposed, indicating that memory integrity is on.

How do I fix compatibility issues?

In addition to double checking that there are no W+X pages and the driver sections are aligned correctly as mentioned above, the most likely issue will be improper memory allocation. Information about the Code Analysis warnings related to memory allocation issued is available on MSDN on the following page:

Code Analysis for Drivers Warnings

The following MSDN links show some examples of commonly-used APIs that cause executable memory to be allocated, along with some example fixes:

Use the following table to interpret the output to determine what driver code changes are needed to resolve the different types of HVCI incompatibilities.

Warning Resolution
Execute Pool Type The caller specified an executable pool type. Calling a memory allocating function that requests executable memory. Be sure that all pool types contain a non executable NX flag.
Execute Page Protection The caller specified an executable page protection. Specify a "no execute" page protection mask.
Execute Page Mapping The caller specified an executable memory descriptor list (MDL) mapping. Make sure that the mask that is used contains MdlMappingNoExecute. For more information, see MmGetSystemAddressForMdlSafe
Execute-Write Section The image contains an executable and writable section.
Section Alignment Failures The image contains a section that is not page aligned. Section Alignment must be a multiple of 0x1000 (PAGE_SIZE). E.g. DRIVER_ALIGNMENT=0x1000
Unsupported Relocs In Windows 10 version 1507 through version 1607, because of the use of Address Space Layout Randomization (ASLR) an issue can arise with address alignment and memory relocation. The operating system needs to relocate the address from where the linker set its default base address to the actual location that ASLR assigned. This relocation cannot straddle a page boundary. For example, consider a 64-bit address value that starts at offset 0x3FFC in a page. It’s address value overlaps over to the next page at offset 0x0003. This type of overlapping relocs is not supported prior to Windows 10 version 1703.

This situation can occur when a global struct type variable initializer has a misaligned pointer to another global, laid out in such a way that the linker cannot move the variable to avoid the straddling relocation. The linker will attempt to move the variable, but there are situations where it may not be able to do so, for example with large misaligned structs or large arrays of misaligned structs. Where appropriate, modules should be assembled using the /Gy (COMDAT) option to allow the linker to align module code as much as possible.

#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
};

There are other situations involving the use of assembler code, where this issue can also occur.

IAT in Executable Section The import address table (IAT), should not be an executable section of memory.

This issue occurs when the IAT, is located in a Read and Execute (RX) only section of memory. This means that the OS will not be able to write to the IAT to set the correct addresses for where the referenced DLL.

One way that this can occur is when using the /MERGE (Combine Sections) option in code linking. For example if .rdata (Read-only initialized data) is merged with .text data (Executable code), it is possible that the IAT may end up in an executable section of memory.

Which APIs are potentially affected?

The following list of APIs that are not reserved for system use may be impacted:

   
API name Description
ExAllocatePool https://msdn.microsoft.com/library/windows/hardware/ff544501(v=vs.85).aspx
ExAllocatePoolWithQuota https://msdn.microsoft.com/library/windows/hardware/ff544506(v=vs.85).aspx
ExAllocatePoolWithQuotaTag https://msdn.microsoft.com/library/windows/hardware/ff544513(v=vs.85).aspx
ExAllocatePoolWithTag https://msdn.microsoft.com/library/windows/hardware/ff544520(v=vs.85).aspx
ExAllocatePoolWithTagPriority https://msdn.microsoft.com/library/windows/hardware/ff544523(v=vs.85).aspx
ExInitializeNPagedLookasideList https://msdn.microsoft.com/library/windows/hardware/ff545301(v=vs.85).aspx
ExInitializeLookasideListEx https://msdn.microsoft.com/library/windows/hardware/ff545298(v=vs.85).aspx
MmAllocateContiguousMemory https://msdn.microsoft.com/library/windows/hardware/ff554460(v=vs.85).aspx
MmAllocateContiguousMemorySpecifyCache https://msdn.microsoft.com/library/windows/hardware/ff554464(v=vs.85).aspx
MmAllocateContiguousMemorySpecifyCacheNode https://msdn.microsoft.com/library/windows/hardware/ff554464(v=vs.85).aspx
MmAllocateContiguousNodeMemory https://msdn.microsoft.com/library/windows/hardware/jj602795(v=vs.85).aspx
MmCopyMemory https://msdn.microsoft.com/library/windows/hardware/dn342884(v=vs.85).aspx
MmMapIoSpace https://msdn.microsoft.com/library/windows/hardware/ff554618(v=vs.85).aspx
MmMapLockedPages https://msdn.microsoft.com/library/windows/hardware/ff554622(v=vs.85).aspx
MmMapLockedPagesSpecifyCache https://msdn.microsoft.com/library/windows/hardware/ff554629(v=vs.85).aspx
MmProtectMdlSystemAddress https://msdn.microsoft.com/library/windows/hardware/ff554670(v=vs.85).aspx
ZwAllocateVirtualMemory https://msdn.microsoft.com/library/windows/hardware/ff566416(v=vs.85).aspx
ZwCreateSection https://msdn.microsoft.com/library/windows/hardware/ff566428(v=vs.85).aspx
ZwMapViewOfSection https://msdn.microsoft.com/library/windows/hardware/ff566481(v=vs.85).aspx
NtCreateSection https://msdn.microsoft.com/library/windows/hardware/ff556473(v=vs.85).aspx
NtMapViewOfSection https://msdn.microsoft.com/library/windows/hardware/ff556551(v=vs.85).aspx
StorPortGetDataInBufferSystemAddress https://msdn.microsoft.com/library/windows/hardware/jj553720(v=vs.85).aspx
StorPortGetSystemAddress https://msdn.microsoft.com/library/windows/hardware/ff567100(v=vs.85).aspx
DxgkCbMapMemory https://msdn.microsoft.com/library/windows/hardware/ff559533(v=vs.85).aspx
IMiniportDMus::NewStream https://msdn.microsoft.com/library/windows/hardware/ff536701(v=vs.85).aspx
FltAllocatePoolAlignedWithTag https://msdn.microsoft.com/library/windows/hardware/ff541762(v=vs.85).aspx
FltAllocateContext https://msdn.microsoft.com/library/windows/hardware/ff541710(v=vs.85).aspx
ChangerClassAllocatePool https://msdn.microsoft.com/library/windows/hardware/ff551402(v=vs.85).aspx
IMiniportMidi::NewStream https://msdn.microsoft.com/library/windows/hardware/ff536710(v=vs.85).aspx
IMiniportWaveCyclic::NewStream https://msdn.microsoft.com/library/windows/hardware/ff536723(v=vs.85).aspx
IPortWavePci::NewMasterDmaChannel https://msdn.microsoft.com/library/windows/hardware/ff536916(v=vs.85).aspx
IMiniportWavePci::NewStream https://msdn.microsoft.com/library/windows/hardware/ff536735(v=vs.85).aspx
PcNewDmaChannel https://msdn.microsoft.com/library/windows/hardware/ff537712(v=vs.85).aspx
PcNewResourceList https://msdn.microsoft.com/library/windows/hardware/ff537717(v=vs.85).aspx
PcNewResourceSublist https://msdn.microsoft.com/library/windows/hardware/ff537718(v=vs.85).aspx
VideoPortAllocatePool https://msdn.microsoft.com/library/windows/hardware/ff570180(v=vs.85).aspx
ClfsCreateMarshallingArea https://msdn.microsoft.com/library/windows/hardware/ff541520(v=vs.85).aspx
WdfLookasideListCreate https://msdn.microsoft.com/library/windows/hardware/ff548694(v=vs.85).aspx
WdfMemoryCreate https://msdn.microsoft.com/library/windows/hardware/ff548706(v=vs.85).aspx
WdfDeviceAllocAndQueryProperty https://msdn.microsoft.com/library/windows/hardware/ff545882(v=vs.85).aspx
WdfDeviceAllocAndQueryPropertyEx https://msdn.microsoft.com/library/windows/hardware/dn265599(v=vs.85).aspx
WdfFdoInitAllocAndQueryProperty https://msdn.microsoft.com/library/windows/hardware/ff547239(v=vs.85).aspx
WdfFdoInitAllocAndQueryPropertyEx https://msdn.microsoft.com/library/windows/hardware/dn265612(v=vs.85).aspx
WdfIoTargetAllocAndQueryTargetProperty https://msdn.microsoft.com/library/windows/hardware/ff548585(v=vs.85).aspx
WdfRegistryQueryMemory https://msdn.microsoft.com/library/windows/hardware/ff549920(v=vs.85).aspx
NdisAllocateMemory https://msdn.microsoft.com/library/windows/hardware/ff550762(v=vs.85).aspx