V-zlom.ru » Создание Вируса » Пишем свой антивирус

Пишем свой антивирус

В статье рассматривается процесс написания простого антивирусного сканера.

Задача статьи состоит в объяснении базовых принципов работы антивирусных программ, использующих сигнатуры для обнаружения вредоносного кода.

Помимо самого сканера мы также напишем программку для создания базы сигнатур.

В силу простоты алгоритма проверки наш сканер сможет обнаруживать только вредоносные программы, распространяющиеся цельным файлом, т.е. не заражающие другие файлы, как PE-Вирусы, и не изменяющие свое тело в процессе деятельности, как полиморфные вирусы.
Впрочем, это относится к большинству вирусов, червей и практически ко всем троянам, поэтому написанный нами сканер имеет право на жизнь :)

Что такое сигнатура

Сигнатура в простом представлении является уникальной частью (последовательностью байт) в файле.
Однако эта часть должна максимально однозначно выделять файл среди множества других файлов.
Это значит, что выбранная последовательность должна присутствовать только одном файле, которому она принадлежит, и ни в каких других.

На практике помимо самой последовательности применяются ещё дополнительные параметры, позволяющие максимально однозначно сопоставлять сигнатуру файлу.
Введение дополнительных параметров также направлено на ускорение поиска сигнатуры в файле.
Такими параметрами, например, могут являться размер файла, смещение последовательности байт, тип файла, специальные маски (отражение того, как примерно должен выглядеть файл, чтобы в нём могла содержаться искомая сигнатура) и многие другие.

В нашем сканере в качестве дополнительного параметра мы будем использовать смещение последовательности в файле относительно начала.
Данный метод довольно универсален в том плане, что подходит абсолютно для любых файлов независимо от типа.
Однако у использования смещения есть один очень значимый минус: чтобы "обмануть" сканер, достаточно слегка "передвинуть" последовательность байт в файле, т.е. изменить смещение последовательности (например, перекомпилировав вирус или добавив символ в случае скрипт-вируса).

Для экономии памяти и повышения скорости обнаружения, на практике обычно используется контрольная сумма (хэш) последовательности.
Таким образом перед добавлением сигнатуры в базу считается контрольная сумма выбранного участка файла. Это также помогает не обнаруживать вредоносный код в собственных базах :)

Антивирусная база

Антивирусная база представляет собой один или несколько файлов, содержащих записи о всех известных сканеру вирусах.
Каждая запись обязательно содержит имя вируса (пользователь же должен знать,что за зверь поселился у него в системе), также в записи обязательно присутствует сигнатура, по которой выполняется поиск.
Помимо имени и сигнатуры, запись может содержать некоторую дополнительную информацию, например инструкции по лечению.

Алгоритм работы сканера

Алгоритм работы сканера, использующего сигнатуры, можно свести к нескольким пунктам:
1. Загрузка базы сигнатур
2. Открытие проверяемого файла
3. Поиск сигнатуры в открытом файле
4. Если сигнатура найдена 
- принятие соответствующих мер
5. Если ни одна сигнатура из базы не найдена
- закрытие файла и переход к проверке следующего

Как видите, общий принцип работы сканера весьма прост.

Впрочем, достаточно теории. Переходим к практике.
Все дополнительные моменты будут разобраны в процессе написания сканера.

Подготовка к реализации

Прежде чем начинать писать код, стоит определить все составные части сканера и то, как они будут взаимодействовать.

Итак, для обнаружения вредоносных файлов нам необходим непосредственно сам сканер.
Сканеру для работы необходимы сигнатуры, которые хранятся в антивирусной базе.
База создается и наполняется специальной программой.
В итоге получается следующая зависимость:

Программа создания базы -> База -> Сканер

Однако прежде чем создавать базу, необходимо определиться с её форматом, который в свою очередь зависит от хранимой информации.

Информация сигнатуры

Сигнатура будет состоять из:
- Смещения последовательности в файле
- Размера последовательности
- Хэша последовательности

