Главная / Блог / Скрытые угрозы ресурсов EXE: шелл-код внутри PE-файла

Скрытые угрозы ресурсов EXE: шелл-код внутри PE-файла

28 ИЮЛЬ, 2025
Скрытые угрозы ресурсов EXE: шелл-код внутри PE-файла
Скрытые угрозы .rsrc: шелл-коды в секции ресурсов PE-файла

Введение

Секция .rsrc может таить в себе весьма коварные вещи, и в данном обзоре мы приоткроем этот "ящик Пандоры".

Общие сведения о ресурсах

Разбираясь с секцией ресурсов приходит осознание того, что «майки» будто специально предоставили нам легальное средство вызова шеллов из исполняемых файлов РЕ. Понятное дело в корне инженеры преследовали совсем иную цель, но в очередной раз получилось как всегда. А всё потому, что в погоне за прибылью Microsoft забыла о простой истине: «Чем проще устройство, тем стабильнее оно работает». С форматом-же РЕ файлов дела обстоят прямо противоположно. Код системного загрузчика образов «Image Loader» настольно раздут, что в нём можно запросто разместить слона.

Начнём с того, что имя любой секции не несет никакого метафизического смысла, и было введено исключительно из эстетических соображений. Поскольку системный загрузчик его игнорирует, мы можем пронумерировать свои секции хоть от 1 до N, и файл прекрассно отработает в памяти. Это потому, что лоадер ищет секции строго в каталоге «IMAGE_DATA_DIRECTORY» РЕ-заголовка, где лежат RVA-указатели (от ImageBase) на все из них. От сюда следует, что если мы хотим найти какую-нибудь секцию внутри жертвы, то ни в коем случае не должны делать ставку на стандартные имена, а искать только через каталог. Каждая запись в этом каталоге имеет размер 8 байт (4 для адреса + 4 под её размер). Всего имеется 16 таких записей, а секция-ресурсов всегда лежит под номером три – это представлено на скрине ниже:

oleaut32.dll

Зато некоторые вирусы/протекторы/упаковщики распознают личные секции только по ник-нэйму, и всякое искажение имени валит их наповал. Более того, даже прогеры из Microsoft допускают эту грубую ошибку в своих системных либах, например «oleaut32.dll» – она распознаёт секцию ресурсов файла по имени, а не по записи в «DATA_DIRECTORY». Дизассемблирование подтверждает, что она действительно содержит внутри себя текстовую строку ".rsrc" и активно её использует.

oleaut32.dll

Эта (на первый взгляд мелкая) ошибка приводит к тому, что если мы загружаем oleaut32.dll в своё приложение, то наша секция-ресурсов обязательно должна ходить под ником «RSRC», иначе в лучшем случае ресурсы могут быть проигнорированны, а в худшем софт вообще упадёт. Автора этой dll можно смело назвать идиотом, т.к. эту либу импортирует добрая половина и системных приложений, в результате чего имеем зависимость типа «снежный ком». Именно по этой причине буквально весь софт под Windows всегда оставляет оригинальное имя секции-ресурсов, чтобы случайно не нарваться на этот шип. Это касается и пакера UPX – он или вообще не трогает ресурсы, или-же оставляет их оригин.имя в своих нёдрах.

smbios_v14_upx.exe

Вызов шелл-кода из ресурсов

Секция ресурсов может содержать в себе следующие 20 типов данных, при этом большинство из них являются бинарными, как и сам шелл-код. Например нельзя физически отличить картинки, шрифты и анимацию от двоичного кода – в более критических случаях можно предварить шелл сигнатурой битпама *.bmp, и запускать его на исполнение не с первого байта тупо пропустив подложную сигнатуру:

