DEP и ASLR – игра без правил. Часть 2
Life-Hack [Жизнь-Взлом]/ХакингРассмотрим очередную "бомбу" под названием "Advanced Space Layout Randomization" ASLR, которая начиная с Win-7 призвана рандомно менять базу образа в памяти. Отметим, что все системные файлы Win запускаются с включённым механизмом ASLR, но у сторонних приложений он может быть отключён. Чтобы наши программы влились в эту струю, нужно указать это явно, посредством флага компиляции "/DYNAMIC_BASE".
Для образов прикладных программ, новая рандомная база вычисляется при каждом запуске исполняемого файла. В отличие от юзерских программ, базы системных образов вычисляется только один раз, при начальной загрузке операционной системы и используется во всей системе вплоть до её перезагрузки. Если-бы системные DLL заново отображались по разным адресам внутри различных процессов, их код нельзя было-бы использовать совместно. Загрузчику пришлось-бы корректировать указатели на функции API для каждого процесса в отдельности, превращая общие функции в закрытые данные процесса.
Механизм ASLR рандомно меняет базы трёх блоков памяти процесса – это: база образа, база стека, и база кучи Heap. Рандом образа - 8-битный, что позволяет выбрать одну из 256 возможных баз. А вот рандом стека и кучи уже 5-битный – такая разрядность ограничивает выбор до 32-х базовых адресов.
Все-кто хочет под управлением ASLR кочевать в памяти, должны иметь в своём РЕ-заголовке определённые флаги, которые задаются программистом при компиляции проекта. Первое поле размером 16-бит находится по-смещению РЕ.16h и называется "Characteristics". Набор этих флагов определяет глобальные свойства исполняемого файла, и каждый из 16-ти его бит несёт в себе определённую информацию. Основные его биты перечислены нихе:
• Бит 0 – IMAGE_FILE_RELOCS_STRIPPED – таблетка от ASLR.
Устанавливается в единицу, если в файле нет информации о перемещениях, и он должен лечь на дефолтную базе. Если база занята кем-то другим, загрузчик сообщает об ошибке.
• Бит 1 – IMAGE_FILE_EXECUTABLE_IMAGE
Устанавливается, если файл является исполняемым экзе, и действительно может быть запущен
• Бит 4 – IMAGE_FILE_AGGRESIVE_WS_TRIM
Если взведён, то ОС принудительно урежет объём памяти для этого процесса, путём разбиения его на страницы. Этим битом могут похвастаться процессы-демоны, которые большую часть времени спят, пробуждаясь лишь несколько раз в день.
• Бит 5 – IMAGE_FILE_LARGE_ADDRESS_AWARE
Приложение способно работать с памятью, объёмом больше 2 Gb.
• Бит 12 – IMAGE_FILE_SYSTEM
Если взведён, значит это системный файл типа драйвера.
• Бит 13 – IMAGE_FILE_DLL
Взведённый бит является признаком DLL-библиотеки.
Из всей этой группы, в контексте ASLR примечательным является только бит (0), который разрешает релоки. У библиотек DLL он всегда сброшен, а у исполняемых файлов типа *.ехе – наоборот взведён. Это потому, что при запуске процесса на исполнение сначала в память загружается экзе, который сразу занимает предпочтительную базу, и только потом уже он подтягивает в своё пространство импортируемые либы – т.е. кто первым встал, того и база. Если DLL будет неперемещаемой, то может получиться конфликт базовых адресов EXE<->DLL.
Чтобы продемонстрировать состояние этих флагов, запустим программу "РЕ-Explorer". Она не то чтобы отображает флаги, но и позволяет редактировать их биты. Это одна из лучших утилит в своём роде, способная проникнуть в самые закрома исполняемых файлов:

Однако это не единственный бит, от которого пляшет ASLR.. а точнее - он для него второстепенный. Прямым рычагом для рандомной релокации образа является слово по-смещение РЕ.5Eh с несовпадающим по смыслу именем "DLL-characteristics". Именно в этом поле хранятся ключи, которые мы задаём компилятору при сборке своего исходника в экзе. Как упоминалось выше, чтобы системный механизм ASLR подхватил наш файл, нужно указать компилятору ключ "/DYNAMIC_BASE" – иначе образ так и останется неперемещаемым, хоть в системе и будет активен ASLR. Бит-мап 16-битного поля РЕ.5Еh представлен ниже:

