Управление Flow Guard для обеспечения безопасности платформы

Что такое flow Guard?

Control Flow Guard (CFG) — это высокооптимизируемая функция безопасности платформы, которая была создана для борьбы с уязвимостями повреждения памяти. При размещении жестких ограничений на то, где приложение может выполнять код, это делает его гораздо сложнее для эксплойтов выполнять произвольный код через уязвимости, такие как переполнение буфера. CFG расширяет предыдущие технологии устранения рисков эксплойтов, такие как /GS (проверка безопасности буфера), предотвращение выполнения данных (DEP) и выборка макета адресного пространства (ASLR).

Использование CFG может помочь:

  • Предотвращение атак на повреждение памяти и программ-шантажистов.
  • Ограничьте возможности сервера только тем, что необходимо в определенный момент времени, чтобы уменьшить область атаки.
  • Усложняйте использование произвольного кода с помощью уязвимостей, таких как переполнение буфера.

Эта функция доступна в Microsoft Visual Studio и работает в версиях Windows с поддержкой CFG; Windows 10 и Windows 11 на клиенте и Windows Server 2019 и более поздних версий на стороне сервера.

Разработчикам настоятельно рекомендуется включить CFG для своих приложений. Вам не нужно включать CFG для каждой части кода, так как смесь включенного CFG и не включаемого кода CFG будет выполняться нормально. Однако не удалось включить CFG для всех кодов, которые могут открыть пробелы в защите. Кроме того, код с поддержкой CFG хорошо работает в версиях Windows с поддержкой CFG и, следовательно, полностью совместим с ними.

Как включить CFG?

В большинстве случаев нет необходимости изменять исходный код. Все, что вам нужно сделать, — добавить параметр в проект Visual Studio, а компилятор и компоновщик включите CFG.

Самый простой метод — перейти к Project | Свойства | Свойства конфигурации | C/C++ | Создание кода и выбор "Да" (/guard:cf) для управления Flow Guard.

Свойство cfg в Visual Studio

Кроме того, добавьте /guard:cf в Project | Свойства | Свойства конфигурации | C/C++ | Командная строка | Дополнительные параметры (для компилятора) и /guard:cf в Project | Свойства | Свойства конфигурации | Компоновщик | Командная строка | Дополнительные параметры (для компоновщика).

Свойство cfg для компилятораСвойство cfg для компоновщика

Дополнительные сведения см. в разделе /guard (включение Функции управления Flow Guard).

Если вы создаете проект из командной строки, можно добавить те же параметры. Например, если вы компилируетсяе проект с именем test.cpp, используйте cl /guard:cf test.cpp /link /guard:cf.

Вы также можете динамически управлять набором целевых адресов, которые считаются допустимыми CFG с помощью SetProcessValidCallTargets из API управления памятью. Тот же API можно использовать для указания того, являются ли страницы недопустимыми или допустимыми целевыми объектами для CFG. Функции VirtualProtect и VirtualAlloc по умолчанию обрабатывают указанный регион исполняемых и зафиксированных страниц в качестве допустимых целевых объектов косвенного вызова. Это поведение можно переопределить, например при реализации JIT-компилятора, указав PAGE_TARGETS_INVALID при вызове VirtualAlloc или PAGE_TARGETS_NO_UPDATE при вызове VirtualProtect, как описано в разделе "Константы защиты памяти".

Как сказать, что двоичный файл находится под контролем Flow Guard?

Запустите средство дамбина (включенного в установку Visual Studio) из командной строки Visual Studio с параметрами /headers и /loadconfig: dumpbin /headers /loadconfig test.exe. Выходные данные двоичного файла в CFG должны показать, что значения заголовков включают "Guard", а значения конфигурации загрузки включают в себя "CF Instrumented" и "FID table present".

выходные данные из дамбина /headers

Выходные данные из дамбина /loadconfig

Как CFG действительно работает?

Уязвимости программного обеспечения часто используются путем предоставления маловероятных, необычных или экстремальных данных запущенной программе. Например, злоумышленник может использовать уязвимость переполнения буфера, предоставляя больше входных данных в программу, чем ожидалось, тем самым перезапустив область, зарезервированную программой для хранения ответа. Это может привести к повреждению соседней памяти, которая может содержать указатель функции. Когда программа вызывает эту функцию, она может перейти в непреднамеренное расположение, указанное злоумышленником.

Однако мощная комбинация поддержки во время компиляции и выполнения из CFG реализует целостность потока управления, которая тесно ограничивает выполнение инструкций косвенного вызова.

Компилятор выполняет следующие действия:

  1. Добавляет упрощенные проверки безопасности в скомпилированный код.
  2. Определяет набор функций в приложении, которые являются допустимыми целевыми объектами для непрямых вызовов.

Поддержка среды выполнения, предоставляемая ядром Windows:

  1. Эффективно поддерживает состояние, определяющее допустимые целевые объекты непрямого вызова.
  2. Реализует логику, которая проверяет, является ли целевой объект непрямого вызова допустимым.

Чтобы проиллюстрировать:

cfg псевдокод

Если проверка CFG завершается сбоем во время выполнения, Windows немедленно завершает программу, что нарушает любые эксплойты, которые пытаются косвенно вызвать недопустимый адрес.

См. также

/guard (включение защиты потока управления)

/GUARD (включение проверок защиты)