Для хэширования будем использовать алгоритм MD5.
Каждый MD5-хэш состоит из 16 байт, или 4 двойных слов.
Для хранения смещения и размера последовательности отведём по 4 байта для каждого.

Таким образом сигнатуру можно описать следующей структурой:

[Offset * 4 ]
[Lenght * 4 ]
[Hash * 16 ]

Запись антивирусной базы

Запись будет содержать:
- Сигнатуру
- Размер имени файла
- Имя файла

Под размер имени файла выделим 1 байт. Этого больше чем достаточно, плюс экономия места =)
Имя файла может быть произвольного размера до 255 символов включительно.

Получается следующая структура:

[Signature]
[NameLen * 1 ]
[Name ... ]

После раскрытия структуры сигнатуры получается вот такая запись:

[Offset * 4 ]
[Lenght * 4 ]
[Hash * 16]
[NameLen * 1 ]
[Name ... ]

Именно в таком виде одна за другой в файле будут лежать записи для всех известных сканеру зловредах.

Помимо самих записей в файле базы должен быть заголовок, в котором будет содержаться число записей в базе и сигнатура файла "AVB" (не антивирусная :) ). Назначение сигнатуры – удостоверится, что это именно файл базы.

Таким образом файл базы будет иметь структуру вида:

[Sign * 3 ]
[RecordCount * 4 ]
[Records]

Переходим к написанию кода.

Реализация

Базовые структуры

Структур не много, всего 2.
Данные структуры будут использоваться как сканером, так и программой создания антивирусной базы.
Во-первых, необходимо объявить все нужные нам структуры.

Первой структурой будет структура сигнатуры SAVSignature.
Следующей структурой будет структура записи SAVRecord, объединяющая сигнатуру с именем.
Данная структура для удобства также содержит функцию выделения памяти под имя зловреда (allocName).

Все структуры будут находиться в заголовочном файле avrecord.h

Листинг : Базовые структуры
---------------------------------------------------------------------------------------------------------
#ifndef _AVRECORD_H__INCLUDED_
#define _AVRECORD_H__INCLUDED_
#include 

//! Структура сигнатуры
typedef struct SAVSignature{
SAVSignature(){
this->Offset = 0;
this->Lenght = 0;
memset(this->Hash, 0, sizeof(this->Hash));
}
DWORD Offset; // - Смещение файле
DWORD Hash[4]; // - MD5 хэш
DWORD Lenght; // - Размер данных
} * PSAVSignature;

//! Структура записи о зловреде
typedef struct SAVRecord{
SAVRecord(){
this->Name = NULL;
this->NameLen = 0;
}
~SAVRecord(){
if(this->Name != NULL) this->Name;
}
//! Выделение памяти под имя
void allocName(BYTE NameLen){
if(this->Name == NULL){
this->NameLen = NameLen;
this->Name = new CHAR[this->NameLen + 1];
memset(this->Name, 0, this->NameLen + 1);
}
}
PSTR Name; // - Имя
BYTE NameLen; // - Размер имени
SAVSignature Signature; // - Сигнатура

} * PSAVRecord;

#endif
-------------------------------------------------------------------------------------------------------

Класс работы с файлом базы


Теперь необходимо написать класс для работы с файлом антивирусной базы.
Если точнее, то классов будет несколько:
- Базовый класс файла "CAVBFile"
- Класс чтения файла "CAVBFileReader"
- Класс добавления записи "CAVBFileWriter"

Объявления всех этих классов находятся в файле CAVBFile.h
Вот его содержимое:

Листинг : Объявления классов работы с файлом базы
--------------------------------------------------------------------------------------------------------

#ifndef _AVBFILE_H__INCLUDED_
#define _AVBFILE_H__INCLUDED_
#include 
#include 
#include "avrecord.h"
using namespace std;


/* Формат файла антивирусной базы

[AVB] // - Сигнатура
[RecordCount * 4 ] // - Число записей
[Records ... ]

Record:
[Offset * 4 ] // - Смещение
[Lenght * 4 ] // - Размер
[Hash * 16 ] // - Контрольная сумма
[NameLen * 1 ] // - Размер имени
[Name ... ] // - Имя зловреда

*/