C-подобный:
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 = образ является терминальным сервером.
Мда.. судя по характеристикам, адепты этой секты способны на многое, и ожидать от них можно чего угодно. Заполучив админские права, и привелегию "Debug" мы можем рекурсией обойти все файлы в системной папке и сбросить непригодные нам биты ASLR. Проблема открытых файлов и невозможности перезаписать их образ на диске, решается копированием дескрипторов открытых файлов функциями OpenProcess() с последующим DuplicateHandle(). В результате мы получим родительские права на объект, со-всеми вытекающими последствиями.
Не знаю для чего это нужно, ведь базу образа можно найти и динамическим путём. Любая малварь поколения "Next" поступает именно так, а жёсткую привязку через РЕ-заголовок можно найти сейчас только на свалке истории. Однако для самообразования можно написать такую утилиту, которая будет искать все исполняемые файлы в текущем дире жёсткого диска, и выводить информацию о поддержки ими технологии ASLR. Тут просто поиск и проверка флага "DLL_DYNAMIC_BASE» в поле РЕ.5Еh :
C-подобный:
format pe console
include 'win32ax.inc'
entry start
;//------
.data
capt db 13,10,' ASLR info v0.1'
db 13,10,' ~~~~~~~~~~~~~~~~~~~',0
info db 13,10,10,' File: ======== %s'
db 13,10,' PE.16h = %04X'
db 13,10,' PE.5Eh = %04X',0
aslr db ' <-- ASLR dynamicBase bit found',0
fData WIN32_FIND_DATA ;// структура для FindFile()
fName = fData +44 ;// ..смещение имени-файла в ней
fMask db '*.exe',0 ;// маска для поиска = только ЕХЕ
frmt db '%s',0 ;// спецификатор для scanf()
hndl dd 0 ;// хэндл поиска
buff rb 512 ;// буфер под РЕ-заголовок
;//------
.code
start: cinvoke printf,capt ;// шапка
;//--- Поиск подходящих кандидатов.. ---------
invoke FindFirstFile,fMask,fData ;// ищем первый файл
mov [hndl],eax ;// запомнить хэндл поиска
;//--- Сбрасываем заголовок найденного файла в буфер -----
@scan: invoke _lopen,fName,0 ;// открываем файл
push eax ;// запомнить его хэндл
invoke _lread,eax,buff,512 ;// считываем 512 байт в свой буфер
pop eax ;// хендл на родину
invoke _lclose,eax ;// файл больше не нужен..
;//--- Поиск флагов в буфере -----------------
mov esi,buff ;// ESI = голова буфа
mov esi,[esi+0x3c] ;// указатель на РЕ-заголовок
add esi,buff ;// смещаемся к нему
mov ax,word[esi+0x16] ;// AX = характеристики ЕХЕ
mov bx,word[esi+0x5e] ;// ВХ = характеристики DLL
and eax,0xffff ;// обнулить не нужное
and ebx,0xffff ;// ...^^^
push ebx ;// запомнить флаги DLL
cinvoke printf,info,fName,eax,ebx ;// выводим батву на консоль
;//--- Определяем поддержку файлом ASLR ------
pop ebx ;// ЕВХ = флаги DLL
test ebx,1000000b ;// чекнуть бит "DLL_DYNAMIC_BASE"
jz @next ;// если он нуль..
cinvoke printf,aslr ;// иначе: даём знать об этом!
@next: invoke FindNextFile,[hndl],fData ;// продолжить поиск в дире..
or eax,eax ;// если EAX = 0, нет больше файлов
jnz @scan ;// повторить, если NoZero.
invoke CloseHandle,[hndl] ;// прибить хэндл поиска
@exit: cinvoke scanf,frmt,capt ;// ждём клаву..
cinvoke exit,0 ;//
;//=== Секция импорта ========================
data import
library msvcrt,'msvcrt.dll',kernel,'kernel32.dll'
import msvcrt,printf,'printf',scanf,'scanf',exit,'exit'
import kernel,FindFirstFile,'FindFirstFileA',\
FindNextFile,'FindNextFileA',CloseHandle,'CloseHandle',\
_lopen,'_lopen',_lread,'_lread',_lwrite,'_lwrite',_lclose,'_lclose'
end data

По идее, чтобы прибить ASLR для избранных файлов, нужно обнаружив у него флаг "DynamicBase" сбросить его, тогда у механизма ASLR останутся несыгранные роли. Но чем он нам мешает? У рандомной смены базы тоже есть своя грация, над которой "художники" постарались на славу. М.Руссинович в седьмом издании своего бестселлера "Внутреннее устройство Windows" описывает эти механизмы на более глубоком уровне, вплоть до алгоритмов вычисления дельты для рандома и прочее. Так-что всем, кого интересует данная тема, советую почитать этот его том. А за сим всё.. до скорого.