1. Вводная часть
2. Атрибуты вирт.страниц в записях РТЕ
3. Характеристики в заголовке РЕ-файла
4. Заключение
1. Вводная часть
Системный механизм безопасности DEP (Data Execution Prevention, предотвращение выполнения данных) в своё время наделал много шуму, и как оказалось он имеет некоторые нюансы. Всё сказанное ниже касается только 32-битных приложений Win, для запуска которых на системах х64 предусмотрена прослойка WoW64 (Windows on Windows).
Если проследить от куда у DEP ноги растут, то получаем следующую картину. На нижнем уровне имеем таблицу дескрипторов GDT, флаги в которой задают атрибуты целым сегментам памяти, например кода и данных в юзер-спейс (кольцо 3), кода/данных в пространстве ядра (кольцо 0), а так-же структуре РЕВ (см. селектор 50 ниже). На команду
Однако после перехода в РМ включается пейджинг (страничная организация памяти), и таблица GDT уходит уже на второй план, уступая место 4-уровневой таблице страниц "PageTable". То-есть большие сегменты вирт.памяти делятся на более мелкие страницы размером по 4 КБ. Записи "РТЕ" в этой таблице описывают атрибуты страниц и соответствие вирт.адреса физ.кадру памяти PFN (Page Frame Number). Так вот механизм DEP включается именно старшим битом(63) в РТЕ, который известен как "Бит XD" в библии Intel'a (eXecute Disable), или "NX" в доках AMD (No-eXecute).
2. Атрибуты вирт.страниц в записях РТЕ
Чтобы наглядно продемонстрировать эту фишку можно провести небольшой эксперимент, использовав в качестве кролика, например, процесс калькулятора. Значит запустим calc.exe, и получив адрес его структуры EPROCESS, запросим атрибуты записи PTE страницы, куда отображается неисполняемая структура РЕВ процесса:
Здесь видно, что вирт.адрес записи РТЕ равен
Активность DEP для процесса calc.exe подтверждает и системный "Диспетчер задач", или более продвинутый его форк "Process Hacker". А вот у страниц Total, AkelPad и WinWord бит DEP вместе с ASLR уже отключён:
Но во-всей этой кухне интересно другое. Мы знаем, что страницы могут иметь три основных атрибута - это чтение(R), запись(W), и исполнение(Е). Эти атрибуты задаются в аргументах таких функций как
В качестве пруфа напишем простое 32-битное приложение с процедурой "HelloWorld", которую поместим для исполнения сначала в секцию данных, а потом скопируем её и в стек. Обратите внимание, что механизм DEP при этом в системе включён для всех (см. Win+Break --> Доп.параметры --> Быстродействие --> DEP).
Как видим, даже с активным DEP данные всё-равно доступны для исполнения (см.файл в скрепке)! Так в чём-же подвох? А дело в том, что мало иметь включённый DEP на уровне ЦП и системы, так ещё и само приложение должно быть скомпилированно с одноимённым ключом. У компоновщиков С++ и MASM этот ключ называется
3. Характеристики в заголовке РЕ-файла
А вот компилятор FASM не может собирать исходники с ключами, поэтому нужно указать его явно в поле "DLL Characteristics" опционального заголовка уже скомпилированного РЕ-файла. Для манипуляций подобного рода как-нельзя лучше подходит крутая софтина
Только теперь наше приложение будет иметь полноценную защиту от исполнения данных DEP, и если сейчас запустить его, то сразу-же получим крэш с ошибкой доступа
4. Заключение
DEP в WoW64 работает, но с ограничениями, т.к. это реализованный в либе Wow64.dll эмулятор, запускающий 32-бит приложения на 64-бит оси. Политика DEP может быть менее строгой или вовсе конфликтовать, если старые программы требуют выполнения кода из стека/кучи, что аппаратная защита ЦП в виде бита NX/XD блокирует. Для корректной работы 32-битных программ, DEP работает в режиме «Opt-Out» или принудительно отключается системой, чтобы избежать критических ошибок. Таким образом нельзя сказать, что DEP в WoW64 не работает полностью - просто он часто уходит в менее строгий режим, для совместимости с 32-битным софтом.
2. Атрибуты вирт.страниц в записях РТЕ
3. Характеристики в заголовке РЕ-файла
4. Заключение
1. Вводная часть
Системный механизм безопасности DEP (Data Execution Prevention, предотвращение выполнения данных) в своё время наделал много шуму, и как оказалось он имеет некоторые нюансы. Всё сказанное ниже касается только 32-битных приложений Win, для запуска которых на системах х64 предусмотрена прослойка WoW64 (Windows on Windows).
Если проследить от куда у DEP ноги растут, то получаем следующую картину. На нижнем уровне имеем таблицу дескрипторов GDT, флаги в которой задают атрибуты целым сегментам памяти, например кода и данных в юзер-спейс (кольцо 3), кода/данных в пространстве ядра (кольцо 0), а так-же структуре РЕВ (см. селектор 50 ниже). На команду
dg (dump gdt) отладчик WinDbg отзывается таким логом:
Код:
0: kd> dg 0 80
P Gr Pr Lo
Sel Base Limit Type l an es ng Flags
---- ----------------- ----------------- ---------- - -- -- -- --------
0000 00000000`00000000 00000000`00000000 <Reserved> 0 By Np Nl 00000000
0008 00000000`00000000 00000000`00000000 <Reserved> 0 By Np Nl 00000000
0010 00000000`00000000 00000000`00000000 Code RE Ac 0 By P Lo 0000029b
0018 00000000`00000000 00000000`ffffffff Data RW Ac 0 Pg P Nl 00000c93
0020 00000000`00000000 00000000`ffffffff Code RE Ac 3 Pg P Nl 00000cfb
0028 00000000`00000000 00000000`ffffffff Data RW Ac 3 Pg P Nl 00000cf3
0030 00000000`00000000 00000000`00000000 Code RE Ac 3 By P Lo 000002fb
0038 00000000`00000000 00000000`00000000 <Reserved> 0 By Np Nl 00000000
0040 00000000`0379c000 00000000`00000067 TSS32 Busy 0 By P Nl 0000008b
0048 00000000`0000ffff 00000000`0000f800 <Reserved> 0 By Np Nl 00000000
0050 ffffffff`fffde000 00000000`00003c00 Data RW Ac 3 By P Nl 000004f3
0058 00000000`00000000 00000000`00000000 <Reserved> 0 By Np Nl 00000000
0060 00000000`00000000 00000000`ffffffff Code RE 0 Pg P Nl 00000c9a
0068 00000000`00000000 00000000`00000000 <Reserved> 0 By Np Nl 00000000
0070 00000000`00000000 00000000`00000000 <Reserved> 0 By Np Nl 00000000
0078 00000000`00000000 00000000`00000000 <Reserved> 0 By Np Nl 00000000
0080 Unable to get descriptor
0: kd>
------------------------------------
Pl : Privilege (0=Kenel, 3=User)
Gran: Granularity (By=Byte, Pg=Page)
Pres: Present (Np=No, P=Yes)
Long: Size (Nl=32, Lo=64 bit)
Однако после перехода в РМ включается пейджинг (страничная организация памяти), и таблица GDT уходит уже на второй план, уступая место 4-уровневой таблице страниц "PageTable". То-есть большие сегменты вирт.памяти делятся на более мелкие страницы размером по 4 КБ. Записи "РТЕ" в этой таблице описывают атрибуты страниц и соответствие вирт.адреса физ.кадру памяти PFN (Page Frame Number). Так вот механизм DEP включается именно старшим битом(63) в РТЕ, который известен как "Бит XD" в библии Intel'a (eXecute Disable), или "NX" в доках AMD (No-eXecute).
2. Атрибуты вирт.страниц в записях РТЕ
Чтобы наглядно продемонстрировать эту фишку можно провести небольшой эксперимент, использовав в качестве кролика, например, процесс калькулятора. Значит запустим calc.exe, и получив адрес его структуры EPROCESS, запросим атрибуты записи PTE страницы, куда отображается неисполняемая структура РЕВ процесса:
Код:
0: kd> !process 0 0 calc.exe
PROCESS fffffa80115c7b00
SessionId: 1 Cid: 1198 Peb: 7fffffde000 ParentCid: 05d8
DirBase: 2996d2000 ObjectTable: fffff8a0051027a0 HandleCount: 98.
Image: calc.exe
0: kd> .process /p fffffa80115c7b00 <-----// Переключить контекст на calc.exe
Implicit process is now fffffa80`115c7b00
0: kd> !cmkd.ptelist -v 7fffffde000 <-----// Адрес структуры РЕВ
VA=000007FFFFFDE000
PXE Idx=00F Va=FFFFF6FB7DBED078 Contents=507000031AB28867 Hard Pfn=0031AB28 Attr=---DA--UWEV
PPE Idx=1FF Va=FFFFF6FB7DA0FFF8 Contents=33F0000178029867 Hard Pfn=00178029 Attr=---DA--UWEV
PDE Idx=1FF Va=FFFFF6FB41FFFFF8 Contents=00A00003ABC2A867 Hard Pfn=003ABC2A Attr=---DA--UWEV
PTE Idx=1DE Va=FFFFF683FFFFFEF0 Contents=831000020D2AB967 Hard Pfn=0020D2AB Attr=-G-DA--UW-V
Здесь видно, что вирт.адрес записи РТЕ равен
0xFFFFF683`FFFFFEF0, и это физ.фрейм в ОЗУ под номером Pfn=0020D2AB. Значение-же самой записи "Entry" указано в столбце "Contents", а в ядре её олицетворяет структура MMPTE_HARDWARE. Обратите внимание на последнюю строку "NoExecute=1" ниже - это и есть бит(NX), который представляет собой рубильник механизма DEP для данной вирт.страницы:
Код:
0: kd> dt _mmpte_hardware FFFFF683FFFFFEF0
nt!_MMPTE_HARDWARE
+0x000 Valid : 0y1
+0x000 Dirty1 : 0y1
+0x000 Owner : 0y1
+0x000 WriteThrough : 0y0
+0x000 CacheDisable : 0y0
+0x000 Accessed : 0y1
+0x000 Dirty : 0y1
+0x000 LargePage : 0y0
+0x000 Global : 0y1
+0x000 CopyOnWrite : 0y0
+0x000 Unused : 0y0
+0x000 Write : 0y1
+0x000 PageFrameNumber : 0y000000000000001000001101001010101011 (0x20d2ab)
+0x000 reserved1 : 0y0000
+0x000 SoftwareWsIndex : 0y00000110001 (0x31)
+0x000 NoExecute : 0y1
0: kd>
Активность DEP для процесса calc.exe подтверждает и системный "Диспетчер задач", или более продвинутый его форк "Process Hacker". А вот у страниц Total, AkelPad и WinWord бит DEP вместе с ASLR уже отключён:
Но во-всей этой кухне интересно другое. Мы знаем, что страницы могут иметь три основных атрибута - это чтение(R), запись(W), и исполнение(Е). Эти атрибуты задаются в аргументах таких функций как
VirtualAlloc() и VirtualProtect(). Но если посмотреть на биты структуры MMPTE_HARDWARE, то среди них имеется лишь бит "Write" (страница доступна для записи), а вот бита "Execute" как-раз таки нет! От сюда следует, что любые страницы вирт.памяти доступны для исполнения, пусть это будет хоть секция-данных, хоть стек. Другими словами, система и сам процессор могут только запретить исполнение битом NX в РТЕ, хотя в дефолте буквально все страницы исполняемые.В качестве пруфа напишем простое 32-битное приложение с процедурой "HelloWorld", которую поместим для исполнения сначала в секцию данных, а потом скопируем её и в стек. Обратите внимание, что механизм DEP при этом в системе включён для всех (см. Win+Break --> Доп.параметры --> Быстродействие --> DEP).
C-подобный:
format pe gui
include 'win32ax.inc'
entry start
;//----------
.data
szText1 db 'Launch code from data section',0
szText2 db 'Launch code from STACK',0
;// Процедура для исполнения в секции-данных
align 4
startDep:
invoke MessageBox,0,szText1,0,0
ret
align 4
endDep:
;//----------
section '.code' code readable executable
start:
;// Первый вызов из секции данных - ОК!
call startDep
;// Теперь копируем процедуру в стек
push esp
mov ecx,endDep - startDep ;// размер кода
sub esp,ecx ;// выделяем фрейм в стеке
mov dword[startDep+5],szText2 ;// изменить текст мессаги
mov esi,startDep ;// источник для копирования
mov edi,esp ;// приёмник
cld ;// прямой шаг
rep movsb ;// скопировать ECX-байт..
call esp ;// вызов процедуры из стека!
add esp,endDep - startDep ;// восстановить стек,
pop esp ;// ..и указатель на него
invoke ExitProcess,0 ;// на выход
;//----------
section '.idata' import data readable
library user32,'user32.dll', kernel32,'kernel32.dll'
include 'api\user32.inc'
include 'api\kernel32.inc'
Как видим, даже с активным DEP данные всё-равно доступны для исполнения (см.файл в скрепке)! Так в чём-же подвох? А дело в том, что мало иметь включённый DEP на уровне ЦП и системы, так ещё и само приложение должно быть скомпилированно с одноимённым ключом. У компоновщиков С++ и MASM этот ключ называется
/NXCOMPAT, и он включён в дефолте так, что его можно только сбросить /NXCOMPAT[:NO].3. Характеристики в заголовке РЕ-файла
А вот компилятор FASM не может собирать исходники с ключами, поэтому нужно указать его явно в поле "DLL Characteristics" опционального заголовка уже скомпилированного РЕ-файла. Для манипуляций подобного рода как-нельзя лучше подходит крутая софтина
Ссылка скрыта от гостей
- этот процесс показан на скрине ниже. Родственная фишка рандомизации базы образа ASLR включается здесь битом "DLL can move" (в бинаре должна быть секция ".reloc"):
Код:
0x0001 Зарезерв (должно быть ноль).
0x0002 Зарезерв.
0x0004 Зарезерв.
0x0008 Зарезерв.
0x0020 IMAGE_DLL_HIGH_ENTROPY_VA = ASLR с энтропией 24 бита (для х64).
0x0040 IMAGE_DLL_DYNAMIC_BASE = ASLR с энтропией 8 бит (для х32).
0x0080 IMAGE_DLL_FORCE_INTEGRITY = обязательная проверка целостности кода.
0x0100 IMAGE_DLL_NX_COMPAT = образ совместим с NX (механизм DEP).
0x0200 IMAGE_DLL_NO_ISOLATION = не изолировать образ.
0x0400 IMAGE_DLL_NO_SEH = запрет на использование в образе обработчиков исключений SEH.
0x0800 IMAGE_DLL_NO_BIND = не связываемый образ.
0x1000 IMAGE_DLL_APPCONTAINER = образ должен выполняться в AppContainer.
0x2000 IMAGE_DLL_WDM_DRIVER = образ является драйвером WDM.
0x4000 IMAGE_DLL_GUARD_CF = поддерживает защиту Control Flow Guard, CFG.
0x8000 IMAGE_DLL_TERMINAL_SERVER_AWARE = образ является терминальным сервером.
Только теперь наше приложение будет иметь полноценную защиту от исполнения данных DEP, и если сейчас запустить его, то сразу-же получим крэш с ошибкой доступа
0xC0000005. От сюда следует, что сбросив всего один бит в х32 исполняемом файле мы можем полностью анулировать этого сторожа, после чего без проблем внедрять свой шелл в любое место процесса-жертвы, для последующего запуска от туда через Wow64.4. Заключение
DEP в WoW64 работает, но с ограничениями, т.к. это реализованный в либе Wow64.dll эмулятор, запускающий 32-бит приложения на 64-бит оси. Политика DEP может быть менее строгой или вовсе конфликтовать, если старые программы требуют выполнения кода из стека/кучи, что аппаратная защита ЦП в виде бита NX/XD блокирует. Для корректной работы 32-битных программ, DEP работает в режиме «Opt-Out» или принудительно отключается системой, чтобы избежать критических ошибок. Таким образом нельзя сказать, что DEP в WoW64 не работает полностью - просто он часто уходит в менее строгий режим, для совместимости с 32-битным софтом.
Вложения
Последнее редактирование: