46297 (751199), страница 4
Текст из файла (страница 4)
Эвристическая маска - набор действий, выявленных при проверке
файла.
Эвристическое число - порядковый номер первой из совпавших эв-
ристических масок.
События распознаются при помощи подпрограмм выявления событий,
в которых могут использоваться также таблицы с данными. Остальные
данные просто хранятся в массивах и не анализируются. Рассмотрим
функциональную схему эвристического анализатора (рис. 6.1.).
Эмулятор кода работает в режиме просмотра, то есть его основная зада-
ча - не эмулировать код, а выявлять в нем всевозможные события. Со-
бытия сохраняются в таблице событий по алгоритму:
if (Events[EventNumber]==0) Events[EventNumber]=++CountEvents;
где: Events - массив событий;
EventNumber - номер регистрируемого события;
CountEvents - порядковый номер зарегистрированного события.
Таким образом, в ячейку массива Events записывается порядковый но-
мер для выявленного события. CountEvents при инициализации равен 0.
После того, как эмулятор завершит свою работу, последовательно запус-
каются два преобразователя. Первый преобразователь заполняет массив
действия, выбирая данные из массива событий и цепочек связных
и несвязных событий по следующему алгоритму:
for(i=0;i
for(j=2;j
"
else if(Events[MaskEvents[i][j]]==0 II Events[MaskEvents[i][j]] goto nextMask; else e=Events[MaskEvents[i][j]]; } nextMask:; } где: CountMaskEvents - число масок цепочек событий; MaskEvents - двумерный массив цепочек связных и несвязных Actions - массив действия. Затем выполняется второй преобразователь, который выбирает дан- for(i=0;i if(Actions[MaskHeurist[i][j]]==0) goto nextMaskI; NumberHeurist=i+1; break; nextMaskI: } где: CountMaskHeurist - число эвристических масок; MaskHeurist - двумерный массив с эвристическими масками; NumberHeurist - эвристическое число. Блокировщик вируса Рассмотрим пример. В дисплейном классе ВУЗа эпидемия, часть машин очередь для обслуживающих класс сотрудников). Ситуация усугубляет- Выход - написать антивирус-блокировщик. Практически все резидентные Разумеется, надо попытаться запустить блокировщик раньше всех ос- install c:\util\stopsvc.com Но если вирус успел заразить command.com или стартует из загрузочно- Листинг программы, блокирующей распространение вируса SVC-1740: ;; Резидентный блокировщик вируса SVC-1740 assume cs:cseg, ds:cseg, ss:cseg org 100h Переходим к инициализации программы jmp Install ;0бработчик прерывания INT 21 h . [Проверим номер функции, если 83h - cmp ah, 83h jnz Skip21 ;0тветим, что вирус присутствует ;3апускаем оригинальный обработчик прерывания db OEAh ;Код команды JMP Инициализируем программу .Проверим, не инсталлирована ли уже эта программа. Если mov ah,83h int 21 h cmp dx, 1990h jz Already .Считаем оригинальный вектор прерывания INT 21 h .Установим наш вектор прерывания INT 21h [Выведем сообщение об успешной инсталляции программы в памяти mov dx, offset OkMes .Выйдем из программы, оставив обработчик резидентным ;Выведем сообщение о том, что вирус mov ah, 9 mov dx, offset BadMes int 21 h .Сообщения программы OkMes db "Yeah! STOPSVC installed now!",13,10 db "(c) KostyaSoft, Samara 1997$" Пример антивируса Итак, нужно написать некую программу, которая будет сканировать ка- Важный момент - поиск и лечение должны производиться после заг- В качестве языка программирования выбран С. Приоритетным призна- Основу программы составляет алгоритм обхода дерева каталогов и по- В тот момент, когда обнаружен очередной потенциально зараженный В случае положительного результата на заражение вызывается функция Если требуется написать программу для лечения для какого-либо другого Итак, как же узнать, заражена программа или нет? В прошлых главах В основе общепризнанного метода лежит принцип выделения сигнату- Разумеется, неправильно было бы использовать для детектирования Вообще говоря, сигнатура - это множество N пар , i=l.N, где Какой должна быть длина сигнатуры? Вообще говоря, чем больше - Итак, в качестве сигнатуры вируса SVC-1740 выберем 6 байт вируса, Итак, сигнатура OB4h 83h OCDh 21h 5Eh 56h длиной б байт расположена Теперь рассмотрим вопрос лечения программы. Фрагменты заражен- Напомним, что вирус SVC-1740, заражая программу, дописывается в ее Но с методической точки зрения, следуя стратегии заражения, необхо- Поэтому для функции cure() предусмотрен именно второй алгоритм Итак, для СОМ-файла считываем 3 байта, с 80-го по 78-й, если считать Таблица 6.1. Таблица перемещений для ЕХЕ-файла Источник, отсчет от Приемник, отсчет от 78 2 76 4 66 14 64 16 60 20 58 22 Демонстрационный антивирус-фаг для вируса SVC-1740. "**"***************"**"**"*"*******"*"*"**^ #include ^include ^include ^include ^include ^include ^include #include ^define F_FOUND 0 #define PATH_LEN 128 #define BAD 1 #define DBG /* Строка имени текущего подкаталога */ /* Строка имени начального места расположения */ /* Строка имени требуемого устройства 7 /* Пустая строка */ int /* Количество отсканированных каталогов 7 /* Количество исследованных файлов 7 /* Количество больных и исцеленных файлов 7 int /* Длина имени файла */ /* Временный индекс */ ' 1 ^include "antilib.c" /* Рекурсивная процедура обхода дерева каталогов */ { struct find_t buf; /* Поиск каталогов */ while (found_d =- F_FOUND) { if ((buf.name[0] != ".") && (buf.attrib & _A_SUBDIR )) { walk(); chdir(".."); } /* К этому моменту не отсканированных нижележащих каталогов n_dir++; getcwd( path, PATH_LEN ); /* Поиск файлов */ while (foundJ == F_FOUND) { if (((buf.name[l-3]=="C")&& (buf.name[l-1]=="M"))ll ( printf("%c%s",13,blank); printf("%c%s\\%s ",13,path,buf.name); /* Нашли новый файл - надо проверить, инфицирован ли он. if (infected(buf.name)==BAD) cure(buf.name); } } main( int argc, char *argv[] ) { if (argc < 2) if(((toupper(argv[1][0]))>"Z")ll((toupper(argv[1][0]))<"A")) drive[0]=argv[1][0]; drive[1 ]=":"; drive[3]="\0'; for (i=0;i n_dir=0; n_fil=0; getcwd(old_path, PATHJ-EN); drive[2]="\0"; system(drive); drive[2]=^\"; chdir(drive); /* Запускаем рекурсивный обход дерева каталогов old_path[2]="0"; system(old_path); old_path[2]='\\"; chdir(old_path); printf("\nKaTanoroB : %с1\пфайлов : %Ь\пОбнаружено больных if (nJII) exit(1); else exit(O); Файл "ANTILIB.C", включаемый в предыдущий: Процедуры обнаружения и лечения /* Сигнатура */ (char) 0х83, (char) OxCD, (char) 0х21, (char) Ox5E, (char) 0х56, "\0"); int infected( char *fn ) I int r,q; char buf[7]; /* Буфер под сигнатуру */ /* Открываем файл */ if (r) { printf(" - ошибка открытия!"); return GOOD; } /* Читаем 6 байт */ r=_dos_read( f, buf, 6, &q ); buf[6]="\0"; if ((r)ll(q!=6)) {printf(" - ошибка чтения!"); _dos_close(f); return GOOD; /* Закрываем файл */ /* Сравниваем байты с сигнатурой 7 ( printf(" - был болен и..."); n_ill++; return BAD; } /* Болен !!! */ /* Годен к в/службе. П/пк мед. службы Орлов :-) */ cure( char *fn ) i int mz; int r,q; char buf[24]; /* Буфер под байты */ /* Открываем файл */ if (r) { printf(" - ошибка открытия!"); return; } /* Читаем первые два байта для определения типа программы */ if ((r)ll(q!=2)) {printf(" - ошибка чтения!"); _dos_close(f); return; } /* Читаем сохраненные вирусом 24 байта старого начала */ r=_dos_read( f, buf, 24, &q ); if ((r)ll(q!=24)) (printf(" - ошибка чтения!"); _dos_close(f); return; } /* Определяем тип программы 7 /* Пишем правильные PartPag и PageCnt 7 r=_dos_write( f, &buf[2], 4, &q ); if ((r)ll(q!=4)) {printf(" - ошибка записи!"); _dos_close(f); return; } /* Пишем правильные ReloSS и ExeSP 7 r=_dos_write( f, &buf[14], 4, &q ); if ((r)ll(q!=4)) {printf(" - ошибка записи!"); _dos_close(f); return; } /* Пишем правильные ReloCS и ExelP */ r=_dos_write( f, &buf[20], 4, &q ); if ((r)ll(q!=4)) {printf(" - ошибка записи!"); _dos_close(f); return; } ) else /* Восстанавливаем сохраненные З первые байта программы */ r=_dos_write( f, &buf[0], 3, &q ); if ((r)ll(q!=3)) {printf(" - ошибка записи!"); _dos_close(f); return; } /* Усекаем файл (переходим на начало вируса r=_dos_write( f, buf, 0, &q); /* Закрываем файл 7 printf("Tenepb исцелен !\п"); return; }
for(e=0,j=2;j
Actions[i]=1;
событий;
ные из массива действия и цепочек эвристических масок и вычис-
ляет эвристическое число по следующему алгоритму:
заражена неизвестным вирусом. До конца сессии - несколько дней,
выключение машин из учебного процесса смерти подобно (в первую
ся тем, что студенты постоянно переносят программы на дискетах с од-
ной машины на другую. Как ограничить распространение эпидемии,
пока вирус не уничтожен?
вирусы определяют факт своего наличия в памяти машины, вызывая ка-
кое-либо программное прерывание с "хитрыми" параметрами. Если напи-
сать простую резидентную программу, которая будет имитировать нали-
чие вируса в памяти компьютера, правильно "отзываясь на пароль",
то вирус, скорее всего, сочтет эту машину уже зараженной. Даже если не-
которые файлы на машине содержат в себе код вируса, в случае исполь-
зования блокировщика заражения всех остальных файлов не произойдет.
тальных программ, например, в файле config.sys:
го сектора, то антивирус-блокировщик не поможет.
;; (с) К. Климентьев, Самара 1997
cseg segment
Start:
lnt21:
;то это запрос присутствия вируса
mov dx, 1990h
Skip21:
Ofs21 dw ?
Seg21 dw ?
Install:
инсталлирована, выведем сообщение об этом и выйдем из программы.
;Вторую копию программы инсталлировать не имеет смысла
mov ax,3521h
int 21h
mov Ofs21, bx
mov Seg21, es
mov ax, 2521h
mov dx.offset lnt21
int 21h
mov ah, 9
int 21h
mov dx, offset Install
int 27h
;или наша программа уже в памяти
Already:
ret
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).
Pi - расположение i-го байта, Bi - значение i-го байта. Но на практике
часто используют непрерывные сигнатуры, для которых важно опреде-
лить только место расположения первого байта и длину сигнатуры.
тем лучше, в идеале в сигнатуру должна входить вся неизменяемая
часть вируса, что гарантирует однозначность распознавания. Но это не-
вероятно увеличит объем антивируса (а известные программы лечат
тысячи вирусов) и замедлит процесс распознавания. Таким образом,
целесообразным следует считать количество от нескольких байт до не-
скольких десятков байт - не больше. Остановимся на цифре 6.
которые размещены начиная с 1724-го байта, если считать от конца за-
раженного файла (с 16-го байта вируса). Вполне возможно, что эти
6 байт совпадают для всех вирусов семейства SVC. Но вероятность
того, что машина сразу заражена несколькими вирусами одного семей-
ства, крайне мала. А вот выбор в качестве сигнатуры шести первых байт
вируса был бы точно ошибочным, потому что, как уже говорилось
выше, подобное начало характерно для очень большого числа вирусов.
начиная с 1724-го байта, если считать от конца зараженной программы.
ной программы, которые необходимо восстановить для излечения, оп-
ределены ранее.
конец, сохраняя в своем теле первые 24 байта оригинальной программы.
Поэтому для излечения как ЕХЕ, так и СОМ-программ, вполне доста-
точно переписать сохраненные 24 байта в начало программы без учета
того, что большая их часть не была изменена, и отсечь 1740 вирусных
байт в конце зараженной программы.
димо в СОМ-программе восстановить только первые три байта,
а в ЕХЕ-програмее - 6 ранее измененных слов заголовка.
лечения, хотя он более медленный и сложный.
от конца файла, и переписываем их в начало файла, для ЕХЕ-файла -
перемещаем 6 слов согласно таблице 6.1. и отсекаем последние
1740 байт.
конца файла
начала файла
^include
^define DRIVE_LEN 4
ftdefine BLANK_LEN 80
^define GOOD 0
char
path[PATH_LEN],
old_path[PATH_LEN],
drive[DRIVE_LEN],
blank[BLANK_LEN];
n_dir,
nJil,
n_ill;
I,
walk()
int found_d, found_f;
found_d=_dos_findfirst("*.*",_A_SUBDIR ,&buf);
chdir(buf.name);
found_d=_dos_findnext( &buf );
больше не осталось - сканируем файлы */
^иг^^оз.Лг^ЛгзЦ^.^^А.МОРМА!- ,&buf);
1=strlen( buf.name );
(buf.name[l-2]=="0")&&
((buf.name[l-3]=="E")&&
(buf.name[l-2]=="X")&&
(buf.name[l-1]=="E")))
n_fil++;
Если заражен, то лечим 7
found_f=_dos_findnext( &buf );
}
puts("ANTISVC - демонстрационный антивирус-фаг");
{ р1Л8("Введите имя диска в качестве параметра"); exit(2); }
{ puts("HeBepHO задано имя диска"); exit(3); }
для выбранного диска 7
walk();
и излечено: %d", n_dir, n_fil, n_ill);
char sign[7]={ (char) OxB4,
int f;
r=_dos_open( fn, 0_RDONLY, &f );
lseek( f, -1724, SEEK_END );
_dos_close(f);
if (strcmp( buf, sign)==0)
return GOOD;
int f;
r=_dos_open( fn, 0_RDWR, &f );
r=_dos_read( f, &mz, 2, &q );
lseek( f, -80, SEEK_END );
if ((mz==Ox4D5A)ll(mz==Ox5A4D))
{ /* Это ехе 7
lseek( f, 2, SEEK_SET );
lseek( f, 14, SEEK_SET );
lseek( f, 20, SEEK_SET );
( /* Это corn */
lseek( f, О, SEEK.SET);
и записываем 0 байт) */
lseek( f, -1740, SEEK_END);
_dos_close(f);