NX bit и DEP: как процессор и ОС запрещают выполнение данных из памяти

NX bit (No-eXecute bit) — аппаратный флаг в записях таблиц страниц процессора, который запрещает выполнение инструкций из определённых областей памяти. DEP (Data Execution Prevention) — системная политика Windows, использующая этот флаг для защиты от запуска кода из данных. Вместе они образуют базовый барьер против классических атак через переполнение буфера: даже если злоумышленник записал shellcode в стек или кучу, процессор откажется его выполнять.

Аппаратная основа: бит NX в таблицах страниц​


В архитектурах x86-64 и x86 с PAE записи таблиц страниц (PTE) имеют ширину 64 бита. Бит 63 в такой записи отведён под флаг запрета выполнения: когда он установлен, страница доступна для чтения и записи, но попытка извлечь из неё инструкцию вызывает исключение #PF (Page Fault) с кодом ошибки, указывающим на нарушение прав выполнения.

Исторически в 32-битном режиме x86 без PAE таблицы страниц имели ширину 32 бита и отдельного бита запрета выполнения не содержали. AMD добавила NX bit в расширение AMD64 (Long Mode), Intel реализовала аналогичную функциональность под названием XD bit (eXecute Disable) в EM64T. В 32-битном режиме с включённым PAE записи расширяются до 64 бит, и бит NX становится доступен — именно так Windows XP SP2 активировала DEP на 32-битных системах.

РежимПоддержка NXУсловие
x86-64 (Long Mode)ДаВсегда доступна, PAE обязателен
32-бит с PAEДаPAE включён через бит 5 регистра CR4
32-бит без PAEНетЗаписи PTE 32-битные, бит отсутствует

В Long Mode режим PAE включён принудительно на аппаратном уровне, поэтому NX bit всегда доступен без дополнительной настройки.

DEP в Windows: политика и механика​


DEP — системная функция защиты памяти, встроенная в Windows начиная с Windows XP и Windows Server 2003. Она помечает страницы данных (стек, кучу по умолчанию, пулы памяти) как неисполняемые. Если приложение пытается выполнить код из защищённой страницы, возникает исключение со статусом STATUS_ACCESS_VIOLATION, и при отсутствии обработчика процесс завершается.

DEP не является исчерпывающей защитой от всех эксплойтов — это один из инструментов в многоуровневой модели безопасности.

Политики DEP​


DEP настраивается при загрузке системы через параметры boot configuration data (политика no-execute page protection). Приложение может узнать текущую политику через GetSystemDEPPolicy и при определённых условиях изменить свою собственную через SetProcessDEPPolicy.

ПолитикаПоведение
AlwaysOffDEP полностью отключён
AlwaysOnDEP включён для всех процессов
OptInDEP активен только для системных процессов и служб; приложения могут запросить защиту
OptOutDEP активен для всех процессов, кроме явно исключённых

Какие области защищены​


При активной политике DEP следующие области памяти помечаются как неисполняемые:

  • Стек потока
  • Куча по умолчанию процесса
  • Пулы памяти
  • Любые страницы, выделенные без флага PAGE_EXECUTE*

Выделения через malloc и HeapAlloc всегда возвращают неисполняемую память. Если приложению нужно выполнить код из динамически выделенной области (например, JIT-компилятор), оно должно явно запросить права на выполнение при выделении через VirtualAlloc с флагами PAGE_EXECUTE, PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE или PAGE_EXECUTE_WRITECOPY.

Рекомендации для разработчиков​


Microsoft рекомендует после генерации исполняемого кода снять права на запись через VirtualProtect, оставив только PAGE_EXECUTE_READ. Это минимизирует окно для атак типа write-then-execute. Также рекомендуется располагать исполняемые области в нижней части адресного пространства, а неисполняемые — выше, чтобы переполнение буфера в данных не достигало кода. Чем меньше исполняемых страниц в процессе, тем меньше поверхность атаки.

NX в Linux: механизм и управление​


В Linux ядро использует NX bit для разделения прав доступа к страницам. По умолчанию стек и куча пользовательских процессов помечаются как неисполняемые. Управление осуществляется через флаги PROT_EXEC, PROT_READ, PROT_WRITE при вызове mmap и через mprotect для изменения прав уже выделенных страниц.

Принцип W^X (Write XOR Execute) — страница не должна быть одновременно доступна для записи и выполнения — является базовым ориентиром при проектировании подсистем ядра и пользовательских приложений. Некоторые дистрибутивы исторически использовали дополнительные патчи (exec-shield, PaX) для расширения защиты на сегменты данных и усиления политик, однако в современных ядрах основная функциональность обеспечивается самим NX bit и стандартными механизмами mmap/mprotect.

