Поделиться через


Реализация кода, совместимого с целостностью памяти

В этом разделе описывается реализация кода, совместимого с целостностью памяти.

Примечание

Целостность памяти иногда называется защищенной гипервизором целостности кода (HVCI) или принудительной целостностью кода низкоуровневой оболочкой. Изначально она была выпущена как часть Device Guard. Device Guard больше не используется, кроме как для поиска параметров целостности памяти и VBS в групповая политика или реестре Windows.

Чтобы реализовать совместимый код, убедитесь, что код драйвера выполняет следующие действия:

  • По умолчанию согласие на NX
  • Использование API-интерфейсов и флагов NX для выделения памяти (NonPagedPoolNx)
  • Не использует разделы, доступные для записи и исполняемые файлы.
  • Не пытается напрямую изменить исполняемую системную память
  • Не использует динамический код в ядре
  • Не загружает файлы данных как исполняемые файлы
  • Выравнивание разделов кратно 0x1000 (PAGE_SIZE). Например, DRIVER_ALIGNMENT=0x1000

Это может повлиять на следующий список DDIs, которые не зарезервированы для использования системой:

Имя DDI
ExAllocatePool
ExAllocatePoolWithQuota
ExAllocatePoolWithQuotaTag
ExAllocatePoolWithTag
ExAllocatePoolWithTagPriority
ExInitializeNPagedLookasideList
ExInitializeLookasideListEx
MmAllocateContiguousMemory
MmAllocateContiguousMemorySpecifyCache
MmAllocateContiguousMemorySpecifyCacheNode
MmAllocateContiguousNodeMemory
MmCopyMemory
MmMapIoSpace
MmMapLockedPages
MmMapLockedPagesSpecifyCache
MmProtectMdlSystemAddress
ZwAllocateVirtualMemory
ZwCreateSection
ZwMapViewOfSection
NtCreateSection
NtMapViewOfSection
ClfsCreateMarshallingArea
NDIS
NdisAllocateMemoryWithTagPriority
Память
StorPortGetDataInBufferSystemAddress
StorPortGetSystemAddress
ChangerClassAllocatePool
Отображение
DxgkCbMapMemory
VideoPortAllocatePool
Аудио минипорт
IMiniportDMus::NewStream
IMiniportMidi::NewStream
IMiniportWaveCyclic::NewStream
IPortWavePci::NewMasterDmaChannel
IMiniportWavePci::NewStream
Класс аудиопорта
PcNewDmaChannel
PcNewResourceList
PcNewResourceSublist
IFS
FltAllocatePoolAlignedWithTag
FltAllocateContext
WDF
WdfLookasideListCreate
WdfMemoryCreate
WdfDeviceAllocAndQueryProperty
WdfDeviceAllocAndQueryPropertyEx
WdfFdoInitAllocAndQueryProperty
WdfFdoInitAllocAndQueryPropertyEx
WdfIoTargetAllocAndQueryTargetProperty
WdfRegistryQueryMemory

Использование тестов целостности кода в HLK для проверки совместимости драйвера целостности памяти

Дополнительные сведения о проверке безопасности связанных системных основ см. в разделах Проверка готовности к проверке целостности кода hyperVisor и целостности памяти и VBS.

Дополнительные сведения о тесте, связанном с основами устройства, см. в разделе Тесты Device.DevFund.

Используйте следующую таблицу, чтобы интерпретировать выходные данные и определить, какие изменения кода драйвера необходимы для устранения несовместимости различных типов целостности памяти.

Предупреждение Искупление

Тип выполнения пула

Вызывающий объект указал тип исполняемого пула. Вызов функции выделения памяти, которая запрашивает исполняемую память.

Убедитесь, что все типы пулов содержат неисполняемый флаг NX.

Выполнение защиты страниц

Вызывающий объект указал защиту исполняемой страницы.

Укажите маску защиты страницы "без выполнения".

Выполнение сопоставления страниц

Вызывающий объект указал сопоставление списка дескрипторов исполняемой памяти (MDL).

Убедитесь, что используемая маска содержит MdlMappingNoExecute. Дополнительные сведения см. в статье MmGetSystemAddressForMdlSafe.

Раздел Execute-Write

Изображение содержит исполняемый и записываемый раздел.

Сбои выравнивания разделов

Изображение содержит раздел, который не выровнен по страницам.

Выравнивание разделов должно быть кратно 0x1000 (PAGE_SIZE). Например, DRIVER_ALIGNMENT=0x1000

IAT в разделе исполняемого файла

Таблица адресов импорта (IAT) не должна быть исполняемым разделом памяти.

Эта проблема возникает, когда IAT находится в разделе памяти только для чтения и выполнения (RX). Это означает, что ОС не сможет выполнить запись в 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>

Чтобы выбрать этот параметр при использовании графического интерфейса средства проверки, выберите Создать пользовательские параметры (для разработчиков кода), нажмите кнопку Далее, а затем выберите Проверки целостности кода.

Для отображения сведений о текущем проверяемом драйвере можно использовать параметр командной строки /query.

verifier /query

См. также:

Контрольный список безопасности драйверов