Показать сообщение отдельно

  #1  
Старый 30.01.2026, 13:13
Marylin
Постоянный
Регистрация: 01.09.2019
Сообщений: 378
Провел на форуме:
145166

Репутация: 0
По умолчанию

1. Вводная часть
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    0  By  Np  Nl  00000000
0008  00000000`00000000  00000000`00000000    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    0  By  Np  Nl  00000000
0040  00000000`0379c000  00000000`00000067  TSS32 Busy  0  By  P   Nl  0000008b
0048  00000000`0000ffff  00000000`0000f800    0  By  Np  Nl  00000000
0050  ffffffff`fffde000  00000000`00003c00  Data RW Ac  3  By  P   Nl  000004f3
0058  00000000`00000000  00000000`00000000    0  By  Np  Nl  00000000
0060  00000000`00000000  00000000`ffffffff  Code RE     0  Pg  P   Nl  00000c9a
0068  00000000`00000000  00000000`00000000    0  By  Np  Nl  00000000
0070  00000000`00000000  00000000`00000000    0  By  Np  Nl  00000000
0078  00000000`00000000  00000000`00000000    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        !cmkd.ptelist -v 7fffffde000        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" опционального заголовка уже скомпилированного РЕ-файла. Для манипуляций подобного рода как-нельзя лучше подходит крутая софтина CFF Explorer - этот процесс показан на скрине ниже. Родственная фишка рандомизации базы образа ASLR включается здесь битом "DLL can move" (в бинаре должна быть секция ".reloc"):


Только теперь наше приложение будет иметь полноценную защиту от исполнения данных DEP, и если сейчас запустить его, то сразу-же получим крэш с ошибкой доступа
Код:
0xC0000005
. От сюда следует, что сбросив всего один бит в х32 исполняемом файле мы можем полностью анулировать этого сторожа, после чего без проблем внедрять свой шелл в любое место процесса-жертвы, для последующего запуска от туда через Wow64.


4. Заключение

DEP в WoW64 работает, но с ограничениями, т.к. это реализованный в либе Wow64.dll эмулятор, запускающий 32-бит приложения на 64-бит оси. Политика DEP может быть менее строгой или вовсе конфликтовать, если старые программы требуют выполнения кода из стека/кучи, что аппаратная защита ЦП в виде бита NX/XD блокирует. Для корректной работы 32-битных программ, DEP работает в режиме «Opt-Out» или принудительно отключается системой, чтобы избежать критических ошибок. Таким образом нельзя сказать, что DEP в WoW64 не работает полностью - просто он часто уходит в менее строгий режим, для совместимости с 32-битным софтом.
 
Ответить с цитированием