Добавление ресурсов в Windows-программы без VS
Дисклеймер: в статье приводится пример использования ресурсов для встраивания шеллкода в исполняемый файл. Автор блога не несёт отвественности за ваши действия и т.д. и т.п….
Ресурсы - это дополнительные двоичные данные, которые можно встроить прямо в исполняемый exe-файл. В Windows они обычно используются для хранения иконок, шрифтов или меню графических приложений, но также при помощи них можно добавлять полезную нагрузку вроде шеллкодов. Непосредственно с этой точки зрения и рассмотрим, как добавить ресурс к нашей программе.
В этой статье не будет подробно рассматриваться система ресурсов, как они работают “под капотом” и как управлять большим количеством ресурсов. Для крупных проектов я советую воспользоваться Visual Studio, сильно облегчающей жизнь в этом случае. Я хотел бы рассмотреть довольно специфическую проблему добавления ресурсов без использования Visual Studio.
Для начала создадим ресурсный скрипт (.rc-файл), который будет
определять включаемые нами ресурсы; назовём его
resources.rc
. Каждая строка в этом скрипте имеет следующую
структуру:
Name Type Filename
Допустим, мы хотим добавить в наш исполняемый файл иконку
logo.ico
:
Logo ICON "logo.ico"
Обратите внимание:
Имя ресурса чувствительно к регистру - в коде программы его надо будет указать так же, как он был указан в ресурсном файле;
Тип может быть любым, однако есть определённый набор встроенных типов для иконок, кареток и т.д. Как я понимаю, в большинстве случаев, когда эти типы уместны, удобнее использовать их. Я покажу на примере кастомного типа, а отличия между ними рассмотрены здесь.
Полученный rc-файл необходимо скомпилировать в двоичный res-файл. Для этого необходимо воспользоваться утилитой из набора инструментов для разработки MSVC Build Tools. Откроем Developer PowerShell for VS 2022:
.exe /r .\resources.rc rc
На выходе получим файл resources.res
.
Теперь в программе, в которой мы планируем использовать ресурс, нам
необходимо найти ресурс при помощи FindResource
и загрузить
его с помощью LoadResource
:
= FindResource(NULL, "Payload", "PAYLOAD");
HRSRC shellcodeRes = LoadResource(NULL, shellcodeRes); HGLOBAL shellcodeRsData
Обе функции возвращают NULL
при ошибке; это стоит
проверять в реальной программе.
shellcodeRsData
- это и есть указатель на содержимое
загруженного ресурса. Теперь мы можем, например, получить его размер
(SizeofResource
), выделить исполняемую память, скопировать
ресурс туда и запустить шеллкод:
// Получаем размер полезной нагрузки
DWORD shellcodeSz = SizeofResource(NULL, shellcodeRes);
// Выделяем память с правами на чтение-запись и исполнение
void* exec = VirtualAlloc(0, shellcodeSz, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
// копируем полезную нагрузку из ресурса в выделенную память
memcpy(exec, shellcodeRsData, shellcodeSz);
// Вызываем загруженный код
((void(*)())exec)();
Осталось скомпилировать программу. В случае с clang заставить его слинковать ресурсный файл очень просто:
.c resources.res -o evil.exe clang main
Дополнительные материалы
ired.team - Loading and Executing Shellcode From PE Resources
The Inner Working of FindResource() and LoadString() Win32 Functions