Статьи «Что нам стоит антивирь построить?!» (2 часть)

Discussion in 'Статьи' started by Spider Agent, 18 Feb 2009.

  1. Spider Agent

    Spider Agent Elder - Старейшина

    Joined:
    22 Feb 2007
    Messages:
    6
    Likes Received:
    19
    Reputations:
    0
    Сканируем файл

    В этой части статьи мы коснемся, пожалуй, самого сложного, но в тоже время интересного момента создания «домашнего» антивируса. Мы займемся написанием процедуры, которая как раз и будет отличать обычный файл от вируса. Но не спешите открывать Delphi, как всегда придется сначала почитать теорию – а что делать без теории никуда:). Для начала нашей процедуре нужно передать имя файла (разумеется, не только имя, но и полный путь до него), которой мы будем проверять на «вшивость». Затем рассчитаем его контрольную сумму. Открываем базу, в которой хранятся сигнатуры вирусов, и начинаем сравнивать с полученной. Если не равны, то переходим к следующей и.т.д. до тех пор, пока все сигнатуры не кончатся. Если ни одна сигнатура не подошла, значит, файл чист, можем переходить к другому (если речь идет о проверки всего HDD). Если же контрольные суммы сошлись, то считываем номер строки сошедшейся сигнатуры, переходим на такою же строку только уже в базе с именами. Считываем ее значение. И, наконец, выдаем пользователю мессагу, что найден вирус – такой то, файл - такой-то. Удалить или нет? (ну, разумеется, всю информацию забиваем в отчет). С первого взгляда все нет так уж просто, как я обещал. Но не так страшен черт, как его малюют. Свидетельством этого является код, приведенный ниже:

    Code:
    procedure CheckFile(FileCrc: string);
    
    var count: integer;
    
    baza: textFile;
    
    VirCRC, VirName: string;
    
    Perexod: string;
    
    begin
    
    Perexod := #10 + #13; // можно оформить как константу
    
    Assignfile(baza, ApplicationPath + ‘\base\vb.bas’); // уже должно быть знакомо
    
    reset(baza); // открываем для чтения с первой строки
    
    count := 0; // обнуляем счетчик, отвечающий за номер строки
    
    while not eof(baza) do // читаем весь файл
    
    begin
    
    inc(count); // то же самое что и count:=count+1
    
    readln(baza, VirCRC); //читаем строку
    
    if (count mod 1000) = 0 then
    
    begin
    
    if canceled then break;
    
    end;
    
    if VirCRC = FileCrc then // Если контрольные суммы сошлись то…
    
    begin 
    
    v := v + 1; // количество найденных вирусов увеличиваем на 1
    
    Form1.naideno.Caption := inttostr(v); 
    
    // отображаем количество найденных вирусов
    
    VirName := GrabLine2(ApplicationPath + ‘\base\Vn.bas’, count);
    
    // узнаем имя вируса 
    
    if Application.MessageBox(Pchar(’Обнаружен вирус : ‘ + VirName + perexod +
    
    form1.Label3.Caption + perexod + ‘ Рекомендуется его удалить! Удалить файл?’),
    
    ‘Внимание! ‘,MB_ICONWARNING + MB_YESNO) = IDYES
    
    // выводим мессагу, с информацией и запросом на удаление файла
    
    then
    
    if deleteFile(Form1.Label3.Caption) then // если файл успешно удален то
    
    with Form1.report.Items.Add do 
    
    begin
    
    Caption := string(VirName); // забьем всю инфу 
    
    SubItems.Add(Form1.Label3.Caption);
    
    SubItems.Add(’Удален‘); // все OK!
    
    end
    
    else
    
    // а если нет, то
    
    with Form1.report.Items.Add do
    
    begin
    
    Caption := string(VirName); // делаем тоже самое
    
    SubItems.Add(Form1.Label3.Caption);
    
    SubItems.Add(’Не удален’); 
    
    // только пишем что файл не удалось удалить  
    
    end;
    
    end;
    
    end;
    
    CloseFile(baza); // ну и как всегда закроем файл
    
    end;
    Ну что? Разве сложно? Это и есть ВСЯ процедура проверки. По большому счету основной алгоритм можно уместить в десять строчек кода (и даже меньше). Остальное это разные украшательства – ну как же без этого?

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

    [Примечание]

    1)Переменная canceled(глобальная) была введена, для того чтобы можно было прервать процесс сканирования.

    Это необходимо в том случае если программа «зависнет» на проверки файла. Если честно, у меня такой ситуации пока еще не возникало, но подстраховаться не мешает.

    2) Удаление вируса производилось за счет стандартной функции окошек DeleteFile. Разумеется, если файл занят, то ничего удалить у нас не получится. Да и вообще не стоит доверять Windows Поэтому я бы рекомендовал написать что-нибудь свое, способное удалить запущенное приложение (например, искать процесс с таким же именем как у вируса, а потом его закрывать и только после этого пытаться удалять)

    3) Для красоты я выводил имя файла, которое сканируется в данный момент, поэтому, как ты уже мог заметить

    в самой процедуре путь до вируса мы узнаем через label3. Если вам это не нравится, вы можете создать глобальную переменную и работать уже с ней.

    4) А вот две функции необходимые для работы нашей процедуры:

    Code:
    function ApplicationPath: string; // определяем директорию программы
    
    begin
    
    Result := ExtractFilePath(ParamStr(0));
    
    end;
    и

    Code:
    function GrabLine(const baza: string; ALine: Integer): string;
    
    // функция для чтения заданной строки в текстовом файле 
    
    var
    
    sl: TStringList;
    
    begin
    
    sndPlaySound(ApplicationPath+’Alarm.WAV’, SND_ASYNC);
    
    // противный звук, сигнализирующий о наличии вируса
    
    
    sl := TStringList.Create; 
    
    try
    
    sl.LoadFromFile(baza);
    
    Result := sl[ALine - 1]; 
    
    finally
    
    sl.Free;
    
    end;
    
    end;
    [Лирическое отступление] Сразу хочу оговориться, чтобы потом в мой адрес не было никаких упреков. Данная процедура, и все вышесказанное являются только моим виденьем данного процесса. И никоем образом не являются, каким либо стандартом. Не надо высказывать свое мнение насчет того, что код не оптимизирован. Я не ставил задачу написать сверхбыстрый антивирус, а лишь пытался донести до читателя свои мысли и соображения по этому поводу

    Глобальный поиск

    Для полного счастья нам осталось научить наш антивирус проверять весь компьютер на наличие заразы. Как всегда начнем с того, что нам нужно. Если вы хоть раз работали с FindFirst, то должны знать что она ищет файлы в строго заданном каталоге. Отсюда следует, что сначала нам необходимо получить все локальные диски, а потом подставлять их в процедуру поиска. Ну что, не буду тянуть и сразу предлагаю разобрать вот эту простенькую процедуру:

    Code:
    procedure GetHDD;
    
    var
    
    Drive: Char; //Буква диска
    
    n: byte; // счетчик дисков
    
    lst: TStringList; // переменная в которой будут храниться список дисков
    
    const
    
    pref = ‘:\’;
    
    begin
    
    lst := TStringList.Create;
    
    lst.Clear;
    
    for Drive := ‘A’ to ‘Z’ do // перебираем буквы дисков
    
    if GetDriveType(PChar(Drive + pref)) = DRIVE_FIXED then 
    
    // и если диск не съемный то…
    
    lst.Add(Drive + pref); 
    
    //добавим в список дисков, которые нужно сканировать
    
    for n := 0 to (lst.Count - 1) do // по очереди начинаем сканировать
    
    ScanDir(lst.Strings[n]); // ScanDir – наша процедуру поиска файлов
    
    lst.Free;
    
    end;
    Я думаю, не стоит подробно останавливаться на этой процедуре, т.к.даже новичок сможет с ней разобраться. И, наконец, переходим к последнему этапу создания антивирусного сканера. Т.к. я люблю объяснять не на теории, а на практике, то сразу приведу код процедуры с комментариями. Если же вам нужна более подробная информация по функциям FindFirst, FindNext, FindClose, то в интернате ее навалом. Не поленитесь поискать.

    Code:
    procedure ScanDir(Dir: string);
    
    var
    
    SearchRec: TSearchRec;
    
    ras: string;
    
    sum: Dword;
    
    begin
    
    if Dir <> ” then if Dir[length(Dir)] <> ‘\’ then Dir := Dir + ‘\’;
    
    // Осуществляем поиск по всем вложенным папкам
    
    if FindFirst(Dir + ‘*.*’, faAnyFile, SearchRec) = 0 then
    
    //Ищем файлы с любым расширением
    
    repeat // пошел цикл
    
    //если имеет название “.” или “..”, тогда продолжаем
    
    if (SearchRec.name = ‘.’) or (SearchRec.name = ‘..’) then Continue;
    
    // если найден каталог то…
    
    if (SearchRec.Attr and faDirectory) <> 0 then
    
    ScanDir(Dir + SearchRec.name) //то проверим и его
    
    Else // иначе мы поймали файл
    
    begin
    
    Form1.label3.caption := dir + SearchRec.Name;
    
    // Выводим найденный файл(как я уже говорил, можете использовать глобальную
    
    // переменную)
    
    ras := AnsiLowerCase(ExtractFileExt(dir + SearchRec.name));
    
    // Узнаем расширение найденного файла
    
    if (ras = ‘.exe’) or (ras = ‘.dll’) then
    
    // И если оно либо *.exe или *.dll то…
    
    begin
    
    sum := FastCheckSum(dir + SearchRec.name);
    
    // Как вы уже успели понять, dir + SearchRec.name –это и есть “подозреваемый”
    
    CheckFile(IntToStr(Sum));
    
    // Начинаем проверять файл
    
    
    end;
    
    end;
    
    Application.ProcessMessages;
    
    if stopScan then // если stopScan=true , то ...
    
    begin
    
    Exit; // останавливаем поиск
    
    end;
    
    until FindNext(SearchRec) <> 0; 
    
    //Продолжаем поиск до тех пор, пока результат работы функции не равен нулю
    
    FindClose(SearchRec);
    
    // завершаем поиск, освобождаем память, выделенный системой под него
    
    end;
    Вот и все. Наш грозный антивирус готов к бою. Теперь он обладает самими примитивными функции для борьбы с вредоносными программами. Я же говорил, что это не так сложно как кажется с первого взгляда. Теперь все только в твоих руках. Можешь воспользоваться этой статьей чисто в образовательных целях, а можешь заняться разработкой «Антивирусом нового поколения»!!! И пони, не бойся ошибаться, постоянно пробуй и, в конце концов, все труды увенчаются успехом.

    Постскриптум

    Минусом данного сканера является отсутствие монитора, способного в реальном времени обнаружить и обезвредить вирус. В дальнейшем (если аудитория этого захочет) я постараюсь осветить и этот вопрос. А пока придется довольствоваться тем, что есть или не ждать моей статьи, а придумать все самому. Еще одним серьезным недостатком является то, что большинство вирусов в природе «обработаны» упаковщиками и разными крипторами. Если один и тот же вирус был упакован разными упаковщиками (мдя… масло масляное), то наш сканер обломается. Поэтому тем, кому захотелось устранить данный недостаток придется прочитать не один десяток крекерских статей, повествующих о таком сложном процессе как распаковка.

    [Не лирическое отступление] Хотелось бы сказать по поводу распаковки. Ясен пень, что у каждого пакера\криптрора\протектора свой алгоритм сжатия\защиты. Поэтому, если мы хотим что бы наш антивирус мог действительно искать вирусы, то придется писать распаковщик каждому пакеру\криптрору\протектору… Согласитесь работа не из приятных! А во-вторых нужно в своей базе иметь сигнатуры наших пакеров, если вирус будет упакован неизвестной «защитой», то наш антивирус опять же идет лесом(именно так работают все современные антивирусы - очень печально). И как же быть в таком случи? Ответ прост: Сделать универсальный распаковщик?(именно так я и поступил). Почему бы не использовать технологию крэкеров (всмысле не печенье, а людей, которые ломают софт). В общем суть такова: находим Оригинальную Точку Входа(кто не знает что это такое goto cracklab.ru ), снимаем дамп упакованной проги, правим его,- вот и все. Знающие люди скажут что еще надо восстановить таблицу импорта, но зачем нужен лишний геморрой? Мы просто будем сканировать дамп в районе точки входа, и отсутствие таблицы импорта нам не как не помещает. В этом процессе самое сложное – это найти оригинальную точку входа, в общем я не стал выпендриваться(придумывать велосипед) и взял плугин к одной известной крэкерской программе, которая собственно этим и занимается. А упакованность файла я проверял такой методикой как энтропия(опять же если не знаете что это такое goto cracklab.ru or wasm.ru). В итоге получился вполне путный распаковщик, который снимает почти все пакеры, и справляющийся с некоторыми протекторами. И опять же это тема отдельной статьи. И если тебя уважаемый читатель увлекла данная тема и ты хочешь увидеть продолжение статьи, то присылай все свои вопросы и пожелания мне на ящик. А мне остается только откланиться на этой позитивной ноте и пожелать вам удачи в этом нелегком труде…

    Исходники к статье

    Neon
    [email protected]
    Источник: SASecurity IB


    [​IMG]
     
    #1 Spider Agent, 18 Feb 2009
    Last edited: 21 Feb 2009
    1 person likes this.
  2. spider-intruder

    spider-intruder Elder - Старейшина

    Joined:
    9 Dec 2005
    Messages:
    700
    Likes Received:
    339
    Reputations:
    37
    В целом не плохо для людей, которые задают вопрос - "Как работает антивирус"

    Но есть масса но: "находим Оригинальную Точку Входа" - не так это и просто бывает...
    Для поиска точки входа зачастую придется отдать управление программе, то-есть все что будет будет до нее может содержать злонамеренный код и твой антивирус его не проверит, так как он берется за дело после получение OEP.
    ИМхо сейчас важнее не сигнатурный сканер а ХИПС(Эвристик+Проактивка+Доверительные политики).
    Сигнатурный поиск отмирает...

    ПС.
    1) после нахождения OEP стоит еще раз проверить энтропию(и попытаться найти сигнатуру еще одного OEP) - файл может быть упакован несколько раз.
    2)Базу сигнатру точек входа можно взять из файла userdb.txt (В комплекте с PeId) или из PeSniff
    3) В сканере стоит прикрутить дизасемблер длин, и добавить фильтр мусорных инструкций (даже без крутого пакера имея исходный код, твой антивирус промолчит, если встретит вирусную сигнатуру разбавленную мусором)
    4) Перехвати хотябы CreateProcess для реализации монитора...

    ПС2 - это не притензии - это рекомендации :) Короче если тебе интересно доделать это до какого-то осмысленного конца - стучи в аську :)
     
    #2 spider-intruder, 18 Feb 2009
    Last edited: 18 Feb 2009
    1 person likes this.
  3. Spider Agent

    Spider Agent Elder - Старейшина

    Joined:
    22 Feb 2007
    Messages:
    6
    Likes Received:
    19
    Reputations:
    0
    собсно, афтор статья не я :) но я ему уже передал))
     
    1 person likes this.
  4. regnet

    regnet Active Member

    Joined:
    12 Mar 2009
    Messages:
    27
    Likes Received:
    106
    Reputations:
    -5
    Наверное на построение уйма времени уходит!?
     
    2 people like this.
  5. erolom

    erolom New Member

    Joined:
    14 Mar 2009
    Messages:
    0
    Likes Received:
    4
    Reputations:
    1
    кто автор статьи?норм так написана
     
    #5 erolom, 9 Apr 2009
    Last edited: 9 Apr 2009
    1 person likes this.
  6. IIAHbI4

    IIAHbI4 Banned

    Joined:
    24 Aug 2006
    Messages:
    276
    Likes Received:
    331
    Reputations:
    11
    я ещё года 3 назад предлагал свой (античат) антивирус написать, но не актуально, есть прекрасные антивирусы.
    Зачем велосипёд изобретать?

    против ТС не выступаю, но статья хороша только в теории изучения работы антивируса, применение её на практике, себя не оправдает.
     
    2 people like this.