//! Класс Файла антивирусной базы
typedef class CAVBFile{
protected:
fstream hFile; // - Объект потока файла
DWORD RecordCount; // - Число записей
public:
CAVBFile();

//! Закрытие файла
virtual void close();
//! Проверка состояния файла
virtual bool is_open();
//! Получение числа записей
virtual DWORD getRecordCount();
} * PCAVBFile;


//! Класс для записи файла
typedef class CAVBFileWriter : public CAVBFile{
public:
CAVBFileWriter() : CAVBFile(){
}

//! Открытие файла
bool open(PCSTR FileName);
//! Добавление записи в файл 
bool addRecord(PSAVRecord Record);

} * PCAVBFileWriter;

//! Класс для чтения файла
typedef class CAVBFileReader : public CAVBFile{
public:
CAVBFileReader() : CAVBFile(){

}
//! Открытие файла
bool open(PCSTR FileName);
//! Чтение записи
bool readNextRecord(PSAVRecord Record);

} * PCAVBFileReader;


#endif
--------------------------------------------------------------------------------------------------------
Теперь перейдем к реализации объявленных классов.
Их реализация будет находиться в файле AVBFile.cpp
Естественно, помним, что необходимо подключить заголовочный файл AVBFile.h

В некоторых функциях нам понадобится проверка существования файла, поэтому сначала напишем именно её.

Листинг : Функция проверки существования файла
---------------------------------------------------------------------------------------------------------
//! Проверка существования файла
bool isFileExist(PCSTR FileName){
return GetFileAttributesA(FileName) != DWORD(-1);
};
---------------------------------------------------------------------------------------------------------
Данный способ проверки существования файла является самым быстрым и используется в большинстве примеров в MSDN, так что его можно считать стандартом для Windows.
Функция GetFileAttributes возвращает атрибуты файла или 0xffffffff в случае, если файл не найден.

Переходим к реализации функций базового класса.

Листинг : Реализация CAVBFile
----------------------------------------------------------------------------------------------------------

CAVBFile::CAVBFile(){
this->RecordCount = 0;
}
//! Закрытие файла
void CAVBFile::close(){
if(hFile.is_open()) hFile.close();
}
//! Проверка состояния файла
bool CAVBFile::is_open(){
return hFile.is_open();
}
//! Получение числа файлов
DWORD CAVBFile::getRecordCount(){
return this->RecordCount;
}
----------------------------------------------------------------------------------------------------------
Здесь всё просто и в комментариях не нуждается.

Теперь реализуем функции класса для записи файла

Листинг : Реализация CAVBFileWriter
----------------------------------------------------------------------------------------------------------


// 
// - CAVBFileWriter
// 

//! Открытие файла
bool CAVBFileWriter::open(PCSTR FileName){
if(FileName == NULL) return false;
// - Если файл не найден то создаем его прототип
if(!isFileExist(FileName)){
hFile.open(FileName, ios::out | ios::binary);
if(!hFile.is_open()) return false;
hFile.write("AVB", 3); // - Сигнатура файла
hFile.write((PCSTR)&this->RecordCount, sizeof(DWORD)); // - Число записей
// - Иначе открываем и проверяем валидность
}else{
hFile.open(FileName, ios::in | ios::out | ios::binary);
if(!hFile.is_open()) return false;
// - Проверка сигнатуры
CHAR Sign[3];
hFile.read((PSTR)Sign, 3);
if(memcmp(Sign, "AVB", 3)){
hFile.close(); // - Это чужой файл
return false;
}
// - Читаем число записей
hFile.read((PSTR)&this->RecordCount, sizeof(DWORD));
}
return true;
}

bool CAVBFileWriter::addRecord(PSAVRecord Record){
if(Record == NULL || !hFile.is_open()) return false;
// - Перемещаемся в конец файла
hFile.seekp(

  • Автор: makar
  • Комментарии: 0
  • Просмотры: 1123
7

Добавить комментарий

Вы не авторизованы и вам запрещено писать комментарии. Для расширенных возможностей зарегистрируйтесь!