Проверка поддержки NX процессором​


Флаг nx в выводе /proc/cpuinfo указывает на аппаратную поддержку:

Bash:
grep -m1 '^flags' /proc/cpuinfo | grep -o '\bnx\b'

Если вывод содержит nx, процессор поддерживает запрет выполнения. В 32-битных системах для активации NX необходимо ядро с поддержкой PAE.

Проверка статуса NX для бинарного файла​


Утилита readelf позволяет проверить, помечен ли бинарный файл как совместимый с NX (отсутствие исполняемого стека):

Bash:
readelf -lW /usr/bin/ls | grep GNU_STACK

Вывод вида GNU_STACK ... RW означает, что стек не исполняемый. Флаг E в поле прав (RWE) указывает на исполняемый стек — это ослабляет защиту и обычно является ошибкой сборки.

Права на страницы работающего процесса​


Файл /proc/<pid>/maps показывает распределение виртуальной памяти процесса с правами доступа. Как правило, страницы стека имеют права rw-p (чтение, запись, приватные, без выполнения), что подтверждает работу NX:

Bash:
cat /proc/self/maps | grep stack

Взаимодействие с эксплуатацией уязвимостей​


NX/DEP блокирует простейший сценарий атаки: запись shellcode в буфер и переход на него. Однако это не делает систему неуязвимой. Атакующие разработали техники обхода:

Return-Oriented Programming (ROP)​


Вместо внедрения нового кода атакующий переиспользует уже существующие инструкции (гаджеты) из исполняемых сегментов программы и библиотек. ROP-цепочка строится из последовательности адресов возврата, каждый из которых указывает на полезную последовательность инструкций, завершающуюся ret. NX не защищает от ROP, потому что код выполняется из легитимных исполняемых страниц.

Ret2libc​


Частный случай ROP: вместо произвольных гаджетов атакующий вызывает функции стандартной библиотеки (например, system()), передавая нужные аргументы через стек. Это позволяет обойтись без внедрения собственного кода.

JIT-spraying​


Если приложение использует JIT-компиляцию (браузеры, виртуальные машины), атакующий может внедрить вредоносный байткод, который JIT скомпилирует в исполняемую память. DEP здесь не помогает, потому что память выделена с правами выполнения легально.

Совместимость и типичные проблемы​


DEP может нарушать работу приложений, которые:

  • Генерируют код динамически без явного запроса прав на выполнение
  • Содержат исполняемый код в секциях данных (thunks, trampolines)
  • Используют старые версии библиотек, не учитывающих NX

Например, приложения, написанные с использованием Active Template Library (ATL) версии 7.1 и ранее, могут пытаться выполнить код на страницах, помеченных как неисполняемые, что приводит к NX fault и завершению процесса.

Для PE-файлов, содержащих код в секции данных, необходимо добавить атрибут IMAGE_SCN_MEM_EXECUTE в поле Characteristics заголовка соответствующей секции. Альтернатива — перенести код в секцию .text.

В Linux аналогичная проблема возникает, если бинарный файл собран с исполняемым стеком (флаг PT_GNU_STACK с правами RWE). Линковщик по умолчанию делает стек неисполняемым, но некоторые ассемблерные вставки или устаревшие объектные файлы могут переопределить это поведение.

Диагностика падения из-за DEP в Windows​


При отладке приложения, завершающегося из-за DEP, в отладчике будет видно исключение STATUS_ACCESS_VIOLATION (код 0xC0000005). Дополнительный флаг в записи исключения указывает, что нарушение вызвано именно попыткой выполнения, а не чтения или записи. В диспетчере задач можно включить столбец «DEP» на вкладке «Процессы», чтобы увидеть, активна ли защита для конкретного процесса.

Ограничения NX/DEP​


NX/DEP — один из слоёв защиты, а не полное решение. Оно не защищает от:

  • ROP и ret2libc (код выполняется из легитимных страниц)
  • Атак на логику приложения
  • Уязвимостей, не связанных с выполнением кода из данных (use-after-free с переиспользованием объектов, type confusion)
  • JIT-эксплойтов

Для полноценной защиты NX комбинируется с другими механизмами: ASLR (рандомизация адресного пространства), Stack Canaries (обнаружение переполнения стека), CFG (Control Flow Guard в Windows), CFI (Control Flow Integrity в компиляторах), SEHOP (защита структурированных обработчиков исключений). Каждый из этих механизмов закрывает определённый класс атак, который NX оставить открытым.

Источники​


 

Похожие темы

Назад
Верх Низ