Реализация кода, совместимого с целостностью памяти
В этом разделе описывается реализация кода, совместимого с целостностью памяти.
Примечание
Целостность памяти иногда называется защищенной гипервизором целостности кода (HVCI) или принудительной целостностью кода низкоуровневой оболочкой. Изначально она была выпущена как часть Device Guard. Device Guard больше не используется, кроме как для поиска параметров целостности памяти и VBS в групповая политика или реестре Windows.
Чтобы реализовать совместимый код, убедитесь, что код драйвера выполняет следующие действия:
- По умолчанию согласие на NX
- Использование API-интерфейсов и флагов NX для выделения памяти (NonPagedPoolNx)
- Не использует разделы, доступные для записи и исполняемые файлы.
- Не пытается напрямую изменить исполняемую системную память
- Не использует динамический код в ядре
- Не загружает файлы данных как исполняемые файлы
- Выравнивание разделов кратно 0x1000 (PAGE_SIZE). Например, DRIVER_ALIGNMENT=0x1000
Это может повлиять на следующий список DDIs, которые не зарезервированы для использования системой:
Использование тестов целостности кода в 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