Главная | Обратная связь | Поможем написать вашу работу!
МегаЛекции

Эвристические анализаторы кода

Эвристическим анализатором кода называется набор подпрограмм, ана-
лизирующих код исполняемых файлов, памяти или загрузочных секторов
для обнаружения в нем разных типов компьютерных вирусов. Рассмот-
рим универсальную схему такого кодоанализатора. Действуя в соответ-
ствии с этой схемой, кодоанализатор способен максимально эффективно
задействовать всю информацию, собранную для тестируемого объекта.

Основные термины:

Событие - это совокупность кода или вызов определенной функ-
ции операционной системы, направленные на преобразование сис-
темных данных, работу с файлами или часто используемые вирус-
ные конструкции.

Цепочка связных событий - это набор событий, которые должны
быть выявлены в порядке их следования.

Цепочка несвязных событий - это набор событий, которые должны
быть выявлены, но не обязательно в строгом порядке.
Действия - набор цепочек связных или несвязных событий, для ко-
торых выполнены все условия.

Эвристическая маска - набор действий, выявленных при проверке
файла.

Эвристическое число - порядковый номер первой из совпавших эв-
ристических масок.

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

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

if (Events[EventNumber]==0) Events[EventNumber]=++CountEvents;

где: Events - массив событий;

EventNumber - номер регистрируемого события;

CountEvents - порядковый номер зарегистрированного события.


Таким образом, в ячейку массива Events записывается порядковый но-
мер для выявленного события. CountEvents при инициализации равен 0.
После того, как эмулятор завершит свою работу, последовательно запус-
каются два преобразователя. Первый преобразователь заполняет массив

действия, выбирая данные из массива событий и цепочек связных
и несвязных событий по следующему алгоритму:

for(i=0;i<CountMaskEvrnrs;i++) {
if (MaskEvents[i][0]==0) {

for(j=2;j<MaskEvents[i][1 ];]++)
if(Events[MaskEvents[i][j]]==0) goto nextMask;

"

else
for(e=0,j=2;j<MaskEvents[i][1];j++) {

if(Events[MaskEvents[i][j]]==0 II Events[MaskEvents[i][j]]<e)

goto nextMask;

else e=Events[MaskEvents[i][j]];

}
Actions[i]=1;

nextMask:;

}

где: CountMaskEvents - число масок цепочек событий;

MaskEvents - двумерный массив цепочек связных и несвязных
событий;

Actions - массив действия.

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

for(i=0;i<CountMaskHeurist;i++) {
for(j=1;j<MaskHeurist[i][0];j++)

if(Actions[MaskHeurist[i][j]]==0) goto nextMaskI;

NumberHeurist=i+1;

break;

nextMaskI:

}

где: CountMaskHeurist - число эвристических масок;

MaskHeurist - двумерный массив с эвристическими масками;

NumberHeurist - эвристическое число.

Блокировщик вируса

Рассмотрим пример. В дисплейном классе ВУЗа эпидемия, часть машин
заражена неизвестным вирусом. До конца сессии - несколько дней,
выключение машин из учебного процесса смерти подобно (в первую

очередь для обслуживающих класс сотрудников). Ситуация усугубляет-
ся тем, что студенты постоянно переносят программы на дискетах с од-
ной машины на другую. Как ограничить распространение эпидемии,
пока вирус не уничтожен?

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

Разумеется, надо попытаться запустить блокировщик раньше всех ос-
тальных программ, например, в файле config.sys:

install c:\util\stopsvc.com

Но если вирус успел заразить command.com или стартует из загрузочно-
го сектора, то антивирус-блокировщик не поможет.

Листинг программы, блокирующей распространение вируса SVC-1740:

;; Резидентный блокировщик вируса SVC-1740
;; (с) К. Климентьев, Самара 1997
cseg segment

assume cs:cseg, ds:cseg, ss:cseg

org 100h

Переходим к инициализации программы
Start:

jmp Install

;0бработчик прерывания INT 21 h.
lnt21:

