Главная / Блог / Скрытые угрозы ресурсов EXE: шелл-код внутри PE-файла
Секция .rsrc может таить в себе весьма коварные вещи, и в данном обзоре мы приоткроем этот "ящик Пандоры".
Разбираясь с секцией ресурсов приходит осознание того, что «майки» будто специально предоставили нам легальное средство вызова шеллов из исполняемых файлов РЕ. Понятное дело в корне инженеры преследовали совсем иную цель, но в очередной раз получилось как всегда. А всё потому, что в погоне за прибылью Microsoft забыла о простой истине: «Чем проще устройство, тем стабильнее оно работает». С форматом-же РЕ файлов дела обстоят прямо противоположно. Код системного загрузчика образов «Image Loader» настольно раздут, что в нём можно запросто разместить слона.
Начнём с того, что имя любой секции не несет никакого метафизического смысла, и было введено исключительно из эстетических соображений. Поскольку системный загрузчик его игнорирует, мы можем пронумерировать свои секции хоть от 1 до N, и файл прекрассно отработает в памяти. Это потому, что лоадер ищет секции строго в каталоге «IMAGE_DATA_DIRECTORY» РЕ-заголовка, где лежат RVA-указатели (от ImageBase) на все из них. От сюда следует, что если мы хотим найти какую-нибудь секцию внутри жертвы, то ни в коем случае не должны делать ставку на стандартные имена, а искать только через каталог. Каждая запись в этом каталоге имеет размер 8 байт (4 для адреса + 4 под её размер). Всего имеется 16 таких записей, а секция-ресурсов всегда лежит под номером три – это представлено на скрине ниже:
Зато некоторые вирусы/протекторы/упаковщики распознают личные секции только по ник-нэйму, и всякое искажение имени валит их наповал. Более того, даже прогеры из Microsoft допускают эту грубую ошибку в своих системных либах, например «oleaut32.dll» – она распознаёт секцию ресурсов файла по имени, а не по записи в «DATA_DIRECTORY». Дизассемблирование подтверждает, что она действительно содержит внутри себя текстовую строку ".rsrc" и активно её использует.
Эта (на первый взгляд мелкая) ошибка приводит к тому, что если мы загружаем oleaut32.dll в своё приложение, то наша секция-ресурсов обязательно должна ходить под ником «RSRC», иначе в лучшем случае ресурсы могут быть проигнорированны, а в худшем софт вообще упадёт. Автора этой dll можно смело назвать идиотом, т.к. эту либу импортирует добрая половина и системных приложений, в результате чего имеем зависимость типа «снежный ком». Именно по этой причине буквально весь софт под Windows всегда оставляет оригинальное имя секции-ресурсов, чтобы случайно не нарваться на этот шип. Это касается и пакера UPX – он или вообще не трогает ресурсы, или-же оставляет их оригин.имя в своих нёдрах.
Секция ресурсов может содержать в себе следующие 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.
Все права защищены. © 2016 - 2025