б*
4. Займемся анализом полученного листинга. Поверхностно изучая за-
раженные приманки, видим:
- файлы увеличили свою длину на 1740 байт;
- в их конце явно видны посторонние коды;
- изменилось время создания файлов, точнее, изменилось количе-
ство секунд - оно стало равным 60;
- в начале файла test.coM появилась команда jmp;
- в заголовке файла test.exe изменились значения полей ReloCS,
ExelP, ExeSP, ReloSS, PartPag и PageCnt.
Итак.
а) В начале вирусного кода содержится последовательность команд
вида:
call sub_1
sub_1: pop si
sub si,3
Подобная последовательность символов характерна для очень мно-
гих вирусов. Команда call помещает в стек смещение следующей за
ней команды. Это значение извлекается вирусом при помощи ко-
манды pop si (в то время как обычно это делается командой ret)
и помещается в регистр si. Скорректировав эту величину на длину
команды call (3 байта), вирус получает возможность корректного
обращения к ячейкам памяти относительно кодового сегмента:
mov cs:Data[si], xxxx.
Не случайно DrWeb всегда реагирует на подобные команды в на-
чале программ, выдавая предупреждающее сообщение. Впрочем,
это не является обязательным признаком присутствия вируса. На-
пример, устаревшая пристыковочная защита от несанкционирован-
ного копирования (НСК) "Nota" также пользуется этим приемом.
б) Важным элементом алгоритма вируса является определение на-
личия собственного резидента в ОЗУ. Вызывая прерывание DOS
с "секретной" функцией 83h, вирус ждет реакции системы. "Здо-
ровая" система не среагирует на провокацию, а "больная" поме-
стит в регистр dx число 1990h (год создания вируса?), чем и из-
вестит о наличии вируса в памяти. Вот соответствующий
фрагмент вирусного обработчика прерывания INT 21h:
cmp ah,83h
je loc_9
loc_9:
mov dx,1990h
iret
Наличие такой проверки использует антивирус-фаг во время детекти-
рования вирусного кода в оперативной памяти. Также антивирус-бло-
кировщик может имитировать присутствие вируса в памяти, предот-
вращая его внедрение в программное обеспечение компьютера.
в) В случае отсутствия вирусного обработчика INT 21h в памяти,
вирус пытается установить его и остаться в памяти резидентно.
Алгоритм резидентной записи кода вируса в память основан
на прямой модификации заголовка блока памяти (МСВ). Под-
робное описание этого алгоритма и методов борьбы с вирусами,
использующими подобный метод инсталляции, можно найти
в одном из номеров журнала "Монитор" за 1993 г.
г) Установив свою резидентную копию в ОЗУ (или обнаружив на-
личие такой копии), вирус передает управление оригинальной
программе. Изучение этого момента чрезвычайно важно для ана-
лиза. В процессе заражения (данный фрагмент из листинга уда-
лен) вирус считывает (в data_15) 24 байта начала программы
и анализирует первые два байта из них. В зависимости от содер-
жимого первого слова ("MZ" или нет), вирус выполняет зараже-
ние жертвы либо по СОМ-, либо по ЕХЕ-алгоритму, дописывая
фрагмент памяти со своим кодом к ее концу. Естественно, счи-
танные 24 байта также дописываются в файл-жертву. Поэтому
для определения способа передачи управления оригинальному
коду программы вполне достаточно повторно сравнить сохранен-
ный фрагмент начала с признаком "MZ":
cmp cs:data_15[si],5A4Dh
je lt_Was_EXE
В случае если программа была заражена по СОМ-алгоритму, вирус
просто извлекает первые 3 байта из ячейки памяти по адресу
data_15, копирует их в старое начало оригинального кода (по адре-
су cs:100h) и передает туда управление. Адресу data_15 соответ-
ствует 80-ый (если считать от конца) байт зараженной программы.
В случае если программа была заражена по ЕХЕ-алгоритму, вирус
вычисляет старую точку входа по сохраненным в data_20 и data_21
значениям полей ReloCS и ExelP, восстанавливает расположение
стека по сохраненным в data_18 и data_19 значениям полей ReloSS
и ExeSP и передает управление на ReloCS+ES+10h:ExeIP (ES -
сегмент PSP; ES+lOh - сегмент начала программы; ES+ReloCS+
10h - полный сегмент точки входа). Расположение этих адресов
в зараженном файле (от конца файла):
data_20 - 60
data_21 - 58
data_18 - 66
data_19 - 64
Еще могут пригодиться сохраненные значения полей PartPag
и PageCnt (от конца файла):
data_16+1 - 78
data_16+3 - 76
Для излечения зараженного файла достаточно восстановить изме-
ненные значения ячеек, адреса которых только что вычислили,
и отсечь 1740 вирусных байт от конца файла.
5. Еще несколько особенностей, с которыми иногда можно встретить-
ся при дизассемблировании кода вируса и изучении листинга. Код
вируса может быть зашифрован. В этом случае в начале вирусного
кода должен располагаться расшифровщик. Вообще говоря, рас-
шифровщиков может быть много, но первый всегда существует.
Если расшифровщик меняется от одного зараженного файла к дру-
гому, значит имеем дело с полиморфным вирусом. Вырожденный
случай - зашифровываются только сохраненные в теле вируса бай-
ты. Для СОМ-файла вполне достаточно пошагово пройти расшиф-
ровщик в отладчике, дождаться его завершения и сохранить на вин-
честер расшифрованный код вируса. Полученный файл можно
дизассемблировать. Для ЕХЕ-файла такое не подходит, так как в
памяти после загрузки отсутствует заголовок, и полученный файл
не может быть дизассемблирован именно как ЕХЕ. Вероятно, при-
дется писать специальную программу расшифровки на основе изу-
ченного по листингу алгоритма расшифровщика.
Расшифровщик может быть совмещен с алгоритмами, противодей-
ствующими трассировке кода вируса с использованием отладчиков.
Ознакомиться с ними можно в специальной литературе, посвящен-
ной борьбе с НСК. Авторы вирусов, как правило, редко изобретают
что-то новое и используют широко известные методы.
Эвристические анализаторы кода
Эвристическим анализатором кода называется набор подпрограмм, ана-
лизирующих код исполняемых файлов, памяти или загрузочных секторов
для обнаружения в нем разных типов компьютерных вирусов. Рассмот-
рим универсальную схему такого кодоанализатора. Действуя в соответ-
ствии с этой схемой, кодоанализатор способен максимально эффективно
задействовать всю информацию, собранную для тестируемого объекта.
Основные термины:
Событие - это совокупность кода или вызов определенной функ-
ции операционной системы, направленные на преобразование сис-
темных данных, работу с файлами или часто используемые вирус-
ные конструкции.
Цепочка связных событий - это набор событий, которые должны
быть выявлены в порядке их следования.
Цепочка несвязных событий - это набор событий, которые должны
быть выявлены, но не обязательно в строгом порядке.
Действия - набор цепочек связных или несвязных событий, для ко-
торых выполнены все условия.
Эвристическая маска - набор действий, выявленных при проверке
файла.
Эвристическое число - порядковый номер первой из совпавших эв-
ристических масок.
События распознаются при помощи подпрограмм выявления событий,
в которых могут использоваться также таблицы с данными. Остальные
данные просто хранятся в массивах и не анализируются. Рассмотрим
функциональную схему эвристического анализатора (рис. 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++)