[Проверим номер функции, если 83h -
;то это запрос присутствия вируса

cmp ah, 83h

jnz Skip21

;0тветим, что вирус присутствует
mov dx, 1990h

;3апускаем оригинальный обработчик прерывания
Skip21:

db OEAh;Код команды JMP
Ofs21 dw?
Seg21 dw?

Инициализируем программу
Install:

.Проверим, не инсталлирована ли уже эта программа. Если
инсталлирована, выведем сообщение об этом и выйдем из программы.
;Вторую копию программы инсталлировать не имеет смысла

mov ah,83h

int 21 h

cmp dx, 1990h

jz Already

.Считаем оригинальный вектор прерывания INT 21 h
mov ax,3521h
int 21h
mov Ofs21, bx
mov Seg21, es

.Установим наш вектор прерывания INT 21h
mov ax, 2521h
mov dx.offset lnt21
int 21h

[Выведем сообщение об успешной инсталляции программы в памяти
mov ah, 9

mov dx, offset OkMes
int 21h

.Выйдем из программы, оставив обработчик резидентным
mov dx, offset Install
int 27h

;Выведем сообщение о том, что вирус
;или наша программа уже в памяти
Already:

mov ah, 9

mov dx, offset BadMes

int 21 h
ret

.Сообщения программы

OkMes db "Yeah! STOPSVC installed now!",13,10

db "(c) KostyaSoft, Samara 1997$"
BadMes db 7, "Perhaps, virus is in memory already. Sorry $"
cseg ends

Пример антивируса

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

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

В качестве языка программирования выбран С. Приоритетным призна-
но использование таких библиотечных процедур, форматы которых
идентичны во многих системах программирования. Поэтому, например,
использовалась процедура _dos_findfirst(), а не findfirst(). Программа
была написана и отлаживалась в системе, программирования JPI
TopSpeed С v3.01, а также была проверена на Borland C++ v3.1. Кроме
того, контролировалось наличие, идентичность по функциям и форма-
там вызова использованных библиотечных функций в системах про-
граммирования Microsoft C++ v6.0 и Watcom C++ vlO.0. Но если что-
то и не совпадет, откорректировать программу любому программисту не
составит труда.

Основу программы составляет алгоритм обхода дерева каталогов и по-
иска в них файлов с расширениями "СОМ" и "ЕХЕ".

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

В случае положительного результата на заражение вызывается функция
cure(), которая и выполняет операцию исцеления зараженной программы.

Если требуется написать программу для лечения для какого-либо другого
вируса, достаточно просто изменить содержимое процедур cure() и infectedQ.

Итак, как же узнать, заражена программа или нет? В прошлых главах
это делалось чисто визуально, теперь же требуется определить формаль-
ные признаки зараженности.

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

Разумеется, неправильно было бы использовать для детектирования
файла такие ненадежные признаки, как, например, 60 секунд во време-
ни создания файла. Во-первых, это может быть признаком случайного
изменения (например, при упаковке/распаковке некоторыми архивато-
рами). Во-вторых, слишком многие вирусы используют для самоопозна-
ния одинаковые признаки. Наконец, эти признаки могут принадлежать
совершенно здоровой программе (как в истории с антивирусом antitime
и сигнатурой MsDos).

Вообще говоря, сигнатура - это множество N пар <Pi,Bi>, i=l.N, где
Pi - расположение i-го байта, Bi - значение i-го байта. Но на практике
часто используют непрерывные сигнатуры, для которых важно опреде-
лить только место расположения первого байта и длину сигнатуры.

Какой должна быть длина сигнатуры? Вообще говоря, чем больше -
тем лучше, в идеале в сигнатуру должна входить вся неизменяемая
часть вируса, что гарантирует однозначность распознавания. Но это не-
вероятно увеличит объем антивируса (а известные программы лечат
тысячи вирусов) и замедлит процесс распознавания. Таким образом,
целесообразным следует считать количество от нескольких байт до не-
скольких десятков байт - не больше. Остановимся на цифре 6.

Итак, в качестве сигнатуры вируса SVC-1740 выберем 6 байт вируса,
которые размещены начиная с 1724-го байта, если считать от конца за-
раженного файла (с 16-го байта вируса). Вполне возможно, что эти
6 байт совпадают для всех вирусов семейства SVC. Но вероятность
того, что машина сразу заражена несколькими вирусами одного семей-
ства, крайне мала. А вот выбор в качестве сигнатуры шести первых байт
вируса был бы точно ошибочным, потому что, как уже говорилось
выше, подобное начало характерно для очень большого числа вирусов.

Итак, сигнатура OB4h 83h OCDh 21h 5Eh 56h длиной б байт расположена
начиная с 1724-го байта, если считать от конца зараженной программы.

Теперь рассмотрим вопрос лечения программы. Фрагменты заражен-
ной программы, которые необходимо восстановить для излечения, оп-
ределены ранее.

Напомним, что вирус SVC-1740, заражая программу, дописывается в ее
конец, сохраняя в своем теле первые 24 байта оригинальной программы.
Поэтому для излечения как ЕХЕ, так и СОМ-программ, вполне доста-
точно переписать сохраненные 24 байта в начало программы без учета
того, что большая их часть не была изменена, и отсечь 1740 вирусных
байт в конце зараженной программы.

Но с методической точки зрения, следуя стратегии заражения, необхо-
димо в СОМ-программе восстановить только первые три байта,
а в ЕХЕ-програмее - 6 ранее измененных слов заголовка.

Поэтому для функции cure() предусмотрен именно второй алгоритм
лечения, хотя он более медленный и сложный.

Итак, для СОМ-файла считываем 3 байта, с 80-го по 78-й, если считать
от конца файла, и переписываем их в начало файла, для ЕХЕ-файла -
перемещаем 6 слов согласно таблице 6.1. и отсекаем последние
1740 байт.

Таблица 6.1. Таблица перемещений для ЕХЕ- файла

Источник, отсчет от конца файла Приемник, отсчет от начала файла
78 2
76 4
66 14
64 16
60 20
58 22


Демонстрационный антивирус-фаг

для вируса SVC-1740.

"**"***************"**"**"*"*******"*"*"**^

#include <stdio.h>
^include <dos.h>

^include <dir.h>

^include <str.h>

^include <process.h>

^include <errno.h>

^include <bios.h>

^include <io.h>

#include <fcntl.h>

^define F_FOUND 0

#define PATH_LEN 128
^define DRIVE_LEN 4
ftdefine BLANK_LEN 80

#define BAD 1
^define GOOD 0

#define DBG
char

/* Строка имени текущего подкаталога */
path[PATH_LEN],

/* Строка имени начального места расположения */
old_path[PATH_LEN],

/* Строка имени требуемого устройства 7
drive[DRIVE_LEN],

/* Пустая строка */
blank[BLANK_LEN];

int

/* Количество отсканированных каталогов 7
n_dir,

/* Количество исследованных файлов 7
nJil,

/* Количество больных и исцеленных файлов 7
n_ill;

int

/* Длина имени файла */
I,

/* Временный индекс */

' 1

^include "antilib.c"

/* Рекурсивная процедура обхода дерева каталогов */
walk()

{
int found_d, found_f;

struct find_t buf;

/* Поиск каталогов */
found_d=_dos_findfirst("*.*",_A_SUBDIR,&buf);

while (found_d =- F_FOUND)

{

if ((buf.name[0]!= ".") && (buf.attrib & _A_SUBDIR))

{
chdir(buf.name);

walk();

chdir("..");

}
found_d=_dos_findnext(&buf);

/* К этому моменту не отсканированных нижележащих каталогов
больше не осталось - сканируем файлы */

n_dir++;

getcwd(path, PATH_LEN);

/* Поиск файлов */
^иг^^оз.Лг^ЛгзЦ^.^^А.МОРМА!-,&buf);

while (foundJ == F_FOUND)

{
1=strlen(buf.name);

if (((buf.name[l-3]=="C")&&
(buf.name[l-2]=="0")&&

(buf.name[l-1]=="M"))ll
((buf.name[l-3]=="E")&&
(buf.name[l-2]=="X")&&
(buf.name[l-1]=="E")))

(
n_fil++;

printf("%c%s",13,blank);

printf("%c%s\\%s ",13,path,buf.name);

/* Нашли новый файл - надо проверить, инфицирован ли он.
Если заражен, то лечим 7

if (infected(buf.name)==BAD) cure(buf.name);

}
found_f=_dos_findnext(&buf);

}
}

main(int argc, char *argv[])

{
puts("ANTISVC - демонстрационный антивирус-фаг");

if (argc < 2)
{ р1Л8("Введите имя диска в качестве параметра"); exit(2); }

if(((toupper(argv[1][0]))>"Z")ll((toupper(argv[1][0]))<"A"))
{ puts("HeBepHO задано имя диска"); exit(3); }

drive[0]=argv[1][0]; drive[1 ]=":"; drive[3]="\0';

for (i=0;i<BLANK_LEN;i++) blank[i]=" ";blank[BI_ANK_LEN-1]="\0";

n_dir=0; n_fil=0;

getcwd(old_path, PATHJ-EN);

drive[2]="\0"; system(drive);

drive[2]=^\"; chdir(drive);

/* Запускаем рекурсивный обход дерева каталогов
для выбранного диска 7
walk();

old_path[2]="0"; system(old_path);

old_path[2]='\\"; chdir(old_path);

printf("\nKaTanoroB: %с1\пфайлов: %Ь\пОбнаружено больных
и излечено: %d", n_dir, n_fil, n_ill);

if (nJII) exit(1); else exit(O);

Файл "ANTILIB.C", включаемый в предыдущий:

Процедуры обнаружения и лечения

/* Сигнатура */
char sign[7]={ (char) OxB4,

(char) 0х83,

(char) OxCD,

(char) 0х21,

(char) Ox5E,

(char) 0х56,

"\0");

int infected(char *fn)

I
int f;

int r,q;

char buf[7]; /* Буфер под сигнатуру */

/* Открываем файл */
r=_dos_open(fn, 0_RDONLY, &f);

if (r) { printf(" - ошибка открытия!"); return GOOD; }

/* Читаем 6 байт */
lseek(f, -1724, SEEK_END);

r=_dos_read(f, buf, 6, &q); buf[6]="\0";

if ((r)ll(q!=6)) {printf(" - ошибка чтения!"); _dos_close(f); return GOOD;

/* Закрываем файл */
_dos_close(f);

/* Сравниваем байты с сигнатурой 7
if (strcmp(buf, sign)==0)

(printf(" - был болен и..."); n_ill++; return BAD; } /* Болен!!! */

/* Годен к в/службе. П/пк мед. службы Орлов:-) */
return GOOD;

cure(char *fn)

i
int f;

int mz;

int r,q;

char buf[24]; /* Буфер под байты */

/* Открываем файл */
r=_dos_open(fn, 0_RDWR, &f);

if (r) { printf(" - ошибка открытия!"); return; }

/* Читаем первые два байта для определения типа программы */
r=_dos_read(f, &mz, 2, &q);

if ((r)ll(q!=2)) {printf(" - ошибка чтения!"); _dos_close(f); return; }

/* Читаем сохраненные вирусом 24 байта старого начала */
lseek(f, -80, SEEK_END);

r=_dos_read(f, buf, 24, &q);

if ((r)ll(q!=24)) (printf(" - ошибка чтения!"); _dos_close(f); return; }

/* Определяем тип программы 7
if ((mz==Ox4D5A)ll(mz==Ox5A4D))
{ /* Это ехе 7

/* Пишем правильные PartPag и PageCnt 7
lseek(f, 2, SEEK_SET);

r=_dos_write(f, &buf[2], 4, &q);

if ((r)ll(q!=4)) {printf(" - ошибка записи!"); _dos_close(f); return; }

/* Пишем правильные ReloSS и ExeSP 7
lseek(f, 14, SEEK_SET);

r=_dos_write(f, &buf[14], 4, &q);

if ((r)ll(q!=4)) {printf(" - ошибка записи!"); _dos_close(f); return; }

/* Пишем правильные ReloCS и ExelP */
lseek(f, 20, SEEK_SET);

r=_dos_write(f, &buf[20], 4, &q);

if ((r)ll(q!=4)) {printf(" - ошибка записи!"); _dos_close(f); return; }

)

else
(/* Это corn */

/* Восстанавливаем сохраненные З первые байта программы */
lseek(f, О, SEEK.SET);

r=_dos_write(f, &buf[0], 3, &q);

if ((r)ll(q!=3)) {printf(" - ошибка записи!"); _dos_close(f); return; }

/* Усекаем файл (переходим на начало вируса
и записываем 0 байт) */
lseek(f, -1740, SEEK_END);

r=_dos_write(f, buf, 0, &q);

/* Закрываем файл 7
_dos_close(f);

printf("Tenepb исцелен!\п");

return;

}

 

Поделиться:





Воспользуйтесь поиском по сайту:



©2015 - 2024 megalektsii.ru Все авторские права принадлежат авторам лекционных материалов. Обратная связь с нами...