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

Привет
Сигнатура будет состоять из:
- Смещения последовательности в файле
- Размера последовательности
- Хэша последовательности
[Offset * 4 ]
[Lenght * 4 ]
[Hash * 16 ]
Запись антивирусной базы
Запись будет содержать:
- Сигнатуру
- Размер имени файла
- Имя файла
[Signature]
[NameLen * 1 ]
[Name ... ]
После раскрытия структуры сигнатуры получается вот такая запись:
[Sign * 3 ]
[RecordCount * 4 ]
[Records]
Переходим к написанию кода.
Реализация
Базовые структуры
Листинг : Базовые структуры
--------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------
Класс работы с файлом базы
--------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------
Листинг : Реализация CAVBFile
----------------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------------
Здесь всё просто и в комментариях не нуждается.
Теперь реализуем функции класса для записи файла
Листинг : Реализация CAVBFileWriter
----------------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------------
Класс чтения записей немного проще.
Листинг : Реализация CAVBFileReader
--------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------
Реализация программы для создания базы
1. Открыть файл зловреда
2. Перейти по указанному смещению
3. Расчитать MD5-хэш последовательности байт
4. Добавить запись в базу
---------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------------
1. Загрузка файла базы
2. Получение списка файлов в указанной папке
3. Если это файл - проверяем. Если папка - рекурсивно переходим к пункту 2.
Листинг : Заголовок
----------------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------------
Рассмотрим её подробнее.
Надеюсь будет полезна)
Что такое сигнатураВ статье рассматривается процесс написания простого антивирусного сканера.
Задача статьи состоит в объяснении базовых принципов работы антивирусных программ, использующих сигнатуры для обнаружения вредоносного кода.
Помимо самого сканера мы также напишем программку для создания базы сигнатур.
В силу простоты алгоритма проверки наш сканер сможет обнаруживать только вредоносные программы, распространяющиеся цельным файлом, т.е. не заражающие другие файлы, как 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]
Переходим к написанию кода.
Реализация
Базовые структуры
Все структуры будут находиться в заголовочном файле avrecord.hСтруктур не много, всего 2.
Данные структуры будут использоваться как сканером, так и программой создания антивирусной базы.
Во-первых, необходимо объявить все нужные нам структуры.
Первой структурой будет структура сигнатуры SAVSignature.
Следующей структурой будет структура записи SAVRecord, объединяющая сигнатуру с именем.
Данная структура для удобства также содержит функцию выделения памяти под имя зловреда (allocName).
Листинг : Базовые структуры
--------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------
Класс работы с файлом базы
Листинг : Объявления классов работы с файлом базыТеперь необходимо написать класс для работы с файлом антивирусной базы.
Если точнее, то классов будет несколько:
- Базовый класс файла "CAVBFile"
- Класс чтения файла "CAVBFileReader"
- Класс добавления записи "CAVBFileWriter"
Объявления всех этих классов находятся в файле CAVBFile.h
Вот его содержимое:
--------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------
Листинг : Функция проверки существования файлаТеперь перейдем к реализации объявленных классов.
Их реализация будет находиться в файле AVBFile.cpp
Естественно, помним, что необходимо подключить заголовочный файл AVBFile.h
В некоторых функциях нам понадобится проверка существования файла, поэтому сначала напишем именно её.
--------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------
Переходим к реализации функций базового класса.Данный способ проверки существования файла является самым быстрым и используется в большинстве примеров в MSDN, так что его можно считать стандартом для Windows.
Функция GetFileAttributes возвращает атрибуты файла или 0xffffffff в случае, если файл не найден.
Листинг : Реализация CAVBFile
----------------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------------
Здесь всё просто и в комментариях не нуждается.
Теперь реализуем функции класса для записи файла
Листинг : Реализация CAVBFileWriter
----------------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------------
При открытии файла, если файл не найден, создается новый файл и в него записывается заголовок файла (сигнатура и число записей).
Если же файл существует, то происходит проверка сигнатуры файла и чтение числа записей.
Функция addRecord в качестве параметра принимает ссылку на структуру добавляемой записи.
Сначала происходит перемещение в конец файла (новый записи дописываются в конец файла).
Затем происходит запись данных в файл согласно оговорённому выше формату.
После записи происходит увеличение счётчика записей.
Класс чтения записей немного проще.
Листинг : Реализация CAVBFileReader
--------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------
Пора переходить к реализации программы создания записей и сканера.В данном случае если при попытке открытия файла выясняется, что файл не существует, функция вернет значение false, свидетельствующее об ошибке.
Чтение записей происходит последовательно и обеспечивается функцией readNextRecord, которая в качестве параметра принимает ссылку на структуру записи, в которую будут прочитаны данные из файла.
На этом написание общего кода закончено.
Реализация программы для создания базы
Алгоритм работы программы следующий:Как было выяснено выше, сканнер без сигнатур не имеет смысла. Именно поэтому первым делом будет реализована программа для создания базы.
В качестве параметров программа будет принимать путь до файла зловреда, путь до файла базы, смещение последовательности в файле зловреда, размер последовательности и, наконец, имя зловреда.
Аргументы передаются формате -A[Value], где A – это соответствующий ключ, а Value – значение.
Обозначим все аргументы:
-s = Путь до файла зловреда
-d = Путь до файла базы
-o = Смещение последовательности
-l = Размер последовательности
-n = Имя файла
1. Открыть файл зловреда
2. Перейти по указанному смещению
3. Расчитать MD5-хэш последовательности байт
4. Добавить запись в базу
Листинг : ЗаголовокРеализация алгоритма здесь приводится не будет, т.к. не относится к теме статьи, но её можно найти в файле md5hash.cpp
Здесь же мы просто объявим соответствующую функцию getMD5, которая принимает указатель на данные, их размер и указатель на буфер из 16 байт, куда будет записан хэш.
Код программы находится в файле avrec.cpp
Сначала подключим все необходимые заголовочные файлы и объявим функцию подсчёта MD5, а также напишем вспомогательную функцию, которая понадобится при разборе аргументов программы.
---------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------
Листинг : Добавление записи в базуСначала открываем файл, затем переходим к указанному смещению и вычисляем хэш.
Всё просто.
И наконец, добавляем запись в файл базы, попутно выводя информацию в консоль.
Для добавления используется класс CAVBFileWriter.
----------------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------------
Реализация сканераНа этом всё, программа готова. Можно компилировать :)
А пока она компилируется, переходим к написанию самого сканера!
Алгоритм работы следующий:Наконец-то добрались и до главной цели - сканера.
Сканер пока будет просто проверять является ли файл вредоносным, или нет.
Лечение, удаление, карантин оставим на потом.
Файл с базой должен находиться в одной папке со сканером и называться avbase.avb
Программа принимает один-единственный параметр - путь до папки, в которой необходимо провести проверку.
Кода в сканере будет немного больше, но в целом всё так же просто.
1. Загрузка файла базы
2. Получение списка файлов в указанной папке
3. Если это файл - проверяем. Если папка - рекурсивно переходим к пункту 2.
А теперь ближе к коду :)Загрузка файла базы будет происходить в специальную структуру SAVRecordCollection, которую мы объявим, несмотря на то, что можно было использовать стандартный vector или другой контейнер.
Проверка файла сводится к простому перебору всех сигнатур.
Если сигнатура присутствует, то сообщаем, что файл злой, в противном случае сообщаем, что всё хорошо.
Листинг : Заголовок
----------------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------------
Листинг : Разбор аргументовфункция processPath будет рассмотрена ниже.
Итак, вначале стандартный разбор аргументов, а также получение пути для
---------------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------------
Листинг : Функция проверки папкиОткрываем файл, выделяем память под записи, после чего читаем в них информацию из файла.
Если всё прошло хорошо, то будет вызвана функция processPath, которая выполняет рекурсивную проверку по указанному пути.
Вот так выглядит эта функция:
----------------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------------
Листинг : Функция проверки файлаПолучаем список файлов и папок (за исключением папок "." и ".."), при этом если нам попалась папка, то проводим рекурсивный просмотр, а если файл, проверяем его функцией checkFile.
Ниже приведён листинг функции checkFile
----------------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------------
Рассмотрим её подробнее.
Осталось только скомпилировать и протестировать.Во-первых, в функции вместо чтения файла использовано отображение файла в память, при котором файл помещается в адресное пространство процесса, и для доступа к файлу не требует производить операции чтения или записи. Доступ осуществляется, как к обычному массиву.
Данный подход выбран по той причине, что при проверке сигнатур требуется постоянно перемещаться по файлу согласно смещению сигнатуры.
Перемещение по массиву намного быстрее перемещения по файлу. Также для расчёта хэша достаточно просто передать указатель на начало последовательности.
При стандартном подходе потребовалось бы каждый раз считывать информацию из файла, что не только медленно, но и просто неудобно.
Функция MapViewOfFile возвращает адрес, начиная с которого отображен файл.
Этот адрес и является началом файла, или если представить файл как массив, то данный адрес будет началом массива.
Поиск сигнатуры выполняется следующим образом:
В цикле перебираются все записи из коллекции.
Если для записи сумма смещения сигнатуры и её размера меньше, чем размер файла (т.е. сигнатура помещается в файл), то производится хэширование последовательности данных.
После этого производится сравнение полученного хэша с хэшем из сигнатуры.
Если они совпадают, то это значит, что файл известен как опасный (или ложное срабатывание =) )
Надеюсь будет полезна)