RT_CURSOR         =  1    Курсор
RT_BITMAP         =  2    Растровое изображение
RT_ICON           =  3    Иконка приложения
RT_MENU           =  4    Меню приложения
RT_DIALOG         =  5    Диалоговое окно
RT_STRING         =  6    Запись в таблице строк
RT_FONTDIR        =  7    Каталог шрифтов
RT_FONT           =  8    Шрифт
RT_ACCELERATOR    =  9    Разделитель в меню
RT_RCDATA         =  10   Ресурс, определяемый приложением (RAW данные)
RT_MESSAGETABLE   =  11   Запись в таблице сообщений
RT_GROUP_CURSOR   =  12   Аппаратно-независимый курсор
RT_GROUP_ICON     =  14   Аппаратно-независимый ресурс значков
RT_VERSION        =  16   Версия приложения
RT_PLUGPLAY       =  19   Ресурс Plug & Play
RT_VXD            =  20   VxD (Virtual Device Driver, только для Win9x)
RT_ANICURSOR      =  21   Анимированный курсор
RT_ANIICON        =  22   Анимированная иконка
RT_HTML           =  23   HTML-ресурс
RT_MANIFEST       =  24   Данные манифеста приложения

Список-же функций WinAPI для работы с ресурсами вообще состоит из ~10 тушканчиков – графику мы можем загружать напрямую через LoadBitmap/Icon/ImageString(), а остальные нужно искать по идентификаторам внутри секции через FindResource() с последующим LoadResource().

LoadBitmap
LoadIcon
LoadImage
LoadString

FindResource
SizeofResource
LoadResource

Тонкости программирование шелл-кодов выходит за рамки статьи, и может мы ещё вернёмся к этому вопросу, а пока рассмотрим вариант его запуска из ресурсов РЕ-файла. Проблема в том, что в атрибутах секции ресурсов нет флага «Исполняемая», и если авер обнаружит таковой, то сразу отправит файл на карантин. Поэтому наша задача найти указатель на тушку шелла через Find/LoadResource(), далее загрузить шелл в заранее выделенную VirtualAlloc() память с атрибутами PAGE_EXECUTE_READWRITE, и наконец стартануть его с чистой совестью. На языке ассемблера это выглядит примерно так – будем считать, что «ID_SHELL» это идентификатор ресурса, а «shell.bin» это сам шелл-кол:

.data
shRes   dd  0
shSize  dd  0
shData  dd  0
;//-----------------

.code
invoke  FindResource,  0, ID_SHELL, ’shell.bin’
mov     [shRes],eax
invoke  SizeofResource,0, eax
mov     [shSize],eax
invoke  LoadResource,  0, eax
mov     [shData],eax

invoke  VirtualAlloc,  0, eax, MEM_COMMIT, PAGE_EXECUTE_READWRITE
push    eax
invoke  WriteProcessMemory, -1, eax, [shData], [shSize], 0
pop     eax
jmp     eax

Однако есть и более изящный способ заключающийся в том, что в арсенале WinAPI имеется не только функция загрузки, но и перечисления ресурсов по их типам EnumResourceTypes(). То-есть мы задаём своему шеллу тип RT_RCDATA=10 (сырые данные без формата), после чего ищем их в секции-ресурсов. Плюс такого подхода в том, что одним из параметров данной функции является указатель на обратный вызов «Callback», внутри которого мы уже тянем за шелл. Если из колбека мы вернём EAX=0, то поиск прекратиться. Как правило колбеки имеют все функции WinAPI с префиксом Enum_XX().

EnumResourceLanguages
EnumResourceNames
EnumResourceTypes

BOOL EnumResourceTypesA(
  [in,opt] HMODULE     hModule,
  [in]     Callback    lpEnumFunc,
  [in]     LONG_PTR    lParam
);

Заключение

Два описанных выше метода имеют один важный недостаток – это вызов API-функций, от которых хорошо-бы избавиться. Если учесть, что имеем указатель на секцию-ресурсов в записи(3) «IMAGE_DATA_DIRECTORY», то остаётся вручную пропарсить всю секцию в поисках нужного нам ресурса. Однако это анулирует только функции Find/LoadResource(), а выделять исполняемую память через VirtualAlloc() с последующим копированием туда шелла придётся по любому посредством WinAPI.

Поделиться

0 комментариев

Пожалуйста, войдите, чтобы оставить комментарий.

Загрузка комментариев...