VX Heavens

Library Collection Sources Engines Constructors Simulators Utilities Links Forum
Minimize
Bookmark

Как пымать виря за хвост

Константин Климентьев
2001

[Вернуться к списку] [Комментарии (0)]

Самара 1996-2001

Скачать текстовую версию книги (zip, кодировка KOI8-R, 88Kb)

Содержание

Введение

...Для человека нет безопасности, кроме той,
которой он сам себя обеспечит.
Г. Гаррисон. Неукротимая планета.

Милостивые государи и государыни! Вашему вниманию предлагается цикл статей под условным названием "Как Пымать Выря За Хвост" (Online-версия, исправленная и дополненная). Здесь "Вырь" (жарг.) - вирус, саморазмножающаяся программа; "пымать" (устар., прост. ) - поймать.

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

Разумеется, материалы адресованы подготовленному читателю. Для понимания текста от читателя требуется, как минимум, умение отличить команду NOP от команды XCHG AX,AX.

А если говорить серьезно, то цикл статей рассчитан на человека, знакомого с системным программированием в среде MS DOS. Поэтому лицам, недостаточно разбирающимся в упомянутой тематике, автор рекомендует, например, следующий минимальный набор литературы.

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

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

Глава 1. Как распознать наличие файлового вируса и изловить его

...Бродил, как лунатик, по коридорам, проверяя отсеки,
заговаривал с любым прохожим, пытаясь на основе чистой
психологии проникнуть в истинную суть его намерений.
С.Павлов. Лунная Радуга.

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

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

Примем следующую вводную, соответствующую достаточно часто встречающейся ситуации:

Стандартная PC (286,386...Pentium), как минимум 1 Мб ОЗУ, как минимум 40 Мб HDD; наличие принтера, звуковой карты, CDD и прочей периферии возможно, но в большинстве случаев - не актуально. Программное обеспечение: шагая в ногу со временем, имеем W95/98, возможно W3.1x, но работаем все равно под DOS, пусть даже и 7.0. Джентельменский набор: NC 3.0-5.0, NU 6.0-8.0, полный DOS, свежие антивирусы: AidsTest и DrWeb, прочая мелочь в лице русификаторов, архиваторов, резидентных примочек и пр.

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

Примем еще одно допущение: наличие заведомо чистой "заклеенной" загрузочной дискеты, содержащей (хотя бы в урезанном виде) вышеупомянутый комплект программ.

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

Итак, что будем делать?

1.1. Самые первые действия при подозрении на вирус

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

Важная информация:

Кем и как используется машина ? Если на нее постоянно таскают мелкие игрушки, гороскопы, пробуют и стирают различные бухгалтерские программы, то вероятность наличия вируса в машине весьма высока. Крупные игрушки, которые, например, с трудом влезают даже в упакованном виде в коробку дискет, как правило, переносятся с машины на машину редко и тщательно проверяются на наличие вирусов. ( Вирус Nigeb на факультет Информатики СГАУ занесли вместе с UFO-1; вирус Burglar живет на многих CD, изобилующих крупными игрушками.

Когда впервые обнаружено наличие глюков? Некоторые вирусы любят приурочивать начало своих проявлений к круглой дате или цифре: 1 мая, 7 ноября, 13 - е число, пятница, пять часов вечера и т.п. (А также: 6 марта, 15 ноября, 11-я минута каждого часа...

Не связано ли оно с первым запуском какой-нибудь программы? Если да, то эта программа - первая в очереди на медкомиссию (если ее еще не стерли.

Не связано ли оно с распаковкой какого-нибудь старого архива и запуском программ из него? Некоторые современные антивирусы (AVP, DrWeb, etc...) умеют заглядывать в архивы наиболее популярных форматов в поисках вирусов. Но изредка ведь еще встречаются .ice, .arc, .zoo, .bsa, .uc2, .ha, .pak, .chz, .eli и пр.! (Если что-то я внес в этот перечень лишнего, то склоняюсь перед авторами упомянутых антивирусов в глубоком пардоне.

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

1.2. Включаем подозрительный компьютер

В присутствии хозяина (хозяйки) включаем компьютер. Внимательно следим за процессом загрузки. Сначала стартует программа POST, записанная в ПЗУ BIOS. Она тестирует память, тестирует и инициализирует прочие компоненты компьютера, завершается коротким одиночным гудком. Если глюки начинаются уже на этом этапе - вирус не виноват. (Теоретически вирус может существовать и в BIOS: говорят, первые вирусы на территорию СССР приехали внутри ПЗУ болгарских Правецов; современные "ПЗУ" часто не являются "постоянными запоминающими устройствами", а представляют собой питающееся от батарейки ОЗУ с возможностью загрузки различных версий BIOS.)

1.3. Анализ подозрительных проявлений

По-прежнему в присутствии хозяина (хозяйки) пытаемся вызвать и пронаблюдать глюки.

Идеально, если вирус (если это действительно вирус), самостоятельно извещает нас о своей сущности, например:

I am VIRUS !!!

Проявления вирусов весьма разнообразны: проигрывание мелодий, отображение посторонних картинок и надписей; некоторые вирусы имитируют аппаратные сбои, заставляя дрожать экран и пр. Подробно ознакомиться с этой темой можно в прилагаемых к антивирусным программам каталогах с описаниями вирусов: для AidsTest он хранится в файле aidsvir.txt, для DrWeb - в файле virlist.web. Лучшим, на взгляд автора, является гипертекстовый каталог avpve, прилагаемый к антивирусному пакету Е.Касперского. В нем можно не только прочитать достаточно подробное описание вируса, но и вживую пронаблюдать его проявления.

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

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

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

Важно: если какая-либо программа подвисает при попытке запуска, существует очень большая вероятность, что именно она в настоящий момент и заражена вирусом. Если компьютер виснет в процессе загрузки (после успешного завершения программы POST), то при помощи пошагового выполнения файлов config.sys и autoexec.bat (клавиша F8 в DOS 6.х) можно легко определить источник подвисания.

1.4. Использование возможностей антивирусов

Не выключая компьютера, запускаем (можно прямо с винчестера) какой-нибудь антивирус. Лучше DrWeb с ключиком /ha.

Вирус (если он есть) может немедленно "сесть" на DrWeb. Тот достаточно надежно детектирует целостность своего кода и в случае чего заорет: я заражен неизвестным вирусом ! Если так и произойдет, то наличие вируса в системе - доказано.

Внимательно смотрим на диагностические сообщения типа: файл такой-то ВОЗМОЖНО заражен вирусом такого-то класса (COM, EXE, TSR, BOOT, MACRO и т.п.).

Подозрения на BOOT-вирус в 99% бывают оправданы. (Контрпример: недавно DrWeb 3.20 ругался на BOOT дискеты, "вылеченной" AidsTest'ом от вируса LzExe.

Большое количество файлов, подозрительных на наличие в них вируса одного класса, также с большой достоверностью указывает на неизвестный вирус. (DrWeb'ы до версии 3.15 активно ругались на *все* стандартные DOC-компоненты из WinWord 2.0.

Кроме того, DrWeb умеет определять наличие в памяти компьютера неизвестных резидентных вирусов и Stealth-вирусов. Ошибки при их определении (в последних версиях антивируса) достаточно редки. (Версия 3.15, не умеющая лечить вирус Kaczor, исправно заподозрила наличие агрессивного резидента в памяти; версия же 3.18, умеющая его лечить, в "заразной" системе вообще ничего не заметила, а детектировала и вылечила вирус лишь при загрузке с чистой дискеты.

При этом имеем в виду, что предупреждения типа "странная дата файла", единичные подозрения на COM- и EXE-вирусы и пр. - врядли могут быть расценены, как бесспорное доказательство наличия вируса.

Наконец, MACRO-вирусы живут исключительно в Windows и никакого негативного влияния на DOS-программы оказать не могут (за исключением того случая, если они что-нибудь потерли в Windows-сеансе.

1.5. Естественные причины компьютерных глюков

Нередко глюки бывают вызваны естественными причинами, никакого отношения к вирусам не имеющими.

1.5.1. Аппаратные сбои компьютера

Исключить эту возможность поможет загрузка с чистой дискеты и запуск ( с нее!) диагностической программы ndiags. Тестируем память, маму, порты и пр. (Иногда достаточным бывает простой внешний осмотр компьютера: однажды причиной вирусоподобных вертикальных зеленых полос на экране оказалось... "кверхногамое воткнутие" кабеля в разъем видеоплаты!)

С другой стороны: летом 1998 г. наконец-то появился первый вирус (Win32.CIH), повреждающий аппаратуру Pentium-компьютеров. Он умеет портить содержимое FLASH-памяти, содержащей процедуры BIOS. Пораженный этим вирусом компьютер просто не загружается ни с винчестера, ни с дискет.

1.5.2. Нарушения в логической структуре диска

Грузимся с чистой дискеты и запускаем (с нее!) ndd. Сначала просто отмечаем наличие ошибок (перекрестных цепочек, потерянных кластеров и пр.). Если ошибок очень много,и подавляющее количество их относится к .СОМ- и .ЕХЕ-файлам , то ни в коем случае не выполняем операцию исправления ошибок: это может быть DIR-подобный вирус, тогда такое "исправление" диска может стать для многих программ фатальным. Если ошибки есть и их относительно немного, рискуем и лечим диск. Вновь загружаемся с винчестера. Глюки пропали?

1.5.3. Конфликты между различными компонентами операционной системы и прикладными программами

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

1.6. Если вирус все-таки есть

Наконец, самый интересный случай. Когда вирус явно не обнаружен, но подозрения на его наличие по-прежнему "имеют место быть". Достаточно подробно эту тему осветил Е. Касперский в своей книжке "Компьютерные вирусы в MS DOS", избранные фрагменты этих материалов можно найти также в гипертекстовом каталоге avpve того же автора.

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

1.6.1. Обнаружение загрузочного вируса.

Загрузимся с чистой дискеты и запустив DiskEditor, заглянем в сектор 0/0/1 винчестера.

Если винт был в свое время распилен при помощи fdisk, то нормальная картина: код занимает приблизительно половину сектора, начинаясь с байтов FAh 33h C0h (вместо 33h иногда может быть 2Bh). Кончаться код должен текстовыми строками типа

Missing operating system.

В конце сектора размещаются внешне разрозненные байты таблицы разделов.

(Вирус OneHalf тоже занимает около половины сектора, но байты в нем другие.)

Обращаем внимание на местоположение активного раздела в таблице разделов. Если операционная система располагается на диске С:, а активен 2,3 или 4-й раздел, то это может означать, что вирус просто изменил точку старта, сам разместившись в начале другого логического диска (заодно посмотрим и там).Но это может свидетельствовать, также, просто о наличии на машине нескольких операционных систем и некоего Boot-менеджера, обеспечивающего выборочную загрузку.

Смотрим также всю нулевую дорожку. Норма, когда она - чистая, т.е. все ее сектора содержат только байт-заполнитель. Наличие мусора, копий сектора 0/0/1 и пр., может свидетельствовать о наличии загрузочного вируса. Впрочем, антивирусы, как правило, при лечении загрузочных вирусов лишь обезглавливают противника (восстанавливают исходное значение сектора 0/0/1), оставляя тело с хвостом и крыльями догнивать на нулевой дорожке.

Смотрим BOOT-сектор MSDOS, он обычно живет в 0/1/1. Внешний вид его для сравнения можно найти как в вышеупомянутой книжке Е. Касперского, так и на любой "здоровой" машине.

Итак, если вирус обнаружен, при помощи того-же DiskEditor'а переписываем в файл зараженный объект: MBR 0/0/1 (а лучше всю нулевую дорожку), BOOT 0/1/1 и пр., ну и, наконец,... отсылаем его вирусологам по одному из нижеследующих адресов:

Ну а копию оставляем себе - для опытов.

1.6.2. Обнаружение файлового вируса

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

Резидентные вирусы могут скрывать изменение длины файла (да и вообще наличие своего кода внутри жертвы), если они написаны по Stealth-технологии. Но при загрузке с "чистой" дискеты все тайное становится явным.

(Некоторые вирусы могут не менять длину заражаемых программ:

В этом случае основной признак зараженности - изменение контрольной суммы байтов файла; легко обнаруживают это изменение антивирусы-инспекторы типа AdInf).

Как крайнюю меру можно рассматривать прямое изучение кода программ, подозрительных на вирус.

Автор, например, считает лучшим программным средством для оперативного изучения кода вирусов программу HackerView (hiew. exe by SEN). Но не забудем нашу вводную: "тачка" чужая, и любимых hiew, td, softice, ida и пр. на ней может просто не оказаться. Зато наверняка есть стандартный отладчик debug.

Загружаем подозрительную на вирус программу (в чистой операционной системе!) в память при помощи команды debug имя_программы.

Команда u позволяет дизассемблировать фрагмент кода, команда d - посмотреть на него, как на 16-ричные данные, команда g адрес запускает программу на выполнение с остановом в указанной точке, команда t обеспечивает пошаговую трассировку кода, команда r отображает текущее содержимое регистров.

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

1.6.3. Обнаружение резидентной части вируса.

Автор отсылает читателей к вышеупомянутой книжке Е. Касперского, где эта тема рассмотрена подробно и всесторонне.

1.7. Ловля вируса "на живца"

Самая интересная, на взгляд автора, глава в 1-й части.

Итак, допустим, что наличие вируса в системе доказано одним из вышепредложенных методов, и определены зараженные вирусом объекты.

Теперь можно начать изучение и, вслед за этим, попытаться удалить вирус с машины. Кроме того, не мешало бы послать образец вируса профессиональным вирусологам.

А для этого необходимо выделить вирус в чистом виде. (Конечно, если Вы обладаете такой возможностью, посылайте вирусологу по электронной почте полный образ зараженного винчестера; Ваш покорный слуга этого позволить себе не может.

1.7.1. Выделение загрузочного вируса.

Как уже указывалось ранее, если вирус заразил винчестер, необходимо при помощи программы DiskEditor сохранить в файле образ зараженного объекта (например, сектора 0/0/1 или всей нулевой дорожки). Но, как известно, загрузочные вирусы только живут в системных областях винчестера, размножаются же они, заражая системные области дискет.

Поэтому внимательно посмотрим на лицевую панель нашего компьютера. Если на ней присутствуют карманы флоповодов обоих типов ( 3.5" и 5.25"), то придется отформатировать 4 дискеты на 4 стандартных формата: 360Кб, 720Кб, 1.2 Мб, 1.44Мб ( а, может, у Вас есть флоповоды на 2.88 Мб? . Затем при помощи программы DiskEditor внимательно рассмотрим и постараемся запомнить внешний вид boot-секторов этих дискет (0/0/1), хотя бы первые байты (естественно, все это делаем на чистой машине !). Вставляем по-очереди (незаклеенные!) дискеты в карманы флоповодов больной машины и (обязательно!) обращаемся к ним: пытаемся прочитать каталог, записать, прочитать и удалить какие-нибудь файлы. Наконец, на чистой машине, при помощи DiskEditor'а вновь бросаем взгляд на сектор 0/0/1. Если он изменился на какой-нибудь дискете, при помощи того же DiskEditor'а снимаем образ всей дискеты в файл. Вирус пойман ! Можно упаковать файл каким-нибудь архиватором и послать его вирусологу.

(Некоторые хитрые вирусы хранят свое тело на дополнительной, специально отформатированной дорожке, т.н. инженерном цилиндре дискеты. В этом случае без пакета копирования ключевых дискет типа fda, teledisk или copymaster не обойтись.

1.7.2. Выделение резидентного вируса.

Как известно, резидентный вирус постоянно сидит в памяти ПЭВМ, выбирая жертвы для заражения. Наиболее часто в качестве жертв выступают запускаемые программы. Однако, могут заражаться открываемые файлы программ; копируемые на дискету или с оной файлы программ (вирус OneHalf; файлы программ, разыскиваемые при помощи DOS-функций FindFirst и FindNext и пр.

Необходимо также подобрать подходящего претендента на "контрольное" заражение - небольшую программу простой структуры, т.н.дрозофилу. Некоторые вирусы пытаются распознать дрозофилу и отказаться от ее заражения: не трогают особо короткие программы; не трогают программы, большая часть которых состоит из повторяющихся байтов (например, 90h - код команды NOP) и пр.

Автор с большим успехом использует в качестве дрозофил программы 5000.COM и 5000.EXE, исходные тексты которых на языке ассемблера прилагаются.

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

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

Согласитесь, гораздо приятней передавать по электронной почте небольшой файлик длиной 5Кб+размер_вируса, чем какой-нибудь clipper.EXE!

1.7.3. Выделение нерезидентного вируса.

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

Одни нерезидентные вирусы заражают только в текущем каталоге, другие вообще во всех каталогах винчестера, третьи - только в подкаталогах 1-го уровня (KAKASHKA), четвертые - в каталогах, указанных в строке path системной среды (Vienna)...

Поэтому воспользуемся программой типа rt (М. Широбоков, Самара), чтобы скопировать дрозофилы во все каталоги диска (запускаем из корневого каталога !):

rt copy a:\5000.* .

Точка "." в конце - символ текущего каталога! Потом дрозофилы можно будет удалить: rt del 5000.*

Теперь выбираем заведомо зараженную программу и... запускаем ее N раз, постоянно изменяя время, дату и пр. Проконтролировать изменение длины поможет та же программа rt:

rt dir 5000.* >5000.txt

Получаем файл 5000.txt, содержащий список файлов 5000.* с указанием их длин. Выбираем тот файл дрозофилы, который изменил длину. Попался, который кусался!

1.8. Далее...

Следующая часть обещает быть гораздо менее занудной и более интересной. Будем вскрывать конкретного выря...

Глава 2. Как исследовать алгоритм работы файлового вируса

Там в стеклянных банках, наполненных какими-то
растворами, пульсировали разные органы.
Отрезанные руки и ноги продолжали жить.
А.Беляев. Человек-амфибия

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

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

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

2.1. Структура COM- и EXE-программ

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

СОМ-программа представляет собой "кусок" кода и данных, начинающийся с исполняемой команды и занимающий не более 64Кб. Например, такую структуру имеет командный процессор СОММАND.СОМ операционной системы MSDOS, версий до 6.22 включительно ( у автора на CD "Ваш Оффис #3" валяется MSDOS 6.3 beta J.

ЕХЕ-программа имеет гораздо более сложную структуру.В начале файла ЕХЕ-программы располагается заголовок, длиной 28 байт следующей структуры:

Смещение Наименование Содержимое
0 Байты 4D5A ('MZ') признак ЕХЕ-файла
2 PartPag Длина файла по модулю 512
4 PageCnt Длина файла в 512-байтовых страницах
6 ReloCnt Размер настроечной таблицы
8 HdrSize Размер заголовка в 16-байтниках
10 MinMem Минимум требуемой памяти
12 MaxMem Максимум требуемой памяти
14 ReloSS Относительный сегмент стека
16 ExeSP Смещение указателя стека
18 ChkSum Контрольная сумма файла
20 ExeIP Смещение точки входа
22 ReloCS Относительный сегмент точки входа
24 TablOff Смещение настроечной таблицы
26 Overlay Номер оверлейного сегмента

Поля ReloCS и ExeIP определяют местоположение точки входа в программу, поля ExeSP и ReloSS - местоположение стека, поля PartPag и PageCnt - размер корневого сегмента программы.

Вычисленный по PartPag и PageCnt размер программы может не совпадать с реальным размером файла. Такие программы называются "сегментированными" или "содержащими внутренние оверлеи". Грамотные авторы вирусов избегают заражать такие программы.

После заголовка может располагаться специальная таблица, точное местоположение которой определяется полем TablOff, а размер - полем ReloCnt. В этой таблице хранятся адреса тех слов в коде программы, которые модифицируются операционной системой во время загрузки программы. Например, просматривая файл программы при помощи утилиты HackerView, мы видим команду: call 0000:1234h; не волнуйтесь, в процессе загрузки программы MSDOS подставит всместо 0 нужный сегментный адрес, и программа будет выполняться правильно J. Кстати, если значение TablOff есть число 40h или большее, то, скорее всего, этопрограмма в формате Windows.

Подобный формат имеет, например, командный процессор COMMAND.COM из Windows 95. Несмотря на свое расширение, он имеет в начале знаменитые буквы 'MZ' и длину 95 Кб.

2.2. Способы заражения программ.

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

Вообще говоря, заражение всех программ возможно по-разному.

2.2.1. Метод приписывания.

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

2.2.2. Метод оттеснения.

Код вируса располагается в начале зараженной программы, а тело самой программы приписывается к концу.

2.2.3. Метод вытеснения.

Из начала (или середины) файла "изымается" фрагмент, равный по объему коду вируса, приписывается к концу. Сам вирус записывается в освободившееся место.

Разновидность метода вытеснения - когда оригинальное начало не сохраняется вообще. Такие программы являются "убитыми насмерть" и не могут быть восстановлены никаким антивирусом.

2.2.4. Прочие методы

Ну и всякая экзотика, типа сохранения вытесненного фрагмента программы в "кластерном хвосте" файла и пр.

2.2.5. "Стандартные" методы заражения

Е. Касперский выделяет следующие способы и называет их "стандартными".

2.2.5.1. Случай COM-программы

Тело вируса приписывается к концу файла, где-то внутри его сохраняются несколько (обычно, три) байтов оригинального начала программы, на их место записываются команды перехода на начало вируса. Когда вирус заканчивает выполнение предусмотренных им действий, он восстанавливает оригинальные байты начала программы (по адресу CS:100h) и передает туда управление.

2.2.5.2. Случай EXE-программ

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

2.3. Исследование конкретного вируса

Этой информации должно хватить для исследования кода какого-нибудь конкретного файлового вируса и разработки алгоритма его лечения.

В качестве жертвы "показательного вскрытия" возьмем широко известный в начале 90-х годов вирус SVC-1740. Выбор определился следующими обстоятельствами:

Вот что говорят по поводу этого вируса вирусологи:

Д.Н.Лозинский:

SVC)

Группа вирусов одного автора, живущего по имеющейся информации в Нижнем Новгороде. Все имеющиеся версии заражают COM и EXE. Содержат близко от конца текст: "(c) 1990 by SVC,Vers. 4.0". Корректируют время создания файла, ставя в нем 60 секунд. Если зараженную программу загружает DEBUG, вирус ее исправляет. При вызове функций просмотра каталога, вирус корректирует в информации о зараженных программах время и длину, скрывая свое наличие в них.

SVC-1689, -1740

Две разновидности одной версии, отличающиеся в одном фрагменте, который, кажется, так и не был отлажен.

Интересно, что некоторые вирусы серии SVC содержат в своих копирайтах упоминание города Свердловска, а совсем не Нижнего Новгорода.

Достоверности ради запустим этот вирус на своей машине и пронаблюдаем за проявлениями:

  1. В MSDOS успели заразиться файлы ARCVIEW.EXE, HIEW.EXE и LEX.EXE. В результате HackerView, умеющий проверять целостность своего кода, отказался работать, сообщив: HIEW bad, work is aborted.
  2. Windows 3.11 и Windows 95 сначала запустились нормально, но затем продемонстрировали разноцветные горизонтальные полосы в видеорежиме 800х600х256 (дело не в том, что вирус заразил какие-нибудь драйвера, а просто в момент старта "форточек" в памяти находился вирусный обработчик Int21h).

Излечение пришло после использования антивирусов:

	DrWeb c: /cup /al
и
	AidsTest c: /f /g /q 

(Запускать антивирус с '*' автор побоялся - коллекцию "залечит").

2.3.1. Дизассемблирование дрозофил

При помощи ранее описанных методов заразим две дрозофилы: 5000.COM и 5000.EXE. Увеличение их длины на 1740 байт заметно только на "чистой" машине (Stealth-эффект).

Вообще говоря, выбор дизассемблеров весьма широк. Широко известна была в свое время программа DisDoc. По признанию Е. Касперского, он активно пользуется интерактивным дизассемблером IDA. Быстро просмотреть код программы позволяет утилита HackerView (hiew). Также возможно использование любого отладчика.

Используем для изучения кода зараженных дрозофил дизассемблер Sourcer v5.04. Впрочем, вполне пригодна для этих целей любая версия программы. Так, излюбленным инструментом автора является версия 1.72. Несмотря на то, что она не содержит многих полезных опций, иногда ошибается при дизассемблировании, зато занимает на дискете (будучи упакована PkLite) всего 48Кб.

Итак, запускаем дизассемблер командой: sr 5000.сом. Получаем на экране темно-синюю лицевую страничку. Нажав клавишу 'a', можно перейти на страничку опций. Автор настоятельно рекомедует установить следующую опцию: 'a' - обязательно дизассемблировать фрагмент программы, располагающийся после команд jmp/ret/iret; это позволяет получить ассемблерный код тех фрагментов программ, в которые нет явного перехода (процедуры обработки прерываний, скрытые подпрограммы и пр.). Вернемся на первую страницу, нажав Enter. Запустим процесс дизассемблирования нажатием клавиши 'g'.

В зависимости от производительности ЦПУ, процесс дизассеблирования длится от нескольких секунд до нескольких минут. (Sourcer 1.72 на 286/16 Мгц, бывалоча, раскручивал программу prince.exe из игрушки Prince Of Persia более часа J. Вообще, для грубой оценки размера листинга можно принять: один килобайт кода дает десять-пятнадцать килобайт текста.

6740 байт зараженной дрозофилы дают нам 96 Кб текста + файл 5000.sdf. Это очень интересный файл, он хранит в текстовом виде как опции, использованные при дизассемблировании, так и параметры полученного текста: размещение фрагментов кода и данных, местоположения символических имен и пр. Если изменить эти параметры на свой вкус, переименовать файл в 5000.def и подсунуть его Sourcer'у в командной строке в качестве параметра, то дизассемблер будет работать в соответствии с новыми инструкциями.

Аналогичную операцию проделаем для файла 5000.ехе.

2.3.2. Анализ листинга

Займемся самым интересным - анализом полученного листинга. Листинг (купированный) файла 5000.сом прилагается.

Поверхностно изучая зараженные дрозофилы, мы видим:

Итак...

2.3.2.1. Начальный фрагмент вирусного кода

В начале вирусного кода содержится последовательность команд вида:

            call sub_1
sub_1:
            pop  si
            sub  si,3

Подобная последовательность характерна для начала очень многих вирусов. Команда call помещает в стек смещение относительно сегмента команд cs следующей за call команды. Вместо естественной команды ret, командой pop si автор вируса извлекает это значение и помещает его в регистр si. Скорректировав эту величину на длину команды call (3 байта), автор вируса получает возможность корректного обращения к ячейкам памяти относительно кодового сегмента примерно так:

	mov cs:Data[si], xxxx

Не случайно DrWeb очень остро реагирует на подобные команды в начале программ, выдавая предупреждающее сообщение. Впрочем, это не является обязательным признаком вируса. Например, устаревшая пристыковочная защита от НСК Нота также пользуется этим приемом.

2.3.2.2. Фрагмент проверки вирусом наличия себя в памяти

Важным элементом алгоритма вируса является этап определения наличия собственного резидента в ОЗУ. Вызывая прерывание DOS с "секретной" функцией 83h, вирус ждет реакции системы. "Здоровая" система не должна реагировать на провокацию, а "больная" обязана поместить в регистр dx число 1990h (год создания вируса ?), чем и извещает о наличии агрессивного кода вируса в памяти.

Соответствующий фрагмент вирусного обработчика прерывания Int21h:

    cmp ah,83h
    je  loc_9
    ...
loc_9:
    mov dx,1990h
    iret

Этот факт может использовать антивирус-фаг для детектирования вирусного кода в оперативной памяти. Также, антивирус- блокировщик может имитировать наличие вируса в памяти, предотвращая его внедрение в программное обеспечение компьютера.

2.3.2.3. Алгоритм резидентной установки вируса в памяти

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

Алгоритм резидентного оставления кода вируса в памяти основан на прямой модификации заголовка блока памяти (MCB). Подробное описание этого алгоритма и методов борьбы с вирусами, использующими подобный метод инсталляции, можно найти в одном из номеров журнала "МОНИТОР" за 1993 г.

2.3.2.4. Алгорим возвращения управления программе-носителю

Установив свою резидентную копию в ОЗУ (или обнаружив состоявшийся факт наличия такой копии), вирус обязан передать управление оригинальной программе. Изучение данного фрагмента чрезвычайно важно для нас.

В процессе заражения (этот фрагмент из листинга удален) вирус считывает (в data_15) 24 байта начала программы и анализирует самое начало. В зависимости от содержимого первого слова ('MZ' или нет), вирус выполняет заражение жертвы либо по СОМ-, либо по ЕХЕ-алгоритму, приписывая фрагмент памяти со своим кодом к ее концу. Естественно, считанные 24 байта также оказываются приписанными к жертве.

Поэтому для определения способа передачи управления оригинальному коду программы вполне достаточно повторно сравнить сохраненный фрагмент начала с признаком 'MZ':

cmp cs:data_15[si],5A4Dh
je  It_Was_EXE
2.3.2.4.1. Возврат в СОМ-программу

В случае, если программа была заражена по СОМ-алгоритму, вирус просто извлекает первые 3 байта из ячейки памяти по адресу data_15, копирует их в старое начало оригинального кода (по адресу cs:100h) и передает туда управление.

Адресу data_15 соответствует 80-й (если считать с конца) байт зараженной программы.

2.3.2.4.2. Воврат в ЕХЕ-программу

В случае, если программа была заражена по ЕХЕ-алгоритму, вирус вычисляет старую точку входа по сохраненным в data_20 и data_21 значениям полей ReloCS и ExeIP, восстанавливает местоположение стека по сохраненным в data_18 и data_19 значениям полей ReloSS и ExeSP и передает управление на ReloCS+ES+10h:ExeIP. (ES - сегмент PSP; ES+10h - сегмент начала программы; 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 вирусных байтов от конца файла. Этой операции будет посвящена Часть 3 статьи "Как Пымыть Выря За Хвост".

2.4. Некоторые особые ситуации при анализе вирусов

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

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

Для случая СОМ-файла вполне достаточно пошагово пройти расшифровщик в отладчике, дождаться его завершения и сохранить на винчестер расшифрованный код вируса (примитивный debug это делать умеет, а навороченный td - нет J . Полученный файл можно дизассемблировать.

В случае с ЕХЕ-файлом такой метод не подходит, так как в памяти после загрузки отсутствует заголовок, и полученный файл не сможет быть дизассемблирован именно как ЕХЕ. Вероятно, придется писать специальную программу расшифровки на основе изученного по листингу алгоритма расшифровщика.(По крайней мере, автор обычно именно так и поступает L). Если есть более простой способ - предложите!

Пример начала зашифрованного вируса Search-1000:

8E86:0100               start:
8E86:0100  E9 01C8          jmp loc_4
......................................
                        loc_4:
8E86:02CB  E8 0000          call sub_2
                        sub_2:
8E86:02CE  5B               pop  bx
8E86:02CF  83 EB 03         sub  bx,3
8E86:02D2  B9 03E1          mov  cx,3E1h
8E86:02D5 ╥BE 0000          mov  si,data_2e
8E86:02D8  80 70 14 9C      xor  byte ptr [bx+si+14h],9Ch
8E86:02DC  46               inc  si
8E86:02DD  E2 F9            loop $-5

Вырожденный случай - когда зашифровываются только сохраненные в теле вируса байты (фрагмент завершения вируса Oops-600):

   
8E86:0146  8B 84 0353 mov  ax,data_12[si]
8E86:014A  F7 D0      not  ax
8E86:014C  A3 0100    mov  word ptr ds:[100h],ax
8E86:014F  8B 84 0355 mov  ax,data_13[si]
8E86:0153  F7 D8      neg  ax
8E86:0155  A3 0102    mov  word ptr ds:[102h],ax
8E86:0158  B8 0100    mov  ax,100h
8E86:015B  50         push ax
8E86:015C  33 C0      xor  ax,ax
8E86:015E  33 F6      xor  si,si
8E86:0160  C3         retn

Расшифровщик может быть совмещен с трюками, противодействующими трассировке кода вируса с использованием отладчиков. Ознакомиться с этими трюками можно в специальной литературе, посвященной борьбе с НСК. Авторы вирусов, как правило, редко изобретают что-нибудь новое и используют широко известные методы (вирус Awme, а еще ранее - Natas):

   
detect_debug    proc    near
        xor     ax,ax
        pushf
        pop     dx
        and     dh,0FEh
        push    dx
        push    dx
        popf
        push    ss
        pop     ss
        pushf
        pop     dx
        test    dh,01
        pop     dx
        je      no_debug
        xor     bp,bp
        mov     cx,ss
        cli
        mov     ss,bp
        les     di,[bp+4]
        mov     ss,cx
        sti
        mov     al,0CFh
        cld
        stosb
        push    dx
        popf
        pop     bx
        jmp     debug
no_debug:
        pop     bx
        cmp     byte ptr [bx],0cch
        jne     nobreak
        jmp     debug
nobreak:
        push    bx
        ret
detect_debug    endp

Но это уже совсем другая тема.

2.5. Прилагающиеся примеры

К тексту статьи прилагаются:

Дрозофилы понадобятся нам в качестве "мишеней" при изучении проблемы написания антивирусов. Но это все будет в 3-й части...

Глава 3. Как избавиться от файлового вируса на своей машине

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

Итак, факт наличия вируса на машине установлен со 100%-ной достоверностью. Более того, вирус пойман, распластан на операционном столе и вскрыт. "Вскрытие показало, что больной умер от вскрытия?" Увы, для того, чтобы избавиться от вируса, необходимо проделать еще ряд действий (что впрочем, также весьма занимательно).

3.1. Обращение к профессиональным вирусологам

Способ первый - безотказный. Упаковываем зараженную дрозофилу каким-нибудь архиватором и посылаем ее по одному из трех адресов, которые можно найти в 1-й части данной работы, а лучше - сразу по всем трем. Не забудем сопроводить данную посылку краткой сопутствующей информацией, типа: вирус обнаружен 31 декабря в 11 часов 59 минут, вероятный источник - гриппующий сосед дядя Вася и пр. J.

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

Итак, первый способ - совершенно безотказный, более того - обязательный, но... медленный и неинтересный.

3.2. Пишем антивирус сами

Способ второй - так поступают настоящие пионе... пардон, программисты J.

3.2.1. Блокировка распространения резидентного вируса

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

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

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

install c:\util\stopsvc.com

Правда, если вирус успел заразить COMMAND.COM или стартует из загрузочного сектора, то антивирус-блокировщик не поможет.

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

3.2.2. Написание собственного сканирующего фага

Пока наше письмо по телефонным линиям спешит к профессиональным вирусологам, попытаемся справиться с вирусом самостоятельно. "Спасение утопающих - дело рук самих утопающих". А кто спорит?

В части 2-й данной работы автор привел результаты вскрытия вируса SVC-1740 и обратил внимание на детали заражения, важные с точки зрения дальнейшего излечения файлов. Разумеется, нам потребуется написать некую программу, которая сканировала бы каталоги указанного диска, искала зараженные файлы и исцеляла бы их.

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

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

3.2.2.1. Общая структура программы

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

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

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

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

3.2.2.2. Детектирование вирусов при помощи сигнатур

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

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

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

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

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

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

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

Итак, сигнатура: 0B4h 83h 0CDh 21h 5Еh 56h длиной 6 байтов лежит начиная с 1724-го байта, если считать от конца зараженной программы.

3.2.2.3. Метод восстановления зараженной программы

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

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

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

Итак, для СОМ-файла:

считываем 3 байта, с 80-го по 78-й, если считать от конца файла, и переписываем их в начало файла.

Для ЕХЕ-файла:

перемещаем 6 слов согласно таблице

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

Для обоих типов файлов:

отсекаем последние 1740 байт.

К тексту прилагаются файл antisvc.c, содержащий основное тело антивируса, а также 3 файла - antilib.c (содержащий процедуры "правильного" лечения), antilib1.c (содержащий процедуры "быстрого"лечения) и shutka.c (ну, в это сами разберетесь J.

Именно так когда-то начинался некогда великий AidsTest...

3.3. Автоматизированные антивирусные средства

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

Как правило, подобного рода антивирусы имеют следующую структуру:

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

3.3.1. Пакет "Доктор Касперский"

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

3.3.2. Антивирус AVSP

Несколько лет назад некоторую известность имел пакет avsp (автор - А. Борисов). Помимо возможностей по поиску и удалению вирусов, обновлению базы данных, пакет содержал также средства блокирования распространения вирусов и средства инспектирования содержимого дисков (аналогично AdInf). База данных хранится в текстовом формате. Посмотрим, например, как выглядит запись в этой базе, соответствующая вирусу SVC-1740 (разработана А. Борисовым):

   
_SVC-1740
NUMB=138.1-44697
TYPE=PROGRAM
SIZE=1740
MASK=83EE032E8984C706065633D2B483
SCAN=*.COM { Opt(End:-1736) Mov(MP:1656,Head:0,24) Tr(End:-1740) }
SCAN=*.EXE { Opt(St:4) Mov(MP:1656,Head:0,24) Tr(End:-1740) }
TEXT=SVC версия 4.0
#_END

Обратим внимание на следующие строки в записи:

MASK=... задает сигнатуру вируса. Примененная в этом примере сигнатура несколько отличается от придуманной нами, она длинней (14 байт) и начинается с 4-го байта вируса. (Кстати, последние 2 ее байта - первые байты нашей сигнатуры).

SCAN=*.COM...правило лечения СОМ-программы. От позиции начала сигнатуры (MP) отсчитывается 1656 байт (начало сохраненных 24-х байтов головы оригинальной программы) и перемещаются в начало программы (Head:0). Вслед за этим от файла отсекается 1740 байт, считая от конца (End).

SCAN=*.EXE...правило лечения ЕХЕ-программ. Аналогично правилу для СОМ-программ.

Т.е. лечение производится по рассмотренному нами ранее "быстрому" алгоритму.

Автору несколько раз приходилось использовать пакет avsp на практике. Пакет несколько староват, но прост и надежен.

3.3.3. Антивирус MultiScan

Сравнительно недавно появился антивирус MultiScan ( автор - В. Колесников, Киев). Этот антивирус также обладает возможностями по обновлению пользователем базы данных, представленной в текстовой форме.

К стыду своему автор вынужден признаться, что лишь с огромным трудом сумел составить правильную запись для поиска и лечения вируса SVC-1740. Пришлось сместить к началу вируса и расширить до 10 байт (так было надо L) сигнатуру:

   
.! - ( признак начала сигнатуры )
Тип, длина и имя вируса.............: c1740 svc
Смещение для сигнатуры в файле......: 0
Длина сигнатуры и сигнатура.........: 10 E8 00 00 5E 83 EE 03 2E 89 84
Количество восстанавливаемых байт...: 24
Смещение между настоящими байтами и началом вируса...: 67C

3.4. Общие понятия об эвристическом анализе

Поговорим немного об эвристическом анализе. (Все нижесказанное - плод авторских размышлений и догадок, оно может истине и не соответствовать J.

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

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

Принцип действия подобных эвристических анализаторов, вероятнее всего, заключается в следующем: антивирус содержит в себе интереснейший механизм эмуляции работы процессора. Антивирус считывает код программы и начинает ее выполнять, но не по-настоящему, а "понарошку" J. Он делает вид, что выполняет процессорные команды, а на самом деле, анализируя их код, видоизменяет соответствующим образом значения своих внутренних образов регистров и ячеек оперативной памяти.

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

Если код программы зашифрован, то антивирус пошагово "раскрутит" его. И не помогут никакие ухищрения с Int1/Int3, обычно затрудняющие работу в "настоящих" отладчиках, ведь все это исполнение - понарошку!

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

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

После окончания работы антивирус "считает фишки", и если их больше чем надо - раздается вопль: вероятен вирус! J.

Конечно, это очень упрощенная схема.Но уже на ней видны основные недостатки современных эвристических анализаторов: а) если гипотетический вирус в процессе своей работы взаимодействует с железом, то эмулятор процессора не сможет этого отловить, это для него - verboten! б) "глубина" эвристического анализа не слишком велика, ибо приходится держать в памяти не только содержимое регистров, но и содержимое (возможно больших) фрагментов ОЗУ, а также крайне сложно пытаться эмулировать работу "толстых" программ, написанных на языках высокого уровня.

Используют эвристический анализ многие современные антивирусы - DrWeb, AVP, MScan и пр. Практика показывает, что наиболее "зорким" эвристическим анализатором обладает DrWeb, но в стремлении обнаружить побольше вирусов допускает много ошибок, в том числе и очень опасных. Хотя чемпионом мира по паникерству, пожалуй, обладает антивирус NAV J. Антивирус MultiScan, хотя и ошибается очень часто, зато умеет автоматически лечить некоторые типы вирусов, что пока недоступно другим антивирусам.

Глава 4. Как избавиться от загрузочного вируса

Он... тщательно подчистил
сковородку. Взгляд его прояснился.
А. и Б. Стругацкие. Хищные вещи века.

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

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

Например, существуют экзотические вирусы, заражающие командные ВАТ-файлы. Жизнеспособность таких вирусов в "дикой природе" крайне невелика, их можно сравнить разве что с лабораторными белыми мышами J.

Или вот еще - макро-вирусы, заражающие файлы сложных типов, которые содержат результаты работы некоторых текстовых процессоров и электронных таблиц. Например, файл с расширением .DOC, создаваемый текстовым процессором MS Word есть, грубо говоря, текст + программа его форматирования + еще что-то. Макро-вирусы заражают именно эту программу форматирования текста. Они образуют отдельный большой класс вирусов, которые здесь и сейчас рассматриваться не будут.

Но существуют и другие программные компоненты, не имеющие никакого отношения к дисковым файлам.

Например, стандартные процедуры ввода/вывода ПЭВМ (BIOS) размещаются в ПЗУ. Вопрос о заразимости вирусами BIOS'ов, размещенных в перезаписываемой Flash-памяти носит пока только теоретический характер.

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

4.1. Техническая информация

Для понимания сути вопроса от читателя требуется хотя бы поверхностное представление о логической структуре дисковых устройств.

Несколько слов об инструменте, при помощи которого мы будем изучать дисковую структуру. Наиболее удобна программа DiskEdit, входящая в состав знаменитых Нортоновских утилит. Автор рекомендует пользоваться либо версией 8.0, либо версиями, более ранними, чем 6.0, ибо в "средних" версиях содержится ряд грубых ошибок L.

Запускаем DiskEdit.exe или DE.exe, нажимаем Alt-D, устанавливаем обязательно режим Physical Disks (Физические Диски), выбираем желаемое устройство и...

4.1.1. Логическая структура диска

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

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

Рабочая поверхность содержит ряд концентрических окружностей - дорожек (треков). Самая ближняя к внешнему краю диска дорожка имеет номер 0, следующая - номер 1 и т.п. На дискетах количество дорожек бывает 40 или 80, на винчестерах типичное количество - несколько сотен или тысяч. Старые BIOS'ы не поддерживали количество дорожек большее, чем 1024. BIOS'ы на современных материнских платах обязательно поддерживают режим работы с большим количеством дорожек (режимы LBA и/или Large). Совокупность всех дорожек, равноудаленных от оси и расположенных на всех рабочих поверхностях, носит название цилиндра. Каждая магнитная дорожка разбита на ряд секторов, нумеруемых от 1 и до максимального значения, которое для дискет составляет 9, 15 или 18, а для винчестеров достигает иногда несколько десятков. В один сектор возможно записать 512 байт информации. Приведем стандартные характеристики для разных типов дискет:

Тип дискетыDS/DD 5"DS/HD 5"DS/DD 3.5"DS/HD 3.5"
Головок2222
Дорожек40804080
Секторов991518
Объем360 Кб1.2 Мб720 Кб1.44 Мб

Любой сектор можно однозначно определить, задав тройку {головка,дорожка,сектор}. Например, самый 1-й сектор на дисковом устройстве имеет координаты 0/0/1.

Прямая работа с дисковым устройством через контроллер дисковода или винчестера очень сложна. Поэтому в ПЗУ каждого PC-совместимого компьютера записаны стандартные процедуры, обеспечивающие доступ к дисковым устройствам. 99% программ, в том числе и сама операционная система DOS, пользуются при обращении к дискам именно этими процедурами.

Доступ к дисковым процедурам возможен через прерывание 13h. Впрочем, эти процедуры (только в той части, которая касается работы с дискетами) имеют еще одну точку входа - через прерывание 40h. Интересно, что обработчик прерывания 40h располагается в BIOSe в 99.99% случаев по адресу F000h:EC59h (проверьте!).

Самый первый сектор на дисковом устройстве (0/0/1) занимает программа загрузки.

4.1.2. Алгоритм загрузки компьютера с диска

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

В случае "наличия отсутствия" таковых, "доисторические" IBM PC загружали кассетную (расположенную в ПЗУ) версию интерпретатора BASIC'а. Современные персоналки в этом случае просто извещают: NO ROM BASIC, SYSTEM HALTED. И глохнут L.

Вы, вероятно, помните, что очень часто в программе SETUP есть опция, позволяющая заставить программу POST начинать поиск либо с дискеты, либо с винчестера, либо вообще игнорировать одно из устройств. Но в любом случае POST должна добраться до какого-нибудь дискового устройства, прочитать сектор 0/0/1, загрузить его содержимое в ОЗУ по адресу 0:7C00h и передать туда управление. С этого момента компьютер снимает с себя всякую "аппаратную ответственность" за ход загрузки. Чем и пользуются зловредные вирусы J.

4.1.2.1. Загрузка с дискеты

Предположим, что программа POST нашла в кармане дисковода A: какую-то дискету. Что же она загрузит в память?

Сектор 0/0/1 (boot-сектор) имеет, в-общем, следующую архитектуру:

Содержимое Размер в байтах Примечание
Команда перехода на код загрузки 3 jmp XXXX или jmp XX nop
Таблица параметров дискеты 21-65 -
Код загрузки остальное - -
Диагностические сообщения - -
Признак загрузочного сектора 2 0AA55h

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

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

а также комментированый исходный текст программы-загрузчика, разработанной В.Аношиным и опубликованной в журнале "Монитор" несколько лет назад.

Варианты кода загрузки также имеют различную длину, содержат разные команды, но, в-общем, выполняют одни и те же функции: обращаются к корневому каталогу своего диска и ищут в нем файлы операционной системы (для "настоящей" MSDOS это IO.SYS и MSDOS.SYS); в случае успеха загружают их в память и передают на них управление; в случае неуспеха выдают на экран что-то вроде:

Non-system disk or disk error
Replace and press any key,

ждут нажатия клавиши и перезагружают систему, повторно вызывая программу POST.

4.1.2.3. Загрузка с винчестера

Теперь рассмотрим случай, когда в дисководе "ничего не торчит" и программа POST прочитала в 0:7C00h нечто из стартового сектора не дискеты, а винчестера.

Загрузка с винчестера, в отличие от загрузки с дискеты, происходит в несколько этапов. В стартовом секторе винчестера располагается не загрузчик конкретной операционной системы, а так называемый внесистемный загрузчик.Кроме того, там же располагается таблица логических разделов винчестера (Partition Table).Совокупность кода внесистемного загрузчика и таблицы разделов образуют главную загрузочную запись - MBR (Master Boot Record).

Итак, стартовый сектор винчестера (0/0/1) имеет, в-общем, следующую архитектуру:

Содержимое Размер в байтах Примечание
Код поиска и инициации загрузчика ОС -
Диагностические сообщения - -
Таблица разделов 4*16=64 -
Признак загрузочного сектора 2 0AA55h

Таблица разделов состоит из 4 строк по 16 байтов каждая. В каждой строке содержится описание одного логического раздела: тип файловой системы для раздела, адрес первого сектора, адрес последнего сектора, длина и пр. А в самом первом байте одной из строк содержится признак "активный раздел" (80h), и это означает, что в 1-м секторе этого раздела должен содержаться загрузчик операционной системы.

Пример таблицы разделов, содержащей 2 элемента:

   1 2 3 4 5 6
   BIGDOS Yes 1 0 1 31 152 63 63 308385
   EXTEND No 0 153 1 31 825 63 308448 1356768
   unused No 0 0 0 0 0 0 0 0
   unused No 0 0 0 0 0 0 0 0
   
000001B0:                                              80 01
000001C0:  01 00 06 1F 3F 98 3F 00 - 00 00 A1 B4 04 00 00 00
000001D0:  01 99 05 1F FF 39 E0 B4 - 04 00 E0 B3 14 00 00 00
000001E0:  00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00
000001F0:  00 00 00 00 00 00 00 00 - 00 00 00 00 00 00

Код загрузчика обращается к этой таблице и начинает ее сканировать, пытаясь выделить активный раздел и заодно проверить корректность таблицы. В вышеприведенном примере активен раздел, описанный в 1-й строке. Его стартовый сектор, содержащий загрузчик операционной системы, располагается по адресу 0/1/1 ( 0-я строна, 1-я дорожка, 1-й сектор, ибо порядок компонентов адреса сектора в таблице несколько иной).

Код загрузчика "перетаскивает" себя в другой фрагмент ОЗУ, освобождая область 7C00h:0, считывает туда содержимое следующего загрузчика и передает туда управление. С этого момента начинает работать загрузчик операционной системы.

Для чего же предназначена такая несколько сложноватая процедура загрузки? Дело в том, что разные разделы винчестера могут содержать различные операционные системы! Например, MSDOS, Xenix, OS-9000 и пр. Изменяя местоположение признака активного раздела в Partition Table, можно обеспечить загрузку и работу в нескольких операционных системых на одной машине с одним винчестером.

Существуют программные средства, обеспечивающие диалоговый сервис по загрузке различных ОС: IBM Boot Manager (поставляется вместе с OS/2), MicroWare MultiLoader (поствляется вместе с OS-9000) и пр. Впрочем, зная теперь схему загрузки, такую программу нетрудно написать самостоятельно.

В случае, когда главная и единственная операционная система - MSDOS, в стартовом секторе активного раздела (0/1/1) располагается... старый знакомый загрузчик MSDOS, практически аналогичный тому, который "живет" в boot-секторе дискеты и который мы уже рассматривали ранее.

К статье прилагается файловый образ стартового сектора винчестера и его комментированный листинг. (Обратите внимание - код занимает чуть меньше половины сектора!)

4.2. Принципы работы загрузочных вирусов

Как устроены загрузочные вирусы.Основная идея любого загрузочного вируса весьма проста.

Вирус просто замещает собой один из загрузчиков на дискете и/или винчестере.

Но, для того, чтобы обеспечить дальнейшую нормальную загрузку, могут быть использованы, например, следующие приемы.

4.2.1, Методы получения вирусом управления

Вирус может сохранить старое содержимое загрузочного сектора в "укромном уголке". После выполнения запланированных действий вирус просто считывает сохраненный оригинальный загрузчик в память по адресу 0:7C00h и передает ему управление. Т.е. в многоступенчатом процессе загрузки просто появляется еще одна, "нелегальная" ступенька. (Это очень похоже на действия вымогателей: схватили, держат на "секретной хате" и изредка дают пообщаться с семьей по телефону J).

Вирус может самостоятельно выполнять действия (или их часть), которыми раньше занимался оригинальный загрузчик.

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

Разумеется, возможны различные комбинации и модификации этих методов. Например: вирус может просто изменить местоположение активного раздела в Partition Table, но полностью самостоятельно выполнить функции следующего в цепочке загрузчика - налицо комбинация 3-го и 2-го методов.

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

4.2.2. Способы сохранения оригинального загрузчика.

Наиболее просто найти "укромный уголок" на винчестере. Например, вся 0-я дорожка (кроме 1-го сектора), как правило, бывает пустой.Зная это, вирус Stoned "заныкивает" оригинальный загрузчик в 0/0/7. Основных недостатков два:

Некоторые вирусы создают для своих нужд (в том числе и для содержания "секретной хаты") псевдосбойные кластеры, т.е. фрагменты винчестера, на самом деле совершенно нормальные, но объявленные неисправными. (Ребята, а чегой-то у вашего приятеля руки связаны и рот заклеен? - Дык, буйный он, в психушку везем J). Но, во-первых, любой NDD или ScanDisk легко и просто отыскивает такие кластеры. А во-вторых, вот уже много лет, как изготовители "винтов" научились делать свою продукци более-менее качественно: если винт "летит", то уж он "летит" сразу, а не "сыплется" помаленьку. (По крайней мере, так уверяют авторы в солидных компьютерных журналах; им хочется верить J).

По-разному "извращаются" авторы загрузочных вирусов, отыскивая свободное место на дискете.

Например, известно, что в 99.99% случаев современные дисководы позволяют отформатировать на дискете дополнительные, так называемые "инженерные" цилиндры. В случае с дискетой DS/ HD 3.5" это может быть, например, 80-й цилиндр.

По-прежнему, можно объявить какой-нибудь сектор неисправным.

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

4.2.3. Прочие характеристики загрузочных вирусов

Вирусы, занимающие 1 сектор, в-общем, свидетельствуют о неплохой програмистской квалификации своих авторов. В самом деле, если вирус заражает и дискеты и винчестер, попробуйте-ка разместить в одном секторе вирусный код, да ведь еще 64 байта в этом секторе займет Partition Table, и минимум 21 - таблица параметров дискеты!

Поэтому нередки случаи, когда вирусу требуется гораздо больше, чем 1 сектор. Например, вирус DEF0 обрушивает на 0-ю дорожку винчестера свое потное и грязное брюхо длиной 8 секторов J.

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

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

4.2.4. Анализ конкретного загрузочного вируса

Рассмотрим принципы работы загрузочных вирусов на примере классического Stoned.

Как уверяют вирусологи, вирус этот был написан приблизительно в 1988 г. в Новой Зеландии и первоначально умел заражать только дискеты 360 Кб. Вскоре появилась версия, умеющая заражать еще и жесткие диски. Характерным признаком вируса Stoned являются знаменитые "Your PC is now Stoned! LEGALIZE MARIJUANA!". (Впрочем, по словам тех же вирусологов, существует несколько вариантов написания этих строчек).

Вирус поразил огромное количество подражаний. В каталогах антивирусных программ можно обнаружить многие десятки Stoned'ов, т.е. вирусов, написанных "по мотивам", а то и просто являющихся слегка видоизмененным оригиналом.

Что же делает вирус Stoned?

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

Но вот мы случайно оставили ее в кармане дисковода во время перезагрузки компьютера. Программа POST, ничтоже сумняшеся, загружает содержимое сектора 0/0/1 дискеты по адресу 0:7С00h , передает туда упраление и...

Вирус стартовал! Первым делом он уменьшает по адресу 0:413h объем доступной машине памяти на 2 Кб (хотя хватило бы и одного). С этого момента "наивная и доверчивая" персоналка будет считать, что у нее в распоряжении ниже A000h:0, например, не 640 Кб, а только 638 Кб.

Затем вирус вычисляет сементный адрес вновь образованного неучтенного фрагмента памяти, перетаскивает себя туда и передает управление. Все правильно, область 7С00h:0 надо освобождать для дальнейших загрузчиков.

Вирус устанавливает адрес обработчика прерывания 13h на себя, расположенного в "секретной" памяти.

Наконец, очень важный для нас фрагмент. Вирус определяет источник загрузки - винчестер или дискета и считывает в 0:7С00h "томившийся в заточении" оригинальный загрузчик. На дискете это адрес 0/1/3, на винчестере - 0/0/7. (Если загрузка была с дискеты, он еще успевает между делом заразить винчестер J. И передает туда управление. Все, дальше загрузка идет, как ей и положено.

Если дискета была не системная, оригинальный загрузчик исправно закричит: Non-System disk, либо начнет загружать операционку. Или внесистемный загрузчик винчестера начнет, как ему и полагается, сканировать Partition Table и пр.

Все-вроде бы по-старому. Только из ОЗУ "выпал" кусок размером 2 Кб. В нем торчит агрессивный обработчик 13-го прерывания, через который проходят все обращения к дискетам. Если на зараженной машине вставить дискету в карман дисковода и обратиться к ней, например, прочитать каталог, вирус немедленно "сядет" в ее boot-сектор. Дискета будет также заражена.

Разумеется, кроме "половой жизни" вирус жаждет еще и "духовных развлечений". С вероятностью 1/8 он при загрузке извещает перепуганного пользователя, что его компьютер, мол, окаменел. И противно при этом хихикает, наверное. J

Файловый образ (купированный) загрузчика, зараженного вирусом Stoned, и фрагмент его комментированного листинга прилагаются.

4.3. Обнаружение загрузочного вируса на машине

Как обнаружить присутствие неизвестного зараженного вируса на компьютере или в загрузочном секторе дискеты?

Этот вопрос уже обсуждался в 1-й части цикла статей. Добавить осталось немного.

Во-первых, визуально. При помощи программы DiskEdit можно обратиться к стартовому сектору дискеты и/или винчестера и сравнить с оригиналом. К статье прилагаются несколько вариантов boot-секторов дискеты и образец "здорового" mbr. Также для сравнения прилагаются файловые образы mbr, "больных" вирусами Stoned, OneHalf и Kaczor.

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

Вирус OneHalf пытается маскироваться под "здоровый" сектор, но его выдают "неправильные" первые байты кода загрузчика.

Вирус Kaczor пошел еще дальше, он сохранил в неприкосновенности даже 1-е байты. Но вот дальше тоже отступил от "исторической правды".

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

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

Во-вторых, многие современные антивирусы умеют "отключать" Stealth-механизм и могут оценить "здоровье" загрузочного сектора по характерным признакам. (Принципы эвристического анализа обсуждались нами ранее, в 3-й части цикла статей).

Да что антивирус... Автор лично видел компьютер с опцией Virus Warning в setup'е. Достаточно просто вставить "больную" дискету в карман дисковода и компьютер начинает верещать!

4.4. Пользуйтесь презервативами!

Избежать заражение дискеты очень просто: надо заклеить на ней прорезь или передвинуть рычажок защиты от записи. Но это, естественно, бессмысленно, если Вы собираетесь что-нибудь переписывать на эту дискету с зараженной машины.

Чтобы избежать заражения винчестера, можно применить простейшее, но абсолютно надежное средство - не забывайте дискеты в кармане дисковода во время перезагрузки ПЭВМ.

Если же это все-таки произошло, Вам спасет включенная опция Virus Protection в BIOS Setup Вашего компьютера. При попытке вируса залезть в mbr Вы, получите визуальное и аудиальное (звуковое) предупреждение. Впрочем, эта в-общем полезная возможность BIOS обычно не защищает ни всю 0-ю дорожку, ни стартовые сектора разделов L.

4.5. Как избавиться от загрузочного вируса

4.5.1. Удаление загрузочного вируса с дискеты

Удаляем вирус с дискеты. Сделать это крайне просто - перепишем всю информацию на винчестер или другую дискету, отформатируем дискету и вернем на нее, все что на ней было раньше. Естественно, машина, на которой мы проделываем эту операцию, должна быть "чистой".

4.5.2. Удаление загрузочного вируса с винчестера

Удаляем вирус с винчестера. Обычно, это тоже достаточно простая операция. Загружаемся с "чистой" дискеты и выполняем команду:

   
fdisk /mbr

Это позволит "очистить" от вируса внесистемный загрузчик и при этом сохранить Partition Table.

Обратите внимание: "кровавый format c:" при работе не трогает 0-ю дорожку, и позволяет избавиться только от загрузочных вирусов, заразивших boot в логическом разделе! Вероятно, это и порождает легенды о "бессмертных" вирусах, которых можно "убить" только низкоуровневым форматированием или посадкой на винчестер операционной системы Unix J).

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

4.5.3. "Ручное" лечение загрузочного вируса

Если Вы боитесь вручать такое ответственное и опасное дело, как лечение загрузочного вируса, программам fdisk и format, можно востпользоваться программой DiskEdit, входящей в состав популярных Нортоновских утилит. Эта программа позволяет произвести минимальное и контролируемое на каждом шаге "оперативное вмешательство" в логическую структуру дискового устройства. Обратите внимание: в программе DiskEdit принята несколько другая схема адресации секторов: не {головка/трек/сектор}, но {трек/головка/сектор}!

4.5.3.1. Случай дискеты

Случай лечения дискеты. Запускаем DiskEdit командой DiskEdit или DE (в зависимости от версии утилит; кстати, крайне не рекомендуется пользоваться программой DiskEdit из версий 6.х ! ). Вставляем в карман дисковода чистую (!) дискету того же формата (чтобы у них совпадала таблица параметров дискеты), что и больная. Нажатием Alt-D выбираем это дисковое устройство. Нажатием Alt-P выбираем здоровый сектор 0/0/1. Обратите внимание: в параметре "количество секторов" необходимо установить единицу (а версии 6.0 это невозможно L) !!! Нажатием Alt-W выбираем опцию записи выбранного сектора в файл, например, boot.bin, лучше на винчестере.

Затем меняем дискету на больную и проводим операцию в обратном порядке: нажатием Alt-F выбираем файл boot.bin, нажатием Alt-W выбираем операцию записи, указываем в качестве результирующего объекта стартовый сектор больной дискеты и записываем здоровый сектор туда.

Все это можно произвести и без использования файла в качестве промежуточного буфера - при помощи хорошо известных пользователям Windows операций Mark (Ctrl-B), Copy (Ctrl-C) и Paste (Ctrl-V).

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

4.5.3.2. Случай винчестера

В случае с лечением винчестера можно применить тот-же прием. Но для этого необходимо "выдрать" с какой-то другой машины здоровый mbr. Вот тут без файла в роли промежуточного буфера точно не обойтись. Затем скопируем "здоровый" mbr в сектор 0/0 /1 зараженной машины. Осталось только восстановить правильное значение элементов Partition Table. Это поможет легко и просто сделать программа Norton Disk Doctor (NDD), входящая в состав тех же самых Нортоновских утилит. Достаточно запустить ее так:

   
NDD /rebuild

и программа самостоятельно рассчитает местоположение всех разделов и поместит их в mbr.

Впрочем, можно обойтись и без других дискет в качестве источников "донорских" загрузчиков. Если Вы знаете, где именно вирус прячет оригинальные загрузочные сектора, их можно взять именно оттуда. Например, мы уже знаем, что вирус Stoned хранит оригиналы на дискете в 0/1/3 (1/0/3 в терминах DiskEdit), а на винчестере в 0/0/7. Кроме того, поползав DiskEdit'ом по потенциальным "укромным уголкам", можно просто-напросто визуально опознать оригинальные загрузчики (если вирус их не зашифровал L ) и вернуть их на законное место.

4.5.4. Написание антивируса для загрузочных вирусов

Так поступают настоящие программисты. Наконец, если нам необходимо вылечить большое количество дискет и/или винчестеров, можно воспользоваться ранее описанными антивирусами, позволяющими автоматизировать процесс лечения новых вирусов (см. ГЛАВУ 3), либо написать свою программу.

К статье прилагается текст демонстрационной антивирусной программы AntiSton.

4.5.5. Важные особые случаи

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

Операцию лечения такого рода вирусов лучше поручить специальным антивирусным программным средствам, например, DrWeb Игоря Данилова или AVP Евгения Касперского.

4.6. Прилагаемые примеры

К статье прилагаются:

во-первых, msdos62.bin,pctools.bin и fformat.bin - различные варианты загрузочных секторов дискеты, anoshin.txt - исходный текст еще одного варианта загрузчика; mbr - образец "здорового" внесистемного загрузчика;

во-вторых, stoned.bin (купирован), kaczor.bin, onehalf.bin - загрузочные сектора винчестера, зараженные соответствующими вирусами;

в-третьих, stoned.lst, kaczor.lst, onehalf.lst, mbr.lst - комментированные листинги некоторых "больных" и "здоровых" загрузчиков;

наконец, antiston.c - исходный текст демонстрационной программы - антивируса.

Глава 5. Борьба с макровирусами

- Если мы можем жить на такой мрачной
и тяжелой планете, то наверное, здесь уже
кто-нибудь живет - мелкий и вредный!
И. Ефремов. Туманость Андромеды

Программы, способные к неконтролируемому саморазмножению (вирусы) могут существовать и в программных средах, отличных от MS DOS. Не будем здесь останавливаться на вирусах для Unix (Linux), для MAC OS, на Windows-вирусах и пр. Хотя, конечно, автор с удовольствием поделился бы, как ему удалось написать вирус для OS-9.

Речь здесь пойдет о так называемых макровирусах. Макровирусы в качестве ресурсов для своего размножения используют макросредства некоторых высокоуровневых приложений, в-основном - различных текстовых процессоров, электронных таблиц и СУБД (MS Word, MS Excel, MS Access, Ami Pro и пр.).

Это возможно, поскольку указанные приложения для увеличения гибкости и мощности предоставляют пользователю встроенные языки для обработки данных и управления возможностями приложения, а также интерпретаторы (исполняющие системы) для этих языков. Для MS Word таким языком является WordBasic (для версий 6.0 и 7.0) или VBA (для более старших версий).

Поскольку MS Word 6.0 по-прежнему весьма распространен, статья будет посвящена обнаружению, распознаванию и удалению макровирусов именно для этой (русифицированной) версии текстового процессора.

5.1. Программирование на языке WordBasiс

Для понимания принципов организации макровирусов и борьбы с ними совершенно необходимо иметь хотя бы поверхностное представление о языке WordBasic.

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

Программы на WordBasic-е хранятся в так называемых шаблонах (template) - документах особого вида. Главным для MS Word является шаблон NORMAL.DOТ. Все макрокоманды по умолчанию загружаются из него и сохраняются в нем. Если файл с этим шаблоном удалить, он будет создан автоматически при запуске MS Word.

Новую макрокоманду можно создать так: после запуска MS Word в меню Сервис выбрвть альтернативу Макрокоманда. Появится форма ввода. В поле Имя надо набрать имя макрокоманды, в поле Описание можно поместить справочный комментарий. Затем следует нажать на кнопку Создать, и появится окно ввода/редактирования макросов. Готовый макрос надо не забыть Записать.

Старую макрокоманду посмотреть/отредактировать можно, если в альтернативе Макрокоманда выбрать имя желаемого макроса из списка и нажать кнопку Правка.

В этом месте может прилететь розовая птица Обломинго J, если макрос, который вы желаете посмотреть/отредактировать зашифрован (ExecuteOnly). В этом случае кнопка Правка будет просто недоступна. Например, изначально зашифрована библиотека полезных макросов MSWORD.DOT. Если Вы хотите изучить возможности WordBasic в совершенстве, я рекомендую Вам заняться изучением текстов этих макросов. Зашифрованы также некоторые макровирусы (например, Cap). Как увидеть эти тексты? См. далее!

Итак, текст макрокоманды должен помещаться между

 
Sub Main
 ....
End Sub

Переменные бывают двух типов: вещественные и символьные (могут содержать как отдельные символы, так и строки). При описании имена строковых переменных должны завершаться буквой '$'. Разрешены массивы. Пример описания 2-х переменных и 2-х массивов:

 Dim AAA, BBB$
 Dim ССС(10), DDD$(2)

Над вещественными числами разрешены классические арифметические и логические операции: изменение знака, сложение (+), вычитание (-), умножение (*), вещественное деление (/), целочисленное деление (MOD). Для символьных переменных знак '+' означает конкатенацию строк.

Примеры:

AAA = 3.1415926
CCC(1) = (AAA * 2.718281828)/2
DDD$(2) = "Терпеть ненавижу "
BBB$ = DDD$(2)+"макровири!"

Управляющие конструкции известны по другим языкам программирования:

Условное исполнение:

If ... Then 
...
Else
...
End If

Цикл с условием:

While ...
...
Wend

Безусловный переход:

Goto ...

Цикл со счетчиком:

For ... = ... To ... Step ...
...
Next ...

Выбор альтернативы:

Select Case ...
Case ...
...
Case ...
...
Else
...
End Select

Большинство сложных операций доступно в виде команд и функций.

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

 
Len    ( строка ) - возвращает длину строки
MsgBox ( строка ) - выводит строку на экран в окне

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

MsgBox Строка
    и
MsgBox(Строка)

Очень важна для нас команда MacroCopy. Опишем ее подробно:

MacroCopy [Шаблон1:]Макро1$,
          [Шаблон2:]Макро2$
          [,ПризнакШифрации]

Выполняет копирование макроса Макро1$ из одного шаблона в другой, присваивая ему имя Макро2$. Если ПризнакШифрации присутствует и он ненулевой, то скопированный макрос не будет доступен для редактирования. Именно на работе этой команды основано свойство саморазмножения макровирусов.

MS Word предоставляет несколько сотен стандартных процедур и функций. Но рассмотрение их выводит нас за рамки статьи. Мы, конечно, будем подробнее останавливаться на некоторых из них, если это необходимо для понимания текста.

5.2. Принципы работы макровирусов

Рассмотрим основные принципы устройства и функционирования макровирусов.

5.2.1. Как макровирусы распространяются.

Макровирусы распространяются вместе с переносимыми с машины на машину документами MS Word. Внутри зараженного документа содержатся вирусные макросы. При этом документ имеет формат шаблона (template), что не исключает нахождение внутри него также содержательного текста, изображений, формул и т.п. Такой документ с точки зрения обработки текста ничем не отличается от "нормального". По умолчанию принято, что файлы "обычных" документов MS Word имеют расширения DOC, а шаблоны - DOT. На практике же MS Word различает их по внутреннему формату автоматически, не извещая об этом пользователя.

"Нормальный" документ превращается в шаблон в момент копирования в него вирусных макросов, т.е. в момент исполнения команды MacroCopy, если установлен в определенное значение флажок способа сохранения FileSaveAs.Format. Важным отличием шаблонов от "нормальных" документов является невозможность сохранения их при помощи SaveFileAs.

5.2.2. Как макровирусы получают управление

В MS Word существуют так называемые "автоматические" макросы. Это макросы, автоматически выполняющиеся при определенных условиях и предназначенные для переопределения их пользователем.

К таким макросам относятся, например, AutoOpen (получающий управление при стандартном открытии документа) и AutoClose (получающий управление при закрытии документа). По умолчанию эти макросы "пустые" и не выполняют никаких действий.

Если открываемый документ (вернее, шаблон) содержит один из автоматических макросов, то они запустятся, а вместе с ними получит управление и макровирус.

Запретить запус автоматических макросов можно, установив в 1 системную переменную DisableAutoMacros.

5.2.3. Как макровирусы заражают MS Word

Получив управление (при помощи одного из автоматических макросов) из зараженного документа, макровирус обычно предпринимает действия по "фиксированию" себя в системе. Для этого он переносит свои макросы в "главный" шаблон NORMAL.DOT. Макросы из этого шаблона автоматически загружаются и активируются при запуске MS Word. Таким образом, если вирус переносит свои макросы в "главный" шаблон, то он получает возможность постоянного находиться в системе.

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

Естественно, удалять NORMAL.DOT можно и имеет смысл только "выключив" MS Word.

5.2.4. Как макровирусы заражают другие документы

Часть действий выполняется в MS Word посредством выполнения стандартных макросов. Так, например, если для открытия нового документа пользователь выбирает пункт меню Открыть/Open, то активизируется стандартный макрос FileOpen.

Если макровирус переопределяет один или несколько таких стандартных макросов, то он иммет возмлжность получить управление в момент выполнения соотвествующих операций. Естественно, обычно макровирусами переопределяются макросы, соответствующие файловым операциям MS Word: FileOpen, FileClose, FilePrint и т.п.

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

5.2.5. Типовая структура макровируса

Как должно быть ясно из вышеприведенного текста, типовой макровирус должен содержать макросы двух типов:

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

Рассмотрим структуру одного из "классических" вирусов - DMV (часть текста пропущена). Этот вирус считается первым из созданных макровирусов. Он состоит из одного макроса - AutoClose.

Макрокоманда AutoClose (часть текста пропущена):

Sub MAIN
  title$ = "Document Macro Virus"
  MsgBox "Counting global macros.", title$, 16

  total = CountMacros(0) ' Число макросов,
                         ' уже содержащихся
                         ' в NORMAL.DOT
  present = 0

  If total > 0 Then      ' Если в NORMAL.DOT
                            ' есть какие-то макросы,

  For cycle = 1 To total ' Цикл по всем макросам
                         ' из NORMAL.DOT

  If MacroName$(cycle, 0) = "AutoClose" Then
                         ' Есть ли там уже
                         ' макрос AutoClose?

  MsgBox "AutoClose macro virus is already"+
  "installed in NORMAL.DOT.", title$, 16

  present = 1 ' Выставляем флажок и считаем, что
              ' NORMAL.DOT уже заражен и мы стар-
              ' товали именно из него

  End If
 End If

 a$ = WindowName$() + ":AutoClose" ' Генерируем имя
                                             ' вирусной копии
                                             ' макроса из двух
                                             ' частей: имени
                                             ' текущего документа-
                                             ' шаблона и  имени
                                             ' макроса

 If present # 1 Then  ' Если NORMAL.DOT не заражен

 ''''''''''''''''''''''''''''''''''''''''''''''''''''
 ' Пропущен фрагмент копирования макроса AutoClose  '
 ' из текущего документа в NORMAL.DOT               '
 ''''''''''''''''''''''''''''''''''''''''''''''''''''

 MsgBox "Infected NORMAL.DOT with copy of"+
        "AutoClose macro virus.", title$, 16

 Else ' Если NORMAL.DOT уже заражен

 present = 0
 If CountMacros(1) <> 0 Then ' Если число макросов в
                                   ' текущем документе
                                   ' ненулевое

 MsgBox "AutoClose macro virus already present"+
        "in this document.", title$, 16

present = 1 ' Выставляем флажок самозараженности.
            ' Кроме того, это означает, что вирус
            ' стартовал из документа, а не из
            ' NORMAL.DOT

End If

If present = 0 Then ' Если мы стартовали из
                    ' NORMAL.DOT и заражаем
                    ' другой документ

FileSaveAs .Format = 1  ' Тип сохраняемого документа -
                        ' шаблон !!!

MsgBox "Saved current document as template.", title$, 16

'''''''''''''''''''''''''''''''''''''''''''''''''''
' Пропущен фрагмент копирования макроса AutoClose '
' из NORMAL.DOT в документ.                       '
'''''''''''''''''''''''''''''''''''''''''''''''''''

MsgBox "Infected current document with copy of"+
       "AutoClose macro virus.", title$, 16
End If
End If

MsgBox "Macro virus has been spread."+
       "Now execute some other code"+
       "(good, bad, or indifferent).", title$, 16
End Sub

Добавим еще несколько комментариев к вышеприведенному алгоритму.

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

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

5.2.6. Некоторые малораспространенные алгоритмы заражения

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

Так, команда

ToolsCustomizeKeyboard .KeyCode=32, .Category=2,
                       .Name="Gangsterz", .Add, .Context=0

ведет к тому, что при нажатии клавиши "пробел" будет выполнен макрос с именем Gangsterz.

5.2.7. Проблемы совместимости

В мире широкое распространение имеют так называемые локализованные версии MS Word: "руссифицированые", "испанизированные", "японизированные" и др. Локализация подразумевает не только перевод системных сообщений и пунктов меню на соответствующий язык, но и подчас переименование стандартных имен, ключевых слов и т.п. В качестве примера приведем ряд таких "синонимов":

FileNew английский
FilerNyt датский
BestandNieuw голландский
TiedostoUusi финский
FichierNouveau французский
DateiNeu немецкий
FileNuovo итальянский
FicheiroNovo португальский
ArchivoNuevo инспанский
ArkivNytt шведский
ArquivoNovo бразильский

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

5.2.8. О проявлениях макровирусов

Возможности языка WordBasic весьма велики, соотвественно этому проявления макровирусов разнообразны - это и файловые операции (открытие/закрытие/чтение/запись/переименование/удаление), различные "шутки" с редактируемым текстом, выдача на экран разнообразных сообщений и пр.

Одним из примечательных проявлений макровирусов можно считать изменение стандартной структуры меню MS Word. Например, вирус Cap при получении управления удаляет из структуры меню альтернативу Макро, обеспечивая этим самым невозможность (относительную) визуально обнаружить свое наличие в файле NORMAL.DOT.

5.3. "Ручное" обнаружение и удаление макровируса

Опишем алгоритм, позволяющий в большинстве случаев обнаружить и обезвредить макровирус в MS Word.

Шаг 1. Лечение необходимо производить в "чистой", т.е. заведомо не зараженной макровирусами среде MS Word. Для этого завершим работу MS Word (не "свернем" до иконки или полоски на линейке задач, а именно завершим!) и при помощи какого-нибудь файлового менеджера (например Проводника/Explorer-а или даже просто NC) переименуем (или даже удалим) файл NORMAL.DOT из каталога WINWORD.

Шаг 2. Вновь запустим MS Word. Программа обнаружит отсутствие файла NORMAL.DOT и предложит создать новый, "чистенький". Естественно, согласимся.

Шаг 3. В меню Файл (File) выберем альтернативу Шаблоны (Template). Далее на последовательно появляющихся окнах нажмем кнопки Организатор, Закрыть Файл и Открыть Файл. В результате чего MS Word предложит загрузить один из документов-шаблонов, в роли которых может выступать как документ, предназначенный для лечения, так и переименованный во время Шага 1 старый "главный" шаблон. Загрузим "пациента".

Шаг 4. Выберем вкладку Макро и обнаружим на экране список макросов, заключенных внутри загруженного шаблона. Вирус (если он есть) скрывается именно среди них. Если список пуст, значит вирус отсутствует!

Шаг 5. Если список непуст, внимательно изучим его содержимое. О наличии вируса с большой долей вероятности свидетельствуют:

Шаг 6. Обнаружив вирусные макросы, ликвидируем их, поочередно отмечая мышью и нажимая кнопку Удалить. Лучше всего, если Вы удалите все макросы, особенно, если не уверены - какие из них "полезные", а какие "вредные".

Шаг 7. Имеем прооперированный и теперь абсолютно здоровый файл. Единственный недостаток (если мы лечили документ, а не NORMAL.DOT) в том, что файл по-прежнему остался шаблоном, и для него недоступна операция FileSaveAs. Какие пустяки! J

5.4. Написание антивируса

Шаблон для написания антивирусных программ, производящих рекурсивный поиск в COM- и EXE-файлах файлах указанного диска, был нами разработан ранее ( см. программу ANTISVC ). Для обеспечения возможности лечения DOC-файлов в текст программы необходимо внести минимальные изменения:

Принципы создания антивируса рассмотрим на примере написания средства для борьбы с широко распространенным вирусом Cap.

5.4.1. Структура DOC-файла

DOC-файл имеет весьма сложную структуру. Он представляет собой OLE2 - объект, организованный по принципу отдельной файловой системы: содержит каталог своих подобъектов, таблицы размещения подобъектов, пустые участки и пр. Документ MS Word является одним из таких подобъектов. Максимально корректное извлечение документа MS Word из DOC-файла реализуется при помощи специализированных библиотек MS Windows API. Но есть способ лучше (т.е. проще). Известно, что:

Приведем также необходимую справочную информацию:

Для примера рассмотрим фрагмент дампа DOC-файла, зараженного вирусом Cap:

    00000:  D0 CF 11 E0 A1 B1 1A E1 .. .. .. .. .. .. .. ..
            ^^^^^^^^^^^^^^^^^^^^^^^
            Признак OLE2-объекта

    00880:  DC A5 65 00 35 C0 19 04 00 00 00 00 65 00 00 00
            ^^^^^^^^^^^^^^^^^^^^^^^
            1-й заголовок документа MS Word (левый)

    00990:  B0 08 00 00 00 00 00 00 5B 0A 00 00 02 00 00 00
                                    ^^^^^
                        Позиция 1-го макрозаголовка (левая)

    012D0:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
                                             ^^^^^
                         Признак макрозаголовка отсутствует

    02800:  DC A5 65 00 35 C0 19 04 00 00 15 00 65 00 00 00
            ^^^^^^^^^^^^^^^^^^^^^^^       ^^^^^^^^^^^
            2-й заголовок документа MS Word   +----- Статус

    02910:  D1 12 00 00 00 00 00 00 7C 14 00 00 12 02 00 00
                                    ^^^^^
                                Позиция 2-го макрозаголовка

    03C70:  .. .. .. .. .. .. .. .. .. .. .. .. FF 01 0A 00
                                                ^^^^^ ^^^^^
    Признак корректного макрозаголовка -----------+     ╕
    Количество макросов (10) ---------------------------+

    03C80:  55 E6 00 00 00 00 01 00 A4 0C 00 00 2C 09 00 00
            ^^ ^^                               ^^^^^^^^^^^
            ╕   ╕                        Длина 0-го макроса
            ╕   +  Байт шифрации
            +----  Версия макроязыка

    03C90:  03 00 00 00 72 08 00 00 55 E9 01 00 02 00 01 00
                        ^^^^^^^^^^^ ^^ ^^
                        Позиция 0-го макроса

    03CA0:  D0 15 00 00 26 00 00 00 03 00 00 00 9E 11 00 00
                        ^^^^^^^^^^^             ^^^^^^^^^^^
                  Длина 1-го макроса   Позиция 1-го макроса

   и т.д.

Разобравшись в вышеприведенном дампе, легко разработать алгоритм обнаружения и лечения DOC-файлов, зараженных вирусом Cap.

К статье прилагаются:

5.4.2. Принципы лечения

Для лечения зараженного DOC-файла вполне достаточно сбросить признак "шаблона". Удалять собственно код макроса при этом не обязательно. Для лечения зараженного шаблона, например, NORMAL.DOT, необходимо в макрозаголовке скорректировать поле "Количество макросов" и исправить число и содержимое описателей макросов, что не должно вызвать затруднений при условии знания структур данных.

5.4.3. Рекомендации по выбору сигнатур

Термин "сигнатура макровируса" имеет в точности такое же значение, что и в случае с COM- и EXE-вирусами. Более того, пока не существует полностью полиморфных макровирусов ( "полиморфность" часто заключается в вариации порядком размещения и числом макросов), а значит - макровирус всегда может быть определен и однозначно детектирован при помощи аппарата сигнатур.

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

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

64h 67h С2h 80h

являются 16-ричным кодом для команды MacroCopy.

5.4.4. Написание вакцин и фагов на языке WordBasic

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

Целесообразно использовать для этой цели принцип переопределения стандартных макросов, в частности FileOpen (а также, возможно, ShellOpen). Поскольку приходится загружать потенциально больные документы, необходмо использовать команду DisableAutoMacros для предотвращения старта вируса.

Приведем пример макроса, выполняющего роль простейшей вакцины для вируса Cap:

   
'*************************************
'* Простейшая вакцина для вируса CAP *
'*       ( макрос FileOpen )         *
'* (с) Климентьев К., Самара 1998    *
'*************************************
Sub MAIN
 Dim dlg As FileOpen
 DisableAutoMacros 1
 GetCurValues dlg
 Dialog dlg
 FileOpen dlg
 Flag = 0
 For i = 1 To CountMacros(1)
  If MacroName$(i, 1) = "CAP" Then Flag = - 1
 Next i
 If Flag = - 1 Then
  r = MsgBox("Кажется, это CAP... Продолжить?", "Warning!", 4)
 End If
 If r = 0 Then FileExit(2)
End Sub

Необходимо создать макрос с именем FileOpen, поместить в него вышеприведенный текст (можно без комментариев) и сохранить в шаблоне NORMAL.DOT. При загрузке зараженного документа на экран будет выдано окно с текстом предупреждения. Пользователь имеет возможность продолжить работу с зараженным документом, либо немедленно прекратить ее.

5.5. Прилагаемые примеры

К статье прилагаются:

Глава 6. Борьба с Windows-вирусами

...Ему не раз доводилось охотиться на зверей
гораздо более странных, чем самые экзотические
животные Африки... Инек не понимал технологии
и научных достижений, сделавших этот
фантастический тир реальностью, но просто
принимал на веру, надеясь, что когда-нибудь
ему удастся отыскать ключ к двери, ведущей к пониманию...
К. Саймак. Наследие звезд.

Вашему вниманию предлагается довольно краткое и поверхностное рассмотрение очень сложной и объемной темы. Таковым оно является по ряду причин.

Во-первых, история Windows-вирусов еще в разгаре, и итоги подводить рано.

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

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

Чтобы понять, о чем пойдет речь в этой статье, рекомендую читателю следующую литературу.

  1. Фролов А.В., Фролов Г.В. Защищенный режим процессоров Intel 80286/80386/80486.- М: "Диалог-МИФИ", 1993. - 234 с.
  2. Зубков С.В. Assembler. Для DOS, Windows и Unix.- М.: ДМК, 1999.-640 С.
  3. Руководство программиста по Microsoft Windows 95.- М.: "Русская редакция", 1997.-600 с. 4. Matt Pietrek. Windows 95 system prograsmming secrets. IDG Books Worldwide, 1995. - 756 p.

Также совершенно необходимо иметь доступ к справочнику по API-функциям Win32 и обладать хотя бы минимальными знаниями и умениями по программированию в Windows (желательно, уметь писать код на языке Си, а вот владеть размещеием кнопок в Delphi-форме совершенно не обязательно).

Буду стараться использовать устоявшуюся, традиционную, проверенную терминологию:

6.1. Предварительные замечания

6.1.1. Режимы процессора

Операционные системы семейства MS Windows, в отличие от MS-DOS, работают в защищенном режиме процессора i80x86.

В этом режиме могут быть задействованы в 32-битовом режиме традиционные регистры общего назначения: AX превращается в EAX, BX в EBX, BP в EBP, FLAGS в EFLAGS и пр.

Сегментные регистры CS, DS, ES и CS остаются 16-разрядными, но в их компании появляются еще два "приятеля": FS и GS.

В защищеном режиме задействованы также дополнительные регистры процессора, которые не использовались в реальном режиме:

Естественно, увеличивается количество команд процессора, прежназначенных как для стандартных манипуляций с данными, так и для работы с "новыми" регистрами.

Мы встретимся с тремя "типами" защищенного режима.

Защищенный режим типа segmentation (старший бит в регистре CR0 равен 0). В этом режиме работает Windows 3.X. Предполагается, что адресное пространство разбито на ряд сегментов. Каждой 16-битовой NE-программе выделяется один или несколько таких сегментов, к ним этой программе разрешен какой -либо вид доступа: для чтения, для записи, для исполнения кода и пр. К остальным сегментам программе доступ заперещен вообще, при любой попытке обращения происходит исключение. (Зато эти сегменты доступны для какой-то другой программы.) Адресация к памяти ваполняется парой СЕЛЕКТОР:СМЕЩЕНИЕ, где селектор играет роль индекса в таблице ДЕСКРИПТОРОВ, содержащих старшую часть линейного/физического адреса (а также права доступа и прочую управляющую информацию).

Защищенный режим типа pagination (старший бит в регистре CR0 равен 1). В этом режиме работают Windows 9X и Windows NT/2K. Адресное пространство разбито на множество страниц длиной по 4 Кб. Селектор по-прежнему указывает на таблицу дескрипторов, только дескриптор в паре со смещением образуют линейный (но не физический!) адрес. 32-битовой PE-программе выделяется уникальный набор последовательных линейных адресов, образующих непрерывную "ленту" памяти с линейными адресами от 0 до 0FFFFFFFFh, и она видит только их и манипулирует только ими. На самом деле линейный адрес содержит указатель в таблице страниц, ссылающийся на какую-то 4-х-килобайтовую страницу физической памяти, и смещение внутри этой страницы. Таким образом, физическая память разрезана на "клочки", эти "клочки" перемешаны, но незаметно склеены в "научно-фантастическом 4-ом измерении", так что 32-битовая PE- программа "клочковатости" не замечает. Для 16-битовых программ NE-формата специально моделируются условия режима типа segmentation.

Режим виртуального i8086 (17-й бит в режиме флагов). В этом режиме работают DOS-программы. Программе выделяется 1 Мб памяти, в которой моделируются таблица прерываний, BIOS и пр. "Если надо", иногда может быть смоделирован доступ к расширенной памяти. Основные ограничения связаны с тем, что прямой доступ к внешним устройствам через порты ввода-вывода запрещен. Каждое такое обращение вызывает переключение в "настоящий" защищеный режим и вызов обработчика исключения. Windows 9X рассматривает "заявку" и довольно часто "удовлетворяет" ее, обращаясь к порту. А вот Windows NT/2K в аналогичной ситуации, как правило, заявку DOS-программы отклоняет.

Кроме того, в защищенном режиме не все программы обладают равными правами. Фрагменты операционной системы обладают наивысшим, нулевым уровнем приоритета (как говорят - работают в нулевом кольце защиты, в Ring0). Им позволяется видеть и изменять таблицы дескрипторов и страниц, карты портов и пр. Прикладные программы работают в 3-м кольце защиты и видят только свое линейное адресное пространство.

6.1.2. Архитектура Windows

Windows 3.x не является отдельной операционной системой, это - графическая оболочка, навороченная надстройка над DOS любой версии, начиная с 3.30. После включения компьютера загрузчик считывает в память IO.SYS и MSDOS.SYS, которые в свою очередь подгружают COMMAND. COM, далее при помощи CONFIG. SYS и AUTOEXEC.BAT устанавливаются необходимые настройки и запускаются необходимые драйвера и резидентники. Наконец, прямо из AUTOEXEC.BAT (или пользователем вручную) запускается стартовая программа WIN.COM, которая в свою очередь, реорганизует память и грузит три главных 16-битовых модуля Windows 3.Х:

Важной особенностью модуля KERNEL является то, что большую часть функций он выполняет не сам, а обращается "за помощью" к никуда не девшимся DOS-овским модулям. Разумеется, какое-нибудь распределение памяти при помощи функций 48h/49h в Windows 3.x совершенно неактуально, там просто структура адресов совершенно другая. А вот файловые операции практически полностью выполняются средствами MSDOS, и значит, в конечном итоге все системные вызовы API обращаются к прерывание Int21.

При разработке Windows 95 фирма Microsoft постарались соблюсти, хотя бы внешне, основные принципы более младшей версии. Порядок загрузки сохранился прежним... только вот Windows разучилась стартовать из-под любой версии DOS, ей теперь подавай одну-единственную, заточенную под нее и поставляемую вместе с ней в общем дистрибутиве версию 7.X. Версия 7.0 (из-под Windows 95) понимает длинные имена файлов, а версия 7.1 (из-под Windows 98/ME) сверх того знакома с FAT32. Можно "расщепить" систему на DOS и графическую оболочку, но можно и запускать эмулятор DOS из-под графической оболочки. Модули KERNEL, USER и GDI теперь присутствуют в виде двух вариантов: 16-битового и 32-битового, например, KERNEL16 и KERNEL32. Часть драйверов (обработка клавиатуры, низкоуровневый доступ к винчестеру и пр.) являются 16-разрядными. Но настоящее "сердце" Windows 9X - это менеджер виртуальных машин VMM, ядро, отвечающее за низкоуровневую поддержку распределения физической памяти и исполнение процессов. В Windows 9X можно обнаружить несколько "слоев" системного сервиса: например, для чтения файла можно обратиться к высокоуровневым функциям API32 (экспортируемым из KERNEL32.DLL), можно - к эмуляции DOS-функций (доступны не при помощи INT21, но работающие очень похоже), или - читать файлы посекторно, обращаясь напрямую к дисковым драйверам Windows при помощи механизмов VxDCall/VMMCall... Есть еще кое-какие способы, они используются самой операционкой и никогда не были и не будут документированы, и в вирусах используются редко.

Наконец, Windows NT/2000. Отдельная операционная система, стартующая без DOS (довольно упрощенный эмулятор DOS запускается из-под графической оболочки). Распределением системной памяти под процессы по-прежнему "заведует" VMM, точнее, наоборот: VMM для Windows 9X является упрощенной версией NT-шного. Весь код 32-разрядный, 16-битовых кусков нет. Системный сервис (практически недокументированный) "сконцентрирован" в-основном в NTDLL.DLL, но для совместимости с другими 32-разрядными приложениями существует "переходник", который также имеет название KERNEL32.DLL.

6.2. Windows и "старые" вирусы

6.2.1. Загрузочные вирусы

Windows 3.X не использует своих процедур для доступа к дисковым секторам (правда, в Windows 3.11 вроде бы был флажок "32-битный доступ к диску", но он подчас и без вирусов работал криво), а пользуется сервисом Int13h. Поэтому загрузочные вирусы в большинстве случаев будут жить и, главное, размножаться по-старому.

Windows 9X для доступа к дискетам всегда использует свои, 32-битные процедуры, никак не связанные с BIOS. А вот к винчестеру может обращаться и через свои процедуры, и через Int13h. Во второй режим она переключается, если при старте обнаруживает какой-либо нестандартный, не-BIOS-овский обработчик Int13h. Это сделано для совместимости с драйверами "больших" дисков типа Ontrack DiskManager, сидящими в MBR аналогично вирусам. Кстати, это может быть и настоящий вирус, операционка "расшаркается" и перед ним. :-) Но это составит лишь моральное удовлетворение для загрузочной "козявки", ибо заражать вставляемые дискеты она все равно не сможет. Итак, загрузочный вирус под Windows 9X живет и здравствует в MBR и BOOT-секторах винчестера, в нужный момент он исправно нарисует при загрузке какую-нибудь картинку, сыграет музычку или нагадит на винчестер (ведь он стартует раньше операционки!), но... вот с "половой жизнью" у него сплошной облом. Впрочем, некто И. Коваль придумал и опубликовал в книжке "Как написать компьютерный вирус" довольно интересный алгоритм, позволяющий загрузочному вирусу изредка... далеко не всегда... с большими трудностями... но все-таки, размножаться под Windows 9X. Идея:

Пока загрузочный вирус И. Коваля уникален, в антивирусных базах он известен под именем Babec.

Windows NT/2000 содержит исключительно 32-битовые собственные драйвера, шанса размножиться у вируса нет никакого. Но в загрузочном секторе он может оставаться годами, и по-прежнему способен в момент перезагрузки "пошутить".

6.2.2. Нерезидентные вирусы

Пожалуй, на них смена операционной среды практически никак не повлияла. Во вcех версиях Windows присутствует более или менее адекватная эмуляция DOS, сопровождающаяся загрузкой COMMAND.COM, моделированием таблицы вектров прерываний, наличием ядра DOS c обработчиками прерывания Int21 и пр. Вирус успешно стартует из зараженой программы, найдет свои жертвы при помощи 4Eh/4Fh и заразит их. Более того, в "настоящем" MS-DOS юзер мог позабыть прописать в файле AUTOEXEC.BAT "тропинку", а в Windows 9X об этом позаботится сама система, добавив по умолчанию что-то вроде:

SET PATH = C:\WINDOWS;C:\WINDOWS\COMMAND

Какой роскошный подарок Vienna-подобным вирусам! Правда, объектов для заражения у вирусов стало существенно меньше. На винчестере полным-полно EXE-файлов... но на самом деле они имеют NE- или PE-формат, а их EXE-заглушка умеет только ругаться "This program can't run in DOS-mode". Заразить, конечно, можно, но из таких программ вирус вряд ли когда-нибудь повторно стартует.

Появилась еще одна ма-а-а-ленькая тонкость. В C:\WINDOWS\COMMAND лежат COM-утилиты с несколько необычной структурой. Они представляют собой совокупность нескольких "сегментов", связанных в кольцевой список. Каждый "сегмент" предваряется заголовком, содержащим специфическую сигнатуру, ссылку на заголовок следующего сегмента и некоторые другие параметры. Первый в списке заголовок укорочен, содержит всего два поля - сигнатуру и ссылку, и лежит... в конце файла. Сигнатура строится по следующему принципу: '***NS', где *** - код языка для локализованной версии Windows, например:

Пример дампа файла FORMAT.COM:

4A80:20610664-20746865-06207072-65737320 and then press
4A90:61067920-6B65790D-0A0034BD-04EFFE00 any key........
4AA0:00010000-00040000-01000000-00040000 ...............
4AB0:0100003F-00000001-0000000A-00000000 ...............
4AC0:00000000-00000000-00000000-00000000 ...............
4AD0:5255534E-053707                     RUSNS7.

Утилита, зараженная "старым" вирусом, не найдет в своем конце сигнатуры и будет работать некорректно. На момент написания этих строк существуют по крайней мере два вируса (Foo.956 и TD.1536), которые умеют заражать такие утилиты "правильно": копируют последние 7 байтов утилиты в конец и прибавляют к смещению длину вируса. Кстати, Foo не копирует их, а переносит. Антивирус обязан сделать при лечении обратную операцию, иначе после "лечения" в конце не окажется нужного заголовка, и утилита "сдохнет"!

Процедура "правильного" лечения может выглядеть примерно так:

    // Пример процедуры лечения ENUNS-вируса
    // (c) Климентьев К., Самара 2001
    int cure() {
     f = open(filename, O_RDWR|O_BINARY);
     lseek(f,(long)SavePos, SEEK_SET );
     read (f,SavedBytes,3); // "Спрятанные" байты
     lseek(f,0,SEEK_SET);
     write(f,SavedBytes,3);
     lseek(f,-7,SEEK_END);
     read (f,Signature,5);  // NS-сигнатура
     read (f,Ptr,2);        // Указатель на следующий заголовок
     lseek(f,-(long)VirLen,SEEK_END);
     write(f,Signature, 5);
     write(f,Ptr-VirLen, 2);
     write(f,0,0);          // "Усекновение" файла
     close(f);
    }

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

6.2.3. Резидентные вирусы

Если вирус заразил один из системных файлов и успел стартовать до Windows 3.x или 9X, то, скорее всего, его активация приведет к "гибели" системы. Ведь он не знает правил распределения памяти, характерных для Windows! (А NT его, вероятно, просто "раздавит" и не заметит, в момент своего старта перераспределив память и установив нужные режимы без оглядки на существующие обработчики прерываний).

С другой стороны, если вирусом заражена одна из программ, запускающихся из AUTOEXEC.BAT или CONFIG.SYS, то вирус будет установлен в памяти "справочной" копии виртуальной DOS-машины и сможет активироваться каждый раз при выполнении какой-либо DOS-программы. Дело в том, что в первом мегабайте ОЗУ живет кусочек conventional-памяти с векторами прерываний, BIOS-ом и всеми резидентными утилитами, загруженными в конфигурационных файлах. Если там прописан KEYRUS, то он исправно будет активироваться во всех DOS-сессиях. А вирус, он - что, рыжий?

Наконец, если резидентный вирус в первый раз стартовал уже во время DOS-сессии, то он будет жить в памяти только пока она активна. Поскольку NT/2000 не обращает внимания на AUTOEXEC и CONFIG, то здесь этот случай также актуален, но только он один.

6.3. Вирусы, заражающие NE-программы

NE-формат программных файлов первоначально был разработан для OS/2, но нам широко известен прежде всего, как основной формат для 16-битовых Windows-программ. Не следует думать, что этот формат используется только в Windows 3.X. Половина утилит в каталоге C:\WINDOWS для Windows 9X - 16-битовые, включая любимый всеми "солитёр" SOL.EXE, альтернативный менеджер задач PROGMAN.EXE и пр. Корректно написанные NE-программы компактны, быстродействующи (если редко обращаются к системному сервису) и прекрасно работают в любой версии Windows.

6.3.1. Структура NE-программы

(Данный раздел - сокращенный фрагмент моей статьи "Заражение во благо", опубликованной в ZF4)

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

This program requires Microsoft Windows only

и завершаться. MS-DOS при старте NE-программы увидит и запустит только эту "заглушку" (больше он ничего и не умеет видеть и запускать). Windows же смотрит глубже и знает, где внутри подобных файлов искать подлинный програмный код.

Делается это так. В EXE-заголовке cлово по смещению 0x18 для NE-программ содержит число, равное (или большее) 0x40. Это - признак не-DOS-программы. Для таких программ по адресу 0х3С располагается указатель на специфический заголовок NE-файла. Довольно часто между MZ-заголовком и NE-заголовком имеется пустое пространство.

Структура NE-заголовка ("неинтересные" для нас поля пропущены):

struct NEhdr
{
 WORD  NE;             // +0   0x454E = 'NE'
 BYTE  linkerVersion;  // +2   версия компоновщика
 BYTE  linkerRevision; // +3   ревизия компоновщика
 WORD  entryTabOffset; // +4   смещение таблицы точек входа
 WORD  entryTabLen;    // +6   длина таблицы точек входа
 DWORD reserved1;      // +8   ???
 WORD  exeFlags;       // +0CH биты описания исполняемого кода
 WORD  dataSegNum;     // +0EH число сегментов "автоданных"
 WORD  localHeapSize;  // +10H исходный размер локального хипа
 WORD  stackSize;      // +12H -"- стека
 WORD  NE_IP;          // +14H смещение в сегменте точки входа
 WORD  NE_CS;          // +16H индекс сегмента точки входа
 WORD  NE_SP;          // +18H смещение в стековом сегменте
 WORD  NE_SS;          // +1AH индекс стекового сегмента (с 1)
 WORD  segTabEntries;  // +1CH к-во элементов в таблице сегментов
 WORD  modTabEntries;  // +1EH -"- в таблице вхождений
 WORD  nonResTabSize;  // +20H -"- в таблице нерезидентов
 WORD  segTabOffset;   // +22H смещ. до табл. сегментов от NEHdr
 WORD  resTabOffset;   // +24H -"- до таблицы ресурсов
 WORD  resNameTabOffset// +26H -"- до таблицы имен ресурсов
 WORD  modTabOffset;   // +28H -"- до таблицы модулей
 WORD  impTabOffset;   // +2AH -"- до таблицы импорта
 WORD  nonResTabOffset;// +2CH -"- до таблицы нерезидентов
 WORD  reserved2;      // +2EH ???
 WORD  numEntryPoints; // +30H к-во перемещаемых точек входа
 WORD  shiftCount;     // +32H Log(SegSiz,2)
 WORD  numResourceSegs;// +34H число ресурсных сегментов
 BYTE  targetOS;       // +36H код операционной системы
 BYTE  miscFlags;      // +37H прочие биты описания программы
 WORD  fastLoadOffset; // +38H смещение области быстрой загрузки
 WORD  fastLoadSize;   // +3AH размер области быстрой загрузки
 WORD  reserved3;      // +3CH ???
 BYTE  winRevision;    // +3EH текущая версия Форток
 BYTE  winVersion;     // +3FH текущая ревизия Форток
};

Поле segTabOffset помогает получить доступ к таблице сегментов, где под сегментом понимается фрагмент программы, хранящий информацию определенного типа: код или данные. Обычно таблица сегментов лежит сразу за NE-заголовком.

Эта таблица содержит segTabEntries записей следующего вида:

struct tagTBSEGMENT
{
 WORD segDataOffset;  // +00 смещение сегмента в логических
                      //     секторах _от_начала_файла_ !!!
 WORD segLen;         // +02 длина сегмента в байтах;
 WORD segFlags;       // +04 слово описания сегмента;
 WORD segMinSize;     // +06 резервируемая под сегмент память,
                      //     0 означает максимум: 64Кб.
};

Логический сектор - это единица измерения внутри NE-программы, двоичный логарифм его размера хранится в поле shiftCount (обычно это 9, что соответствует 512-байтовой длине). Позиции всех сегментов в NE-файле выровнены по логическим секторам.

О поле segFlags этой таблицы можно сказать чуть-чуть подробней:

Поля NE_IP и NE_CS в NE-заголовке описывают положение точки входа в NE-программу. NE_CS - это индекс строки в таблице tagTBSEGMENT (нумерация с 1). NE_IP - смещение в этом сегменте. Вот как найти в файле точку входа в программу. Строка с описанием кодового сегмента располагается в позиции:

(NE_CS-1)*8+segTabOffset+winInfoOffset

Прочитав в этой строке поле segDataOffset, получаем файловый адрес точки входа:

(1<<shiftCount)*segDataOffset+NE_IP.

В самом конце кодового сегмента иногда (если установлен восьмой бит поля segFlags таблицы tagTBSEGMENT) лежат длина Relocation Table (одно слово) и сама Relocation Table.

Структура записи этой таблицы такова:

struct tagRELOCATEITEM
{
 BYTE addressType;    // +00 способ задания адреса ссылки
 BYTE relocationType; // +01 тип настроечной ссылки
 WORD itemOffset;     // +02 смещение в сегменте до релокейшена
 WORD index;          // +04 индекс в таблице ссылок,
                      //     либо номер сегмента
 WORD extra;          // +06 порядковый номер функции,
                      //     либо смещение в сегменте
}

Очень важную роль релокейшены играют при межсегментных переходах. В код команд JMP XXXX:YYYY и CALL XXXX:YYYY необходимо в процессе загрузки программы в память поместить конкретные значения сегмента и смещения. Если в сегменте присутствует несколько вызовов одной и той же внешней процедуры, то в Relocation Table имеется строка только для описания одного, самого первого релокейшена. Каждый релокейшен указывает на следующий, тот в свою очередь - на следующий, признак последнего - число 0xFFFF.

Единственный релокейшен должен сразу иметь значение 0xFFFF.

6.3.2. Способы заражения NE-программ

Долгое время считалось, что заражение Windows-программ невозможно. Видимо, в этом были уверены и вирусописатели, и "вирусочитатели" (Это слово Я изобрел! Прошу зарегистрировать копирайт!J), поэтому в 1990-1994 гг. количество более-менее успешных попыток было мизерным. А когда надежные технологии заражения появились, пришла пора Windows 95 и PE-программ.

Тем не менее, история NE-программ и NE-вирусов еще продолжается. Хотите примеров? Их есть у меня!

Vir_1_4 (1992). Пожалуй, самая ранняя пташка. По современным понятиям - "недовирус": внедряться в файл уже умел, а вот возвращать управление жертве - нет. Поэтому, после работы просто удалял себя из зараженного файла, и со второго раза жертва запускалась без проблем.

Cybertech (1993). Вирус применял довольно сложную технику патчинга (проповедник хакерства Крис Касперски говорит "паДчинг", ну и где же хоть одна буква "Д" в слове "patch" ?! J) файлов KERNEL286.DLL и KERNEL386.DLL. О "патчинге кернелов" - позже. Вируса этого в моей коллекции нет, поэтому ничего добавить не могу - читайте "Вирусную энциклопедию" от Касперского.

Tentacle (1995) разбирал NE-программы на "кубики" (сегменты), потом складывал их в новом порядке, добавляя свой код. Очень интересно и свидетельствует о глубоком проникновении в структуру NE-файла. Только вот вышло в результате медленно и нерационально.

Aep (1995). Первое использование действительно здравой идеи! Вирус Aep искал кодовый сегмент, сдвигал остаток файла вниз и приписывался в хвост к кодовому сегменту в пустое пространство. Существуют версии для OS/2 и Windows 3.X.

WinTiny. И снова 1995 год, июнь. Бета-версия "Чикаго" уже заюзана юзерами до дыр, до выхода официального релиза оставалось всего два месяца. Товарищ Burglar из Тайваня наконец-то догадался, что простейший способ заражения - раздвинуть таблицу сегментов и вставить в нее описатель для нового кодового сегмента, расположенного в конце файла, т.е. - для вируса. Коротко и ясно. С появлением этого вируса можно считать, что родился "стандартный" метод заражения NE-программ.

Vecna (он же Win.Bonk), 1998 год (очень во-время! :-). Vecna/29A аккуратно прописал обработчики прерывания 21h и получил вполне работоспособный резидентный вирус. Заражение выполнялось забавно - тело вируса "мертвым" куском приписывалось к хвосту файла, а в программу внедрялся 152-байтный загрузчик, который и "оживлял тело".

Видимо, новые NE-вирусы, если они будут появляться (а почему бы нет?) ориентироваться будут на алгоритм заражения, примененный в вирусе WinTiny. Этот алгоритм хорошо изучен и многократно опубликован, в том числе и на русском языке, в том числе и в бумажных книжках.

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

winInfoOffset-=8.

Раз NE-заголовок сместился, то отностительные смещения в нем "поплыли", надо их подправить:

    entryTabOffset+=8;
    resTabOffset+=8;
    resNameTabOffset+=8;
    modTabOffset+=8;
    impTabOffset+=8;
    nonResTabOffset+=8 (последнее не обязательно).

Кроме того, размер таблицы сегментов увеличится на одну запись:

segTabEntries +=1.

В освободившеся место таблицы вписывается новая строка - описатель для нового кодового сегмента. В ней устанавливается:

    segLen = Длина_инфектора_в_байтах;
    segFlags = 180h;
    segMinSize = Длина_инфектора_в_байтах.
    segDataOffset = (Выровненная_длина_файла) / shiftCount.

В NE-заголовке прописывается новая точка входа (старые значения полей сохраняются внутри вируса):

    NE_CS = segTabEntries;
    NE_OP = Смещение первой команды внутри вируса.

Для вирусного сегмента создается своя Relocation Table, чтобы он мог передать управление в оригинальный кодовый сегмент.

    nrelocs = 1 (это размер таблицы);

    addressType = 3;
    relocationType =4;
    itemOffset = Смещение релокейшена (поля адреса в JMP);
    index = старый NE_CS;
    extra = старый NE_IP,

Код вируса плюс Relocation Table дописываются в конец файла (не непосредственно в конец, а в позицию, выровненную на целое число логических секторов).

Исполняемая часть вирусного завершается командой JMP межсегментного перехода:

   
db 0EAh
dw 0000h
dw FFFFh

Давайте изучим методы анализа и лечения NE-заразы на примере вируса WinTiny. Сначала я приготовил к этой роли вирус Vecna (он же Win.Bonk)... но потом пожалел, и решил попридержать "лакомый кусочек". Пригодится в другой раз и в другом месте.

Дизассемблировать NE-программы лучше всего при помощи IDA. Но много пользы может принести и обычный HIEW одной из последних версий. (Кстати, для WinTiny легко доступен комментрованный исходник).

Наилучшее качество трассировки достигается, разумеется, при использовании WinICE, но только специальной 16-битовой версии под Windows 3.X. Альтернатива: инсталлируйте Borlanc C/ C++ v5.X, и вы получите в качестве "бесплатного" приложения TD, TDW (вот он-то и имеется в виду в данном случае) и TD32. Конечно, это - приложение 3-го кольца защиты, и какие-нибудь суперхитрые системные навороты оно не потянет... а имеются ли они внутри вируса класса WinTiny? Слава Богу, нет, и таковыми даже и не пахнет.

6.3.3. Схватить и казнить!

Вирус WinTiny ищет и заражает NE-программы в текущем каталоге. Возьмем в качестве "червяка" для ловли этого вируса обычного "солитёра" J, т.е. файл SOL.EXE длиной 171775 байт. Вот фрагменты его дампа.

   
000 4D 5A FF 00-50 01 00 00-04 00 00 00-FF FF 00 00 MZ-заголовок
010 B8 00 00 00-00 00 00 00-40 00 00 00-00 00 00 00
020 00 00 00 00-00 00 00 00-00 00 00 00-00 00 00 00
030 00 00 00 00-00 00 00 00-00 00 00 00-80 00 00 00
040 0E 1F BA 0E-00 B4 09 CD-21 B8 01 4C-CD 21 54 68               Тh
050 69 73 20 70-72 6F 67 72-61 6D 20 72-65 71 75 69 is program requi
060 72 65 73 20-4D 69 63 72-6F 73 6F 66-74 20 57 69 res Microsoft Wi
070 6E 64 6F 77-73 2E 0D 0A-24 00 00 00-00 00 00 00 ndows.
080 4E 45 05 3C-1E 05 01 00-00 00 00 00-02 03 0B 00 NE
090 00 10 00 10-94 00 08 00-00 00 0B 00-0B 00 04 00
                ^^^^^^^^^^^
               NE_CS и NE_IP
0A0 17 00 40 00-98 00 F8 04-FF 04 07 05-9F 05 00 00
0B0 00 00 04 00-00 00 02 08-5E 00 1E 01-00 00 00 04
0C0 60 00 0C 0B-70 1D 0C 0B-7C 01 0B 07-30 1D 0C 07
0D0 F7 01 3C 0B-30 1D 3C 0B-B4 02 FE 16-30 1D FE 16
0E0 2E 04 6B 0F-30 1D 6C 0F-2B 05 84 00-30 1D 84 00
0F0 35 05 50 04-30 1D 50 04-2C 01 AC 01-70 1D AC 01
080 4E 45 05 3C-1E 05 01 00-00 00 00 00-02 03 0B 00
090 00 10 00 10-94 00 08 00-00 00 0B 00-0B 00 04 00
0A0 17 00 40 00-98 00 F8 04-FF 04 07 05-9F 05 00 00
0B0 00 00 04 00-00 00 02 08-5E 00 1E 01-00 00 00 04
0C0 60 00 0C 0B-70 1D 0C 0B-7C 01 0B 07-30 1D 0C 07
...
100 86 05 D8 02-30 1D D8 02-BE 05 B8 08-30 1D B8 08
110 4C 01 B6 02-51 0C B6 02-04 00 0E 80-01 00 00 00

   После заражения файл "поправился" на 742 байта. Вот та же область
   файла, зараженного вирусом WinTiny.
   
000 4D 5A FF 00-50 01 00 00-04 00 00 00-FF FF 00 00 MZ
010 B0 00 00 00-00 00 00 00-40 00 00 00-00 00 00 00
020 00 00 00 00-00 00 00 00-00 00 00 00-00 00 00 00
030 00 00 00 00-00 00 00 00-00 00 00 00-78 00 00 00
                                        ^^^^^
                           Адрес NE-заголовка
040 0E 1F BA 0E-00 B4 09 CD-21 B8 01 4C-CD 21 54 68               Th
050 69 73 20 70-72 6F 67 72-61 6D 20 72-65 71 75 69 is program requi
060 72 65 73 20-4D 69 63 72-6F 73 6F 66-74 20 57 69 res Microsoft Wi
070 6E 64 6F 77-73 2E 0D 0A-4E 45 05 3C-26 05 01 00 ndows.NE
                            ^^^^^
                            NE смещен !!!!
080 00 00 00 00-02 03 0B 00-00 10 00 10-00 00 0C 00
                                        ^^^^^^^^^^^
                                Новые NE_CS и NE_IP
090 00 00 0B 00-0C 00 04 00-17 00 40 00-A0 00 00 05
0A0 07 05 0F 05-9F 05 00 00-00 00 04 00-00 00 02 08
0B0 5E 00 1E 01-00 00 00 04-60 00 0C 0B-70 1D 0C 0B
                            ^^^^^^^^^^^^^^^^^^^^^^^
                     Начало таблицы сегментов
0C0 7C 01 0B 07-30 1D 0C 07-F7 01 3C 0B-30 1D 3C 0B
...
100 BE 05 B8 08-30 1D B8 08-4C 01 B6 02-51 0C B6 02
110 F0 29 DB 02-80 01 DB 02-04 00 0E 80-01 00 00 00
    ^^^^^^^^^^^^^^^^^^^^^^^
    Описатель вирусного сегмента

   Вот дизассемблированное начало кода вируса.
   
; Disassembled & commented by Constantin E. Climentieff
0C0000 9C         pushf
0C0001 60         pusha
0C0002 1E         push  ds
0C0003 06         push  es
0C0004 B88616     mov   ax,01686   ; Доступен ли
0C0007 CD2F       int   02F        ; сервис DPMI?
0C0009 0BC0       or    ax,ax      ; Если да, то
0C000B 7409       je    .0000C0016 ;  продолжаем
0C000D 07         pop   es
0C000E 1F         pop   ds
0C000F 61         popa
0C0010 9D         popf
0C0011 EA0000FFFF jmp   0FFFF:00000 ; Возврат в программу
0C0016 ....

Собственно говоря, этого вполне достаточно - мы обнаружили фрагмент возврата управления программе-носителю. Это команда по смещению 0С0011 - межсегментный переход, содержащий внутри своего кода релокейшен.

Стоит упомянуть, что у вируса WinTiny имеются проблемы с размещением рабочих переменных, ведь кодовый сегмент нельзя видоизменять! Поэтому, используя интерфейс DPMI, вирус выделяет себе рабочее пространство, копирует в него часть своего кода... в общем, не так уж все и безоблачно. Но нас это не должно волновать.

И вот самый конец зараженного файла, фрагмент вируса, в котором располагается Relocation Table со старыми значениями NE_CS и NE_IP внутри.

   
1В0 65 69 2С 20-54 61 69 77-61 6У 2У 01-00 03 04 12 ei, Taiwan.
1С0 00 08 00 94-00
       ^^^^^^^^^^^
       Старые NE_CS и NE_IP

Теперь можно писать антивирус. Достаточно разработать две процедуры:

Процедура распознавания вируса WinTiny в файле основана на классическом принципе сравнения сигнатур. В качестве сигнатуры возьмем первые 7 байтов вирусного кода:

0x9C 0x60 0x1E 0x06 0xB8 0x86 0x16

Функция infected() вычисляет по данным из NE-заголовка позицию кодового сегмента и точки входа внутри нее, затем считывает оттуда первые байты и сравнивает с сигнатурой.

 struct NEhdr  nh;
 DWORD  p;

// (c) Климентьев К., Самара 2001
infected( char *s )
{
 int f; WORD w; BYTE sigbuf[8];
 struct tagTBSEGMENT tb;

 f = open(s,O_BINARY|O_RDONLY);
 read(f,&w,2);
 if (w!=0x454E) {close(f); return GOOD;}
 lseek(f,0x18,SEEK_SET); read(f,&w,2);
 if (w<0x40) {close(f); return GOOD;}
 lseek(f,0x3C,SEEK_SET); read(f,&w,2);
 lseek(f,(long)w,SEEK_SET); read(f,&nh,sizeof(nh));
 if (nh.NE!=0x4550) {close(f); return GOOD;} p = (long)w;
 lseek(f,(long)((nh.NE_CS-1)*8+nh.segTabOffset+p),SEEK_SET);
 read(f,&tb, sizeof(tb));
 lseek(f,(long)(1<<nh.shiftCount)*tb.segDataOffset+nh.NE_IP, SEEK_SET);
 read(f, sigbuf, sizeof(sigbuf));
 if ((sigbuf[0]!=0x9C)||(sigbuf[1]!=0x60)||
     (sigbuf[2]!=0x1E)||(sigbuf[3]!=0x06)||
     (sigbuf[4]!=0xB8)||(sigbuf[5]!=0x86)||
     (sigbuf[6]!=0x16))
                       {close(f);return GOOD;}
 close(f); return BAD;
}

Процедура cure() читает из конца вируса (из Relocation Table) сохраненные поля, описывающие правильную точку входа и восстанавливает из в заголовке. Сам вирусный код после этого можно уже не удалять.

   
// (c) Климентьев К., Самара 2001
cure(char  *s)
{
 int f;
 f = open(s,O_BINARY|O_RDWR);
 lseek(f,-4,SEEK_END);
 read(f,&nh.NE_CS,2);
 read(f,&nh.NE_IP,2);
 lseek(f,p,SEEK_SET);
 write(f,&nh,sizeof(nh));
 close(f);
}

6.4. Вирусы, заражающие PE-программы

PE-формат был разработан для 32-битовых операционных систем фирмы Microsoft по мотивам Unix-овского COFF-формата. Является основным для Windows NT и Windows 9X.

6.4.1. Структура PE-программы

(Данный раздел - сокращенный фрагмент моей статьи "Заражение во благо", опубликованной в ZF4)

Как и в случае NE-программ, для PE-программ:

Отличие в том, что специфический заголовок PE-файла содержит в начале сигнатуру 'PE', а не 'NE'. Вот полная структура этого заголовка:

   
struct PEhdr
{
 DWORD PE;         // +00  Сигнатура
 WORD MachType;    // +04  Тип процессора
 WORD NOfSections; // +06  Количество секций
 DWORD TimDat;     // +08  Время/дата создания
 DWORD PSymTable;  // +0СH Адрес таблицы символов
 DWORD NOfSymbols; // +10H К-во строк в таблице символов
 WORD SzOfOptHdr;  // +14H Размер переменной части заголовка
 WORD Flags;       // +16H Флаги
 WORD R1;          // +18H ???
 BYTE MajorLnkV;   // +1AH Старшая версия линкера
 BYTE MinorLnkV;   // +1BH Младшая версия линкера
 DWORD SizeOfCode; // +1CH Размер исполняемого кода
 DWORD SizeOfInD;  // +20H Размер иниц-ных данных
 DWORD SizeOfUnInD;// +24H Размер неиниц-ных данных
 DWORD EntryPoint; // +28H Адрес точки входа
 DWORD BaseOfCode; // +2CH Смещ. кода в памяти
 DWORD BaseOfData; // +30H Смещ. иниц-ных данных в памяти
 DWORD ImBase;     // +34H RVA отобpажения файла в память
 DWORD SectAlign;  // +38H Фактор выравнивания объектов в ОЗУ
 DWORD FileAlign;  // +3CH Фактор выравнивания объектов в файле
 WORD MajorOSV;    // +40H |
 WORD MinorOSV;    // +42H |
 WORD MajorImV;    // +44H |
 WORD MinorImV;    // +46H +> Версии и субверсии компонентов
 WORD MajorSSV;    // +48H |
 WORD MinorSSV;    // +4AH |
 DWORD Win32Vers;  // +4CH |
 DWORD SizeOfIm;   // +50H Размер образа программы в ОЗУ
 DWORD SizeOfHd;   // +54H Размеp заголовка и таблицы объектов
 DWORD CSum;       // +58H Контpольная сумма
 WORD SubS;        // +5CH
 WORD ProcFlags;   // +5EH
 DWORD SizeOfStR;  // +60H
 DWORD SizeOfStC;  // +64H
 DWORD SizeOfHpR;  // +68H
 DWORD SizeOfHpC;  // +6CH
 DWORD LoaderFlags;// +70H
 DWORD NrOfRVAs;   // +74H
 DWORD ExRVA;      // +78h RVA таблицы экспорта
 DWORD ExSize;     // +7Ch размер таблицы экспорта
 DWORD ImRVA;      // +80h RVA таблицы импорта
 DWORD ImSize;     // +84h размер таблицы импорта
 DWORD RsRVA;      // +88h RVA таблицы ресурсов
 DWORD RsSize;     // +8Ch размер таблицы ресурсов
 DWORD ExcRVA;     // +90h RVA таблицы исключений
 DWORD ExcSize;    // +94h размер таблицы исключений
 DWORD ScRVA;      // +98h RVA таблицы безопасности
 DWORD ScSize;     // +9Ch размер таблицы безопасности
 DWORD FURVA;      // +A0h RVA таблицы настроек
 DWORD FUSize;     // +A4h размер таблицы настроек
 DWORD DbRVA;      // +A8h RVA таблицы отладочной инф.
 DWORD DbSize;     // +ACh размер таблицы отладочной инф.
 DWORD IDRVA;      // +B0h RVA строки описани модуля
 DWORD IDSize;     // +B4h размер строки описания модуля
 DWORD MhRVA;      // +B8h RVA таблицы описания процессора
 DWORD MhSize;     // +BCh размер таблицы описания процессора
 DWORD TLRVA;      // +C0h RVA области данных цепочек
 DWORD TLSize;     // +C4h размер области данных цепочек
 DWORD LCRVA;      // +C8h RVA таблицы параметров загрузки
 DWORD LCSize;     // +CCh размер таблицы параметров загрузки
 DWORD R2[2];      // +D0h ???
 DWORD IARVA;      // +D8h RVA ???
 DWORD IASize;     // +DCh размер ???
 DWORD R3[2];      // +E0h ???
 DWORD R4[2];      // +E8h ???
 DWORD R5[2];      // +F0h ???
};

PE-программы - это набор секций (еще их называют "сегментами" или "объектами"), среди которых можно отметить:

NE-программы тоже были разбиты на сегменты, но в них за каждым сементом жестко была закреплена его роль: например, если сегмент был определен в NE-заголовке заголовке как кодовый, то после загрузки в память в дескрипторе его были "каленым железом" прописаны биты "Execute" и "Readonly". Для PE-программ же свойства секций могут быть выставлены какие угодно простым изменением битовых флагов в таблице описания секций.

Под каждую секцию PE-файла на диске и PE-программы в памяти выделяется целое число блоков фиксированной длины. Длины блоков - поля FileAlign и SectAlign в PE-заголовке, соответственно. Даже если секция содержит всего один байт полезной информации, под нее все равно будет выделен целый блок.

Загрузчик Windows чисто формально берет секции на диске и помещает их в оперативную память (начиная с адреса ImBase), модифицируя только отдельные адреса. Это означает, что в оперативной памяти по адресу ImBase можно обнаружить сигнатуру 'MZ', потом код "заглушки", потом PE-заголовок и пр. Все заголовки, справочные таблицы и структуры, имеющиеся в файле, присутствуют и в памяти! (Они слегка по другому заполнены и располагаются "посвободней", с шагом, кратным SectAlign).

Вот формат таблицы описания секций (она лежит сразу после заголовка, длина заголовка = 18h + SzOfOpеHdr):

   
struct PEObjTbl
 {
  BYTE  ObjName[8]; // +00  Символьное  имя  секции
  DWORD VirtSize;   // +08  Размер секции в памяти
  DWORD VirtRVA;    // +0СH Смещение секции от ImBase
  DWORD PhSize;     // +10H Размер секции на диске
  DWORD PhOffset;   // +14H Смещение секции от начала файла
  DWORD R1[3];      // +18H ???
  DWORD ObjFlags;   // +24H Флаги свойств секции
};

В поле ObjFlags таблицы хранятся битовые флаги свойств секции:

VirtSize - это реальное количество "полезных" байтов в секции, а PhSize - округленное до FileAlign.

Как найти положение точки входа в файле? Надо просканировать таблицу объектов до тех пор, пока не будет выполнено условие:

(EntryPoint >= VirtRVA) && (EntryPoint < VirtRVA+VirtSize).

Это даст информацию о том, в какой именно секции располагается объект. Кстати, некоторые объекты, например, точка входа в вирус, могут располагаться до первой секции!

По параметрам этой секции вычисляем позицию первой исполняемой команды на диске:

EntryPoint - VirtRVA + PhSize.

6.4.2. Способы заражения PE-программ

Первый в истории PE-вирус Bizatch/Boza. В конец файла дописывается дополнительная секция с кодом вируса. К таблице секций добавляется еще одна запись (например, с именем типа .vlad) и флагом свойств 0C0000040h. Модифицируется PE-заголовок:

Jacky. В конец файла дописывается код вируса. Считая, что код вируса принадлежит последней секции, значение ее размеров в таблице секций увеличивается (с учетом выравнивания на FileAlign и SectAlign). Модифицируются флаги свойств секции: 0F0000040h. PE-заголовок модифицируется так же, как и в Способе 1 (разумеется, SizeOfHd и NOfSections трогать не стоит).

CIH (Чернобыльский). Начало кода вируса записывается в позицию между концом PE-заголовков и началом первой секции. Туда же указывает EntryPoint. По мере возможности куски кода вируса записываются в пустые "хвосты" секций, а остаток - в конец последней секции.

Маленькие вирусы. Код инфектора непрерывным куском помещается в неиспользуемый "хвост" кодовой секции. Достаточно модифицировать только значение виртуальной длины этой секции в таблице секций и точку входа. Длина файла не изменяется.

6.4.3. Доступ к системному сервису

Чтобы манипулировать файлами, памятью и пр., вирусу необходимо обращаться к соответствующим средствам, предоставляемым операционной системой, т.е. к системному сервису. Но большая часть реальных возможностей системы (прямых вызовов менеджера виртуальных машин) от прикладных программ скрыта, а тот сервис, который все-таки документирован (Win32 API), по умолчанию не общедоступен. В Windows 9X/NT действует тоталитарно-бюрократический принцип: приложения обязаны при своем создании (при компиляции и компоновке) задекларировать те функции, которыми собираются пользоваться - внести их в свою таблицу импорта. В свою очередь, динамические библиотеки DLL, содержащие код сервисных функций, предоставляют только те из них, которые описаны в таблицах экспорта. Вирус, являющийся нелегальным кодом, может воспользоваться чужими сервисами лишь чисто случайно, если перечень импортированных функций удовлетворяет запросам вируса. Наиболее часто для получения доступа к желаемым системным сервисам вирусы используют следующие приемы.

6.4.3.1. Доступ к API32 по предопределенным адресам

Для конкретной версии Windows 9X/NT положение в адресном пространстве динамической библиотеки KERNEL32.DLL и адреса функций внутри нее - фиксированы. Далее для справки приведены адреса нескольких "ключевых" функций, которые с большой вероятностью будут использованы вирусом - этого вам достаточно для опознания их в дизассемблированном листинге:

Windows KERNEL32ExitProcess _lopen CreateFileA GetProcAddress
98SE BFF70000BFF8D4F8 BFF774BA BFF77ADB BFF76DA8
NT4WS/SP5 77F0000077F19FB2 77F12569 77F10B0E 77F13FCA
2K/SP1 77E8000077E9B0BB 77E9D72B 77E92B8D 77E9564B
2K/SP2 77E8000077E98F94 77E9602F 77E86F87 77E89AC1
W95 BFF70000BFF8AFDD BFF772B7 BFF77817 BFF76C18
NT4SR/SP6 77F0000077F19FE6 77F125BA 77F10B5F 77F14010

Разумеется, вирусы, использующие жестко фиксированные адреса, способны работать лишь в конкретной версии Windows. Вот простой пример кода такого вируса (Murkry.399):

   
; Disassembled & commented by Constantin E. Climentieff
401000 push      eax     ; В EAX := ImBase (=400000h)
401001 xchg      edi,eax ; Теперь ImBase также и в EDI
...    ...
40105F xor  eax,eax      ; Параметры - в стек:
401061 push eax          ; - hTemplateFile
401062 push eax          ; - dwFlagsAndAttributes
401063 push 003          ; - dwCreationDisposition
401065 push eax          ; - lpSecurityAttributes
401066 push eax          ; - dwShareMode
401067 push 0C0000000    ; - dwDesiredAccess
40106C lea  eax,[ebp][0003C]
40106F push eax          ; - lpFileName
401070 lea  eax,[edi][000000175]
401076 call d,[eax]      ; Вызов API-функции
401078 cmp  eax,-001
40107B jne  .00040107
40107D retn
...    ...
401175 dd   BFF77817    ; Адрес CreateFileA

Более продвинутые их собраться должны содержать в себе и использовать табличку типа вышеприведенной (разумеется, более полную).

6.4.3.2. Поиск адресов функций API32

Наиболее широко в Win9X-вирусах распространена техника прямого поиска нужных адресов функций в области памяти, занимаемой KERNEL32.DLL.

Первым делом вирус должен обнаружить в памяти саму KERNEL32. Эта библиотека имеет 'MZ' в начале, все типичные для PE-файла заголовки, таблицы экспорта и импорта и т.п., поэтому ее можно найти прямым сканированием. Есть подходы, облегчающих этот поиск:

Далее вирус сканирует таблицу экспорта KERNEL32 и находит адреса требуемых функций. Формат таблицы экспорта:

   
struct ExpTab
 {
  DWORD Chs;        // +00 Какие-то характеристики
  DWORD TimDat;     // +04 Время/Дата
  DWORD Version;    // +08 Версия
  DWORD Name;       // +0C Адрес имени библиотеки
  DWORD Base;       // +10 Начальный номер функции (обычно, 1)
  DWORD NFunctions; // +14 адресов функций
  DWORD NNames;     // +18 имен функций
  DWORD *FAdr;      // +1C массива адресов функций
  DWORD *NAdr;      // +20 массива адресов имен
  DWORD *NOrd;      // +24 массива ординалов
 };

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

Обычно бывает достаточно найти адрес только GetProcAddress, и с помощью нее получить все остальные адреса.

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

   
B8 00 AD DE-00 FF E0 6A-00 E8 15 00-00 00 6D 6F  .............mo
72 74 5B 4D-41 54 52 69-58 5D 27 73-20 76 69 72  rt[MATRiX]'s vir
75 73 00 E8-1D 00 00 00-46 49 41 53-4B 4F 27 39  us.....FIASKO'9
39 20 2D 20-31 30 78 20-34 20 6E 6F-74 68 69 6E  9 - 10x 4 nothin
67 2E 2E 2E-00 6A 00 E8-B6 08 00 00-6A 00 E8 A9  g...............
08 00 00 47-65 74 50 72-6F 63 41 64-64 72 65 73  .GetProcAddres..
73 00 00 00-00 00 00 00-00 00 2A 2E-65 78 65 00  s.........*.exe.
00 00 00 00-00 00 00 00-00 00 00 00-00 00 00 00  ................
00 00 00 00-00 00 00 00-00 00 00 00-46 69 6E 64  ............Find
46 69 72 73-74 46 69 6C-65 41 00 46-69 6E 64 4E  FirstFileA FindN
65 78 74 46-69 6C 65 41-00 47 65 74-43 75 72 72  extFileA GetCurr
65 6E 74 44-69 72 65 63-74 6F 72 79-41 00 43 72  entDirectoryA Cr
65 61 74 65-46 69 6C 65-4D 61 70 70-69 6E 67 41  eateFileMappingA
00 4D 61 70-56 69 65 77-4F 66 46 69-6C 65 00 55   MapViewOfFile U
6E 6D 61 70-56 69 65 77-4F 66 46 69-6C 65 00 47  nmapViewOfFile G
65 74 46 69-6C 65 41 74-74 72 69 62-75 74 65 73  etFileAttributes
41 00 53 65-74 46 69 6C-65 41 74 74-72 69 62 75  A SetFileAttribu
74 65 73 41-00 43 72 65-61 74 65 46-69 6C 65 41  tesA CreateFileA
00 43 6C 6F-73 65 48 61-6E 64 6C 65-00 53 65 74   CloseHandle Set
46 69 6C 65-54 69 6D 65-00 53 65 74-46 69 6C 65  FileTime SetFile
50 6F 69 6E-74 65 72 00-53 65 74 45-6E 64 4F 66  Pointer SetEndOf
46 69 6C 65-00 53 65 74-43 75 72 72-65 6E 74 44  File SetCurrentD
69 72 65 63-74 6F 72 79-41 00 00 00-00 00 00 00  irectoryA

Следует только различать внутри дампа зараженного файла две группы текстовых строк с именами системных сервисов: 1) принадлежащую вирусу и содержащую имена в основном файловых функций; 2) принадлежащую программе и содержащую в том числе разнообразные имена функций общего назначения - ExitProcess, GetVersion и пр.

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

6.4.3.3. Прямые обращения к системному ядру

Если дизассемблировать код какой-нибудь API-функции, то часто можно обнаружить весьма странные обращения к прерываниям, например, к 20-му:

   
CD20 int  20h
0801 dw   0053h ; <- Номер сервиса
0100 dw   0001h ; <- Идентификатор драйвера

Это - не DOS-овский выход из COM-программы, это - обращения к "глубинному" системному сервису Windows 9X, к драверам устройств и менеджеру виртуальных машин. Например,

В свою очередь, каждый из драйверов поддерживает от 1-2 до сотни с лишним сервисов, типа (на примере VMM):

В файле VMM.INC пакета DDK определены макросы VMMCall, VxDCall и пр., позволяющие программистам-"драйверистам" обращаться к Int-ам и их параметрам комфортно, одной командой, например:

VMMCALL _PageAllocate.

Внутри NTDLL полным-полно вызовов прерывания 2Eh, это обращения к NT Native API - к сервисам специфического системного ядра Windows NT/2000.

Сами операционные системы при работе обращаются не к API, а именно к этим сервисам. Вот почему резидентные (и не только) Windows-вирусы так стремятся перехватить обращения к этим сервисам, как, бывалоча, делали это в DOS-овские времена с INT 21h и INT 13h.

Но обращаться к прерываниям из 3-го кольца защиты невозможно. Кроме того, существует множество неочевидных нюансов использования прямых вызовов. Чего стоит хотя бы тот факт, что операционная система после вызова "затирает" INT 20 совсем другим кодом!

Как вирусы решают эти проблему - см. ниже.

6.4.4. "Нерезидентные" вирусы

Самый простой тип Windows-вирусов, как правило, работающих в 3-м кольце защиты. Идея работы ничем не отличается от придуманной 15 лет назад. Это типичные Search-вирусы, самые продвинутые из которых даже сканируют дерево каталогов, а наиболее примитивные - всего лишь заражают в текущем. Используют сравнительно небольшое подмножество API-функций для обращения к файлам.

При анализе кода таких вирусов (и не только их), полезно иметь под рукой справочник по API-функциям и их параметрам. В качестве такового можно взять файл WINBASE.H из BC5 или VC6, где описаны Си-заголовки этих функций. Следует иметь в виду, что в коде вируса параметры будут помещены в стек в обратном порядке (пример для функции CreateFile см. выше - в 6.4.3.1).

Кроме того, в Win32 API для многих функций имеются симметричные пары с буквами 'A' и 'W; в конце имени, например: CreateFileA и CreateFileW. Варианты с 'A' воспринимают нормальные ASCII-шные строковые параметры, варианты c 'W' - Unicod-овские (видимо, когда имя файла записано на эльфийском языке J). Вирусописатели, разумеется, по-эльфийски nicht verstehen, и посему используют в-основном A-варианты.

Наконец, любопытно, что API32 имеет несколько вариантов файлового сервиса. Кроме "современных" CreateFile, ReadFile, WriteFile, CloseFile и пр., там присутствуют (и по-прежнему прекрасно работают!) вызовы в "старинном" стиле от Windows 3.X:

   
_lopen ( LPCSTR lpPathName, int  iReadWrite );
_lcreat( LPCSTR lpPathName, int  iAttribute );
_lread ( HFILE  hFile,  LPVOID lpBuffer,  UINTuBytes );
_lwrite( HFILE  hFile,  LPCSTR lpBuffer,  UINTuBytes );
_lclose( HFILE  hFile );
_llseek( HFILE  hFile,  LONG  lOffset,   int iOrigin );

Вирусы, обращающиеся к файлам при помощи функций API32, можно отследить при помощи утилиты FILEMON Теда Мирецки.

6.4.5. Переход в нулевое кольцо защиты

"Законый" путь в нулевое кольцо для пользовательской программы один - стать системным драйвером. Причем правила игры постоянно меняются - способы организации драйверов в Winows 95 одни, в Windows NT другие, в Windows 2000 третьи, а ведь грядет уже и Windows XP... Вирусы же считают себя "нормальными героями", каковые, как известно, "всегда идут в обход".

Первая группа методов связана с прямым использованием шлюзов перехода из кольца в кольцо, хранящихся в LDT или GDT. Вирус может модифицировать имеющийся шлюз, а может и создать свой. Например, вирус Yabram:

В результате управление получает фрагмент того же вируса, только привелегии у этого кода уже равны 0. Вся "шутка юмора" основана на том, что команда SGDT по правилам защищенного режима - не привелегированная. Кстати, это верно только для Windows 9X, а в NT/2000 она принудительно делается "привелегированной", и поэтому в этих операционках метод неработоспособен.

Вторая группа основана на подмене адреса обработчика исключения или прерывания в IDT. Как известно, для обработчиков система милостиво "включает" доступ в нулевое кольцо, значит, вирус должен стать в ее глазах таким обработчиком и сгенерировать ситуацию прерывания/исключения. Любопытно, что у большого числа вирусов программная реализация этого метода чуть ли не байт в байт слизнута с "Чернобыльского" вируса CIH. Поскольку CIH пойдет у нас "на закуску", то пришлось искать образец, в котором был бы реализован пусть и тот же алгоритм, но "своими словами". И вот, пожалуйста, фрагмент кода вируса Sign.2028:

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

Имеются и другие любопытные способы. Но они - экзотика.

6.4.6. "Резидентные" вирусы

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

6.4.6.1. Как вирус остается в памяти

Обычно вирус остается в памяти самым тривиальнейшим способом - стартовав как отдельная программа и став, таким образом, отдельным процессом. Разумеется, на практике не так все просто: при запуске вирус является частью зараженной программы, и должен не только "сесть" в память, но и вернуть управление жертве.

Первый выход - расщепить процесс на два потока при помощи

CreateThread( LPSECURITY_ATTRIBUTES lpThreadAttributes,
              DWORD dwStackSize,
              LPTHREAD_START_ROUTINE lpStartAddress,
              LPVOID lpParameter,
              DWORD dwCreationFlags,
              LPDWORD lpThreadId ),

причем в одном из них будет жить программа, а в другом - крутиться вирус. Когда программа завершится, все потоки "погаснут" вместе с ней.

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

Третий выход - внедриться не в точку входа программы, но в точку вызова какого-нибудь сервиса какой-нибудь заведомо "резидентной" библиотеки (например, KERNEL32). Когда программы будут обращаться к этому сервису, вирус "проснется". Например, довольно привлекательным для вируса будет внедрение в точку вызова CreateProcess.

6.4.6.2. Как вирус следит за другими программами

Первая возможность состоит в том, чтобы непрерывно в цикле отслеживать список активных процессов при помощи

   
/* Для Windows 9X/ME и 2000 и KERNEL32.DLL */
CreateToolhelp32Snapshot()
Process32First()
Process32Next()

   и
   
/* Для Windows NT и PSAPI.DLL */
EnumProcessModules()
GetModuleFileNameEx().

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

Вторая возможность вошла в "моду" после "Чернобыльского" вируса: перехватить при помощи недокументированного VMM вызова

   
; InstallFileSystemApiHook
; eax - новый адрес обработчика

int 20h
dw  67h ;
dw  40h ; IFSMgr

; eax - старый адрес обработчика

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

   
; [esp+01Ch] - адрес структуры IOREQ, где по смещению 0Сh -
;              адрес имени файла в Unicode;
; [esp+010h] - дисковое устройство;
; [esp+00ch] - код операции с файлом: 0-читать, 1-писать,
;              10-переместить указатель, 11-закрыть,
;              36-открыть.

После того, как обнаружено обращение к нужному файлу (например, к файлу с расширением .EXE), заражение файлов осуществляется при помощи

   
; Ring0_FileIO
; eax - код операции с файлом
;        0D500h - открыть/создать
;          ebx := 2
;          ecx := 0
;          edx := 1
;          esi := Адрес имени файла
;        0D600h - читать
;          ebx := handle
;          ecx := Количество читаемых байтов
;          edx := Позиция в файле
;          esi := Адрес буфера для чтения
;        0D601h - писать
;          ebx := handle
;          ecx := Количество записываемых байтов
;          edx := Позиция в файле
;          esi := Адрес буфера
;        0D700h - закрыть
;          ebx := handle

int 20h
dw  32h
dw  40h ; IFSMgr

;   После открытия/создания в eax возвращается handle

Все это возможно, естественно, только в нулевом кольце защиты.

6.4.7. Обуздание "Чернобыля"

Процесс анализа поведения вируса, исследования кода и написания антивируса рассмотрим на примере знаменитого Win95.CIH ("Чернобыля"). Про этот вирус очень много написано, но в-основном, лишь про его жуткую троянскую функцию, 26 апреля 1999 года порубавшую в капусту Flash-BIOS-ы нескольких миллионов компьютеров во всем мире.

"Чернобыль" до сих пор является законодателем мод среди вирусописателей. Несложно найти откомментированный (в том числе и по русски) авторский исходник версии Win95.CIH v1.4 длиной 1019 байтов. Комментариев полно, но они весьма поверхностны. Наверное поэтому очень многие (чуть ли не каждый второй!) вирусы, написанные после "Чернобыля", содержат в себе просто-напросто готовые куски из него.

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

Берем, например, CD "Все для С++. Том 2" выпуска осени 1998 г. На нем заражены 16 программ, практически, все файлы SETUP.EXE во всех дистрибутивах. А также и программа SHELL.EXE, запускаемая из AUTORUN.INF, - стоит только вставить CD в карман CDD, и, как говорится, VIRUS сразу STARTOVAL!

Скажем, "огромное, блин, спасибо" L создателям этого диска, извлечем файл SHELL.EXE в отдельный каталог и распялим его на операционном столе. Сестра, скальпель!

6.4.7.1. Как "Чернобыль" отличает Win9X от NT

Вирус работает только в Windows 9X, поэтому первая задача, которую он решает - определение типа операционной системы, в которой он работает. Это действие выполняется стандартным для вирусов перехватом Structured Excepion Handler (SEH).

С каждым потоком каждого процесса связана структурка под названием TIB - Thread Information Block (приведен начальный фрагмент):

   
struct TIB {
     DWORD NextSEH       // +00 Ссылка на следующий SEH
     DWORD CurrentHeader // +04 Адрес текущего SEH
     DWORD StackTop      // +08 Вершина стека
     DWORD StackBase     // +0C База стека
     ...
}

До TIB-а добраться очень просто, при старте программы на его сегмент указывает регистр FS. Соответственно, FS:[0] - это NextSEH, FS:[4] - это CurrentHeader и т.п.

SEH представляет собой цепочку обработчиков, причем голова цепочки определена в TIB. Вирус встраивается в цепочку "первым номером":

   
; Disassembled & commented by Constantin E. Climentieff
0320: 55         push ebp
; Адресация пока еще не занятого фрагмента стека
0321: 8D4424F8   lea  eax,[esp-0008]
; Замена адресов: fs:[0] <=> Адрес поля в стеке
0325: 33DB       xor  ebx,ebx
0327: 648703     xchg eax,fs:[ebx]
; Классическое вычисление текущего смещения
032A: E800000000 call 00000032F
032F: 5B         pop  ebx
; А это и есть адрес нового обработчика
0330: 8D4B42     lea  ecx,[ebx+00042]
; Вот только сейчас этим адресом инициализируются
; адресованные ранее поля в стеке
0333: 51         push ecx
0334: 50         push eax

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

   
; Disassembled & commented by Constantin E. Climentieff
; Старый SEH возвращается на свое место
0371: 33DB       xor  ebx,ebx
0373: 648B03     mov  eax,fs:[ebx]
0376: 8B20       mov  esp,[eax]
0378: 648F03     pop  fs:[ebx]
037B: 58         pop  eax
037C: 5D         pop  ebp
; Внимание!!! Возврат управления зараженной программе!
037D: 688C274000 push 00040278C ; RVA точки входа!
0382: C3         retn

На основании анализа этих коротких фрагментов можно сделать несколько важных выводов.

  1. Вирус не использует память для хранения своих временных данных, ибо область, в которой он сейчас живет, запрещена для записи. Поэтому немножко странным и сложным, но необходимым выглядят использование для этой цели стека. Если же какой-нибудь другой вирус стартует не из "межсекционного пространства", а из секции со сброшенным флагом ReadOnly, то необходимости в этом нет... Поэтому весьма забавно наблюдать, как многие вирусописатели с тупым упрямством воспроизводят в своих творениях именно этот (байт-в -байт!) образец перехвата SEH. J
  2. Вирус встраивает свой обработчик в SEH для того, чтобы предотвратить GPF (General Protection Fault) под Windows NT/2000. Дело в том, что использованная в вирусе процедура входа в 0-е кольцо защиты работоспособна только под Windows 9X.
  3. Наконец, (и это самое главное!) нам становится известен правильный адрес (точнее, RVA) точки входа в зараженную программу. Эта константа хранится внутри команды кода PUSH по смещению
    37Eh-320h=05Eh
    от первого вирусного байта. Этого вполне достаточно, чтобы написать лечилку.
6.4.7.2. Как "Чернобыль" переходит в Ring0

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

   
; Disassembled & commented by Constantin E. Climentieff
; Берется адрес IDT - таблицы прерываний/исключений
0335: 50         push eax
0336: 0F014C24FE sidt [esp-0002] ; В NT не сработает !!!
033B: 5B         pop  ebx
; В EBP помещается адрес дескриптора обработчика INT3
033C: 83C31C     add  ebx,01C
033F: FA         cli
; Дескриптор 6-байтовый, и модифицируется по частям
0340: 8B2B       mov  ebp,[ebx]
0342: 668B6BFC   mov  bp,[ebx-0004]
; Выполняется адресация на новый обработчик
0346: 8D7112     lea  esi,[ecx+00012]
0349: 56         push esi
; Вписывается 1-я половина адреса
034A: 668973FC   mov  [ebx-0004],si
034E: C1EE10     shr  esi,010
; Затем вторая
0351: 66897302   mov  [ebx+00002],si
0355: 5E         pop  esi
; И генерируется INT3
0356: CC         int  3

Обработчик прерывания INT3 получает от операционной системы привелегии 0-го кольца защиты... но он принадлежит вирусу! Таким образом, фрагмент кода вируса, адресованный командой по смещению 346, будет работать с системным уровнем привелегий.

6.4.7.3. Как "Чернобыль" перехватывает системные вызовы

Вирус заменяет обработчик системных обращений к файловой системе при помощи "секретного" сервиса InstallFileSystemApiHook (см. п. 6.4.6.2), не описанного даже в Interrupt List Ральфа Брауна.

; Адрес нового обработчика запросов к файловой системе
03B7: 8D87F7FCFFFF lea     eax,[edi+0FFFFFCF7]
03BD: 50           push    eax
03BE: CD2067004000 VxDcall 0040.0067 ; INT 20h/dd 400067h
; В EAX возвращается старый адрес, сохранить его
03C4: 0F23C0       mov     dr0,eax
; После вызова INT20 эта команда операционной системой
; модифицируется так, что теперь имеет вид
; CALL [Системный_Обработчик_InstallFileSystemApiHook]
; Из этой команды можно извлечь (и заменить на свой)
; адрес обработчика сервиса InstallFileSystemApiHook
03C7: 58           pop     eax
03C8: 8B4E3D       mov     ecx,[esi+0003D] ; Это 3C0h !!!
; Из кода команды CALL "вынимается" адрес
03CB: 8B11         mov     edx,[ecx]
03CD: 8950FC       mov     [eax-0004],edx
03D0: 8D40D6       lea     eax,[eax-002A]
; И заменяется на свой
03D3: 8901         mov     [ecx],eax
03D5: FA           cli

После выполнения этого фрагмента вирус перехватил на себя обработку двух сервисов VMM:

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

; Этот фрагмент вируса может физически располагаться
; в конце любой секции, для него следует вновь
; получить уникальное смещение
03D9: E800000000   call    0000003DE
03DE: 5B           pop     ebx
03DF: 83C324       add     ebx,024
; "Секретный" вызов "Отменить перехват обработчика"
;  обращений к файловой системе
03E2: 53           push    ebx
03E3: CD2068004000 VxDcall 0040.0068 ; INT 20h/dd 400068h
; Адрес старого обработчика при перехвате был сохранен
; (см. предыдущий фрагмент), поэтому к нему можно
; обратиться командой CALL
03E9: 58           pop     eax
03EA: FF742408     push    [esp+00008]
003E: FF53FC       call    [ebx-0004] ; Сохраненный адрес !
03F1: 59           pop     ecx
; Вновь перехватить вновь кем-то установленный обработчик.
; В результате вирусный обработчик по-прежнему остается
; в начале цепочки обработчиков.
03F2: 50           push    eax
03F3: 53           push    ebx
03F4: FF53FC       call    [ebx-0004]
03F7: 59           pop     ecx
03F8: 0F23C0       mov     dr0,eax
03FB: 58           pop     eax
03FC: 5B           pop     ebx
03FD: C3           retn

Обработчик обращений к файлам при вызове сканирует содержимое стека с параметрами вызова (см. п. 6.4.6.2) и ждет запроса на открытие какого-либа файла. Все остальные запросы перенаправляются старому обработчику по сохраненному адресу. Все это исключительно похоже на обработку DOS-вирусом прерывания Int21:

   
; Вычисление уникального смещения
403008: E800000000      call 40301C
40301C: 5E              pop  esi
40301D: 81C603030000    add  esi,000000303
; Обработчик уже занят заражением файла?
403023: F60601          test [esi],001
403026: 0F85F0010000    jne  00040501C
; Проверка стековых параметров вызова (см.  п. 6.4.6.2)
40302C: 8D5C2428        lea  ebx,[esp+00028]
403030: 833B24          cmp  [ebx],024  ; Открыть файл?
403033: 0F85DD010000    jne  000405016
 ....
6.4.7.4. Как "Чернобыль" отличает уже зараженную программу

Вирус читает (см. п. 6.4.6.2) заголовок EXE-программы, по смещению 3Сh ищет адрес PE-заголовка и считывает PE-сигнатуру с отступом в один байт:

   
4030CE: 33C0        xor  eax,eax
4030D0: B4D6        mov  ah,0D6            ; Код операции чтения
4030D2: 8BE8        mov  ebp,eax
4030D4: 33C9        xor  ecx,ecx
4030D6: B104        mov  cl,004            ; Кол-во байтов
4030D8: 33D2        xor  edx,edx
4030DA: B23C        mov  dl,03C            ; Смещение в файле
4030DC: FFD7        call edi               ; Читать
4030DE: 8B16        mov  edx,[esi]
4030E0: 4A          dec  edx               ; Смещение-1 !!!
4030E1: 8BC5        mov  eax,ebp
4030E3: FFD7        call edi               ; Повторно читать
4030E: 813E0050450  cmp  d,[esi],000455000 ; '\0PE\0' ?
4030B: 0F850A010000 jne  .0004031FB        ; Уже заражен

Win95.CIH полностью заполняет стартовым фрагментом своего кода пустое пространство между stub-заглушкой и PE-сигнатурой. Значит, в здоровом файле первый байт до сигнатуры 'PE\0\0' нулевой, а в больном там лежит последний байт "головы" вируса. Сам вирус именно так и проверяет, нужно ли заражать программу, или это уже сделано. Это обстоятельство может быть использовано для вакцинации файлов: достаточно записать в этот байт какое-либо ненулевое значение, и Win95.CIH не тронет такой файл.

6.4.7.5. Как "Чернобыль" заражает программы

Вирус сначала "собирает из кусочков" свою копию в области памяти с разрешенной записью. Затем он вносит в код определенные исправления:

Сканирует таблицу секций заражаемой программы и, если в "хвостах" секций имеется достаточно места, вписывает в них куски кода. Головной фрагмент помещается в пустое пространство между MZ-заголовком и PE-заголовком, точка входа в PE-заголовке модифицируется так, чтобы указывала не первую вирусную команду. В результате вирус может полностью уместиться в них, и длина файла не изменится! (Хотя это, конечно, случается не всегда).

6.4.7.7. Антивирус для "Чернобыля"

Итак, достаточно написать две процедуры:

Процедура распознавания вируса Win95.CIH в файле основана на классическом принципе сравнения сигнатур. В качестве сигнатуры возьмем первые 7 байтов вирусного кода:

0x55 0x8D 0x44 0x24 0xF8 0x33 0xDB

Для поиска точки входа в вирус воспользуемся тем фактом, что она лежит вне стандартных секций, в пустом пространстве между MZ-заголовком и PE-заголовком. Это пространство занимает не больше одного блока длиной SectAlign, значит для зараженного файла выполняется условие EntryPoint < SectAlign, а позиция точки входа в файле в точности равна EntryPoint.

   
struct PEhdr  ph;
DWORD  p;

// (c) Климентьев К., Самара 2001
infected( char *s )
 {
  int f; WORD w; BYTE sigbuf[8];

  f = open(s,O_BINARY|O_RDONLY);

  /* Это EXE-файл? */
  read(f,&w,2); if (w!=0x5A4D) {close(f); return GOOD;}

  /* Это Windows-программа? */
  lseek(f,0x18,SEEK_SET); read(f,&w,2);
  if (w<0x40) {close(f); return GOOD;}

  /* Это PE-программа? */
  lseek(f,0x3C,SEEK_SET); read(f,&w,2);
  lseek(f,(long)w,SEEK_SET); read(f,&ph,sizeof(ph));
  if (ph.PE!=0x4550) {close(f); return GOOD;}

  /* Позиция заголовка в файле, а в памяти это SectAlign */
  p = (long) w;

  /* Точка входа лежит в области заголовков? */
  if (ph.EntryPoint>ph.SectAlign) {close(f); return GOOD;};
  lseek(f,ph.EntryPoint,SEEK_SET); read(f,sigbuf,sizeof(sigbuf));

  /* Сигнатура совпадает? */
  if ((sigbuf[0]!=0x55) || (sigbuf[1]!=0x8D)||
     (sigbuf [2]!=0x44) || (sigbuf[3]!=0x24)||
     (sigbuf [4]!=0xF8) || (sigbuf[5]!=0x33)||
     (sigbuf [6]!=0xDB)) {close(f);return GOOD;}

  /* Программа заражена! */
  close(f); return BAD;
}

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

   
// (c) Климентьев К., Самара 2001
cure(char  *s)
 {
  int f;
  f = open(s,O_BINARY|O_RDWR);
  lseek(f,ph.EntryPoint+0x5E,SEEK_SET);
  read(f,&ph.EntryPoint,4);
  ph.EntryPoint-=ph.ImBase;
  lseek(f,p,SEEK_SET);
  write(f,&ph,sizeof(ph));
  close(f);
}

Процедуры компилируются как в ДОС-код при помощи BC/C++ 3.1 (или при помощи TopSpeed C 3.1), так и в 32-битовый код консольного приложения при помощи BC/C+ + 5.0.

6.5. Вирусы, внедряющиеся в ядро системы

Имеются в виду вирусы, заражающие системные библиотеки, например, KERNEL32.DLL. Ведь они имеют PE-формат! Если при заражении прикладных программ вирусы внедряются в точку входа, то при заражении библиотек обычно модифицируется какой-либо системный вызов. Например, вирусы

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

6.6. Вирусы, изменяющие конфигурационные файлы

Эта техника более характерна для сетевых червей типа I-Worm и "троянов", которым, как правило, требуется только однократно закрепиться в системе. (Хотя, конечно, существуют и "комбинированные" вирусы, например, Win95.Babilonia).

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

6.6.1. AUTOEXEC.BAT и CONFIG.SYS

Эти файлы актуальны только на этапе загрузки Windows 9X/ME, причем запустить из них можно только DOS-программу. Но если эта программа-вирус установится резидентно, то этот резидент будет оживать каждый раз при активации DOS-сессии.

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

6.6.2. WIN.INI и SYSTEM.INI

Присутствуют и актуальны, как в Windows 3.X, так и Windows 9X. Представляют собой обычные текстовые файлы, лежащие в C:\WINDOWS. Вирус может вписаться в строки группы [windows] файла WIN.INI:

   
[windows]
load=...    ; Автозагрузка
run=...     ; Автозагрузка

В этом случае указанные программы автоматически запустятся после старта Windows.

Также опасны строки группы [boot] файла SYSTEM.INI:

[boot]
shell=...     ; Файловый менеджер, обычно EXPLORER.EXE
scrnsave=...  ; Псевдоним программы-хранителя экрана
device=...    ; Драйверы устройств
и др.

В принципе, опасны любые строки, связанные с действиями по запуску программ. Например, строки из группы [extensions]:

   
[Extensions]
zip=e:\winzip\winzip32.exe ^.zip

Ничто не мешает вирусу вписать в эту строку запуск собственного дроппера, который уже, по цепочке, запускает нужную программу, соответствующую расширению.

Все это легко ищется "глазками" и удаляется "ручками". Программу сканирования и удаления также написать очень просто.

6.6.3. Файлы Реестра USER.DAT и SYSTEM.DAT

В принципе, это главный конфигурационный файл для Win32. Формат его не текстовый, а двоичный. Состав конфигурационных ключей примерно такой же, как и для INI-файлов.

Например, автоматически запустятся программы, прописанные в ключах ветви:

   
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\
CurrentVersion\Run

Обнаружить и удалить заразу подобного рода можно при помощи стандартной утилиты REGEDIT. Кроме того, вот пример программы, сканирующей Реестр и дезактивирующей вирус TeddyBeer:

   
// Пример сканирования и правки зараженного реестра
// (c) Климентьев К., Самара 2001
#include <stdio.h>
#include <windows.h>
#include <winbase.h>
#include <commctrl.h>

HKEY  hOpenedKey;
BYTE  szBuffer[1024], bDataBuf[1024];
DWORD lBufSize1, lBufSize2, lTypeCode, dwReserved, index;
LONG  Result1, Result2;

main()
 {
  if ((Result1 = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
       "Software\\Microsoft\\Windows\\CurrentVersion\\Run",
       dwReserved, KEY_READ, &hOpenedKey )) == ERROR_SUCCESS)
        {
         Result2 = index = 0;
         while (Result2!=ERROR_NO_MORE_ITEMS)
          {
           lBufSize1 = lBufSize2 = BUFSIZE;
           Result2 =  RegEnumValue( hOpenedKey,  index++,
                                    szBuffer, &lBufSize1,
                                    NULL, &lTypeCode, bDataBuf,
                                    &lBufSize2 );
           if (!strcmp(szBuffer,"TeddyBear"))
               {
           printf("\nНайден вирус TeddyBear!!!");
           RegDeleteKey (hOpenedKey,  szBuffer);
     }      } } }

Вирусы, обращающиеся к реестру при помощи функций API32, можно отследить при помощи утилиты REGMON Теда Мирецки.

6.6.4. Каталоги автозапуска

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

  
Содержимое папки═ C:\WINDOWS\SYSTEM\IOSUBSYS

BIGMEM   DRV  9 952 05.05.99 22:22 BIGMEM.DRV
ESDI_506 PDR 24 406 05.05.99 22:22 ESDI_506.PDR
............................................................................
TORISAN3 VXD 11 067 05.05.99 22:22 TORISAN3.VXD
LIZARD   VXD  1 967 01.01.01 11:11 LIZARD.VXD   ; Вирус !!!
VOLTRACK VXD 18 495 05.05.99 22:22 VOLTRACK.VXD

Эта зараза также обнаруживается визуально и удаляется двумя-тремя кликами мышки.

Впрочем... нынешние юзеры обычно умеют редактировать лишь DOC и RTF-документы при помощи WinWord. Есть подозрение, что даже вирусы, описанные в данном разделе, современные юзеры скорее всего самостоятельно не найдут и удалить не смогут.

6.7. Несколько полезных советов

6.7.1. Как заподозрить и отловить вирус

Аккуратно написанный и тщательно отлаженный резидентный вирус, как правило, в системе незаметен. Многочисленные "General protect fault", к сожалению, в наши дни обычно свидетельствуют не о столько наличии вируса, сколько о глючности программно -аппаратной конфигурации. Чтобы уверенно разрешать дилемму "это-вирус-или-не-вирус", пользуйтесь резидентными антивирусными инспекторами типа AVPI!

Но есть и способы самостоятельного обнаружения файлового Windows-вируса.

Прежде всего, лишите его преимущества резидентности - загрузитесь с чистой системной дискеты (желательно, с MS-DOS версии 7.1 из-под Windows 98/ME, дабы присутствовала поддержка FAT32).

Далее, обратите особое внимание на файлы программ "повышенного риска": на содержимое каталогов C:\WINDOWS, С:\WINDOWS\SYSTEM, C:\WINDOWS\SYSTEM32 и C:\WINDOWS\COMMAND, а также на "пусковые" файлы основных приложений, типа WINWORD.EXE, WINZIP32.EXE и пр. Таких файлов немного, обычно - три-четыре десятка. Основные признаки, позволяющие заподозрить заразу, следующие.

  1. Изменение длины файла и его содержимого. Можно выдрать оригинал из дистрибутива при помощи EXPAND (из упакованных файлов типа INST32.EX_) или EXTRACT (из CAB-инетов) и сравнить побайтно при помощи FCOMP.
  2. Взгляните на заголовок PE-файла при помощи HIEW. Точка входа должна лежать внутри секции .text или .CODE, и ни в коем случае не в области заголовков или в секции с каким-нибудь странным именем типа .vlad. Кроме того, имейте в виду, распространенные компоновщики не вставляют точку входа в секцию, последнюю по счету!

    Пример:

    Таблица секций:

    Name    VirtSize RVA      PhysSize Offset   Flag
    
    .text    00000030 00001000 00000200 00000400 60000020
    .rdata   00000092 00002000 00000200 00000600 40000040
    .data    00000029 00003000 00000200 00000800 C0000040
    .rsrc    000003A0 00004000 00000400 00000A00 C0000040
    .t00fic  00001000 00005000 00000400 00000E00 E0000000
    
    Точка входа:
       
    Entrypoint RVA 00005000
    

    Последняя по счету секция со странным именем .t00fic начинается с адреса 5000 и имеет длину 1000, причем точка входа соответствует первому байту этой секции. Пора бить тревогу! Это действительно был вирус Win95.Arianne.1022.

  3. Загляните внутрь кода при помощи HIEW, не встречаются ли недалеко от точки входа "знаменитые" комбинации:
       
        call $1
    $1: pop  РЕГИСТР
        sub РЕГИСТР,ЧИСЛО
    
       или
       
    push ...
    int  20h   ; VMMCall XXXX.YYYY
    dd   ....
    
  4. В 16-ричном дампе имеются две группы текстовых строк - имен API32-сервисов, причем одна из них - "файл- ориентированная" (см. п. 6.4.3.2).

Кроме того, огромную помощь могут оказать утилиты типа REGMON и FILEMON от Теда Мирецки, которые позволяют отлавливать подозрительные обращения к Реестру и файлам.

6.7.3. Как избавиться от вируса "народными средствами"

В принципе, часто можно даже не писать никаких антивирусов.

  1. Системные драйвера и утилиты и основные файлы приложений можно восстановить из дистрибутивов при помощи EXPAND или EXTRACT.
  2. Файлы Реестра USER.DAT и SYSTEM.DAT целесобразно извлечь из предыдущей сохраненных копий, если Вы уверены в их "здоровье" при помощи SCANREG. Кстати, хотя в Windows 95 такой утилиты нет, можно воспользоваться экземпляром, взятым из Windows 98.
  3. Часто можно "вычислить" правильную точку входа. Допустим, для программы NOTEPAD сведения о кодовой секции выглядят так:
       
    Name     VirtSize RVA      PhysSize Offset   Flag
    .text    00003E9C 00001000 00004000 00001000 60000020
    

    Посмотрим на начало кодовой секции, начиная с файловой позиции 1000.

       
    401000 00 00 00 00-77 69 6E 64-6F 77 73 00-64 65 76 69     windows devi
    401010 63 65 00 00-4F 75 74 20-6F 66 20 52-43 20 73 74 ce  Out of RC st
    401020 72 69 6E 67-20 73 70 61-63 65 21 21-00 00 00 00 ring space!!
    401030 44 45 56 20-45 72 72 6F-72 21 00 00-63 6F 6D 6D DEV Error!  comm
    401040 64 6C 67 5F-46 69 6E 64-52 65 70 6C-61 63 65 00 dlg_FindReplace
    401050 2A 2E 2A 00-45 64 69 74-00 00 00 00-22 00 00 00 *.* Edit    "
    401060 2E 74 78 74-00 00 00 00-20 00 00 00-E9 03 00 00 .txt
    401070 1E 00 00 00-E9 03 00 00-21 00 00 00-E8 03 00 00
    401080 1F 00 00 00-E8 03 00 00-22 00 00 00-FF FF FF FF
    401090 00 00 00 00-00 00 00 00-20 00 00 00-25 64 00 00             %d
    4010A0 25 32 2E 32-64 3A 25 32-2E 32 64 3A-25 34 2E 34 %2.2d:%2.2d:%4.4
    4010B0 64 00 00 00-25 32 2E 32-64 5C 25 32-2E 32 64 5C d   %2.2d\%2.2d\
    4010C0 25 32 2E 32-64 00 00 00-0D 0A 00 00-55 8B EC 83 %2.2d        U ь
    4010D0 EC 44 56 FF-15 E0 63 40-00 8B F0 8A-00 3C 22 75 ьDV  рc@  ╗  <"
    4010E0 13 46 8A 06-84 C0 74 04-3C 22 75 F5-80 3E 22 75  F    t <"u .>"
    4010F0 0D 46 EB 0A-3C 20 7E 06-46 80 3E 20-7F FA 80 3E
    

Без телескопа видно, что начало секции занимают статические данные типа текстовых строк и форматов вывода для функции printf(), а что-то похожее на код начинается с позиции... ну-ка, ну-ка... все правильно, ориганальная точка входа располагается в памяти по адресу 4010CCh и в файле по адресу 10ССh!

При помощи того же HIEW исправьте в заголовке EntryPoint, и любой вирус будет обезврежен.

Заключение

В статье кратко (и, возможно, с ошибками и неточностями) рассмотрена вершина айсберга проблем, связанных с антивирусной борьбой в среде Windows. Например, совершенно не рассмотрены методы обнаружения полиморфных Windows -вирусов. Не упомянуты "дыры" в области VMM-памяти, куда вирус может поместиться целиком. Оставлены без внимания тонкости, связанные с использованием вирусами системных процессов Windows NT.

Данная статья - лишь трамплин для любознательного и смелого исследователя проблемы Windows-вирусов.

К статье прилагаются:

Заключение

...Начинает изучать ее, переворачивая страницы
то от начала к концу, то от конца к началу.
Г. Уэллс. Человек-невидимка.

Итак, цикл статей под условным наименованием "Как Пымать Выря За Хвост" завершен.

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

Автор благодарит В. Руссу, и А. Отенко, в процессе обсуждения с которыми ряда проблем, родились некоторые важные фрагменты текста.

Наконец, автор не может не сказать thank you very much всем тем, кто прямо или косвенно споспешествовал созданию данного опуса, прежде всего - участникам дискуссии "Господибожемой" в электронной конференции relcom.comp.virus.

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

Автор не считает работу полностью завершенной и надеется на конструктивную критику со стороны читателей.

С глубочайшим почтением и искренней преданностию есмь, милостивые государи и государыни, Ваш покорный слуга - автор, а проще:

Климентьев К.Е., бывший аспирант, а ныне ассистент кафедры "Информационные системы и технологии" Самарского государственного аэрокосмического университета

Глава 7. Противодействие сетевым червякам

...И скачет, и пляшет, и прыгает с ветки на ветку,
и ныряет в воду, и проползает в земные расщелины.
С. Снегов. Люди как боги.

I N P R O G R E S S

Официальное заявление от автора статьи

Милостивые государи и государыни! В начале 1999 г. московское издательство "ДМК" (http://www.dmk.ru) выпустило в свет тиражом 5000 экз. книжку некоего Игоря Гульева, озаглавленную "Компьютерные вирусы. Взгляд изнутри". Около 25% текста этой книги являются беззастенчивым и грубым плагиатом с текста статьи, которую Вы сейчас читаете. Г-н Гульев умудрился сохранить в своем "труде" мой индивидуальный стиль, мою фразеологию и мои намеки, понятные лишь узкому кругу моих друзей и знакомых. В то же время он внес грубые искажения в смысл текста, в результате чего образовалось огромное количество неточностей, ошибок и просто глупостей. Кстати, к авторству остальных 75% текста он также имеет весьма отдаленное отношение.

Впрочем, мне не жаль настоящих авторов этих фрагментов, ибо Гульев спер у них и подписался под текстами, за публикацию которых его вполне могут привлечь по статье УК РФ за распространение заведомо вредоносного программного обеспечения. :-) Обидно, что я оказался с этими недоумками под одной обложкой.

Остается только сожалеть, что нечистоплотное московское издательство нашло достойного партнера в лице жуликоватого "автора" и выпустило в свет столь дурно пахнущий "продукт". Ну что ж - Бог им судья.... и прокуратура Российской Федерации :-)

И самое главное: Гульев бездарь, а я напишу еще - больше и лучше; читайте же оригинал!

				Ваш покорный слуга,
				Климентьев К.Е.
				Самара, март-апрель 1999 г.

P.S. Еще одна книжка, в создании части которой я неожиданно для себя "поучаствовал": А. Парандовский, А.Парандовский., Д.Козлов. Энциклопедия компьютерных вирусов.-М.: Солон-Р, 2001.

				Ваш покорный слуга,
				Климентьев К.Е.
				Самара, 2001 г.

Приложение А. Как распознать полиморфный вирус

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

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

Расширим несколько понятие сигнатуры, введенное ранее в этой статье. Назовем сигнатурой множество из N троек {Кi,Pi,Bi}, i= 1.. N, где Ki - порядковый номер выполняемой значимой команды, Pi - номер байта в этой команде, Bi- значение этого байта.

Поясним понятие "порядковый номер выполняемой значимой команды".

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

   
    mov  ax, 0
    jmp  Label
    mov  ax, 1
Label:
    nop

команда NOP будет иметь номер 3, хотя это 4-я по счету команда по расположению в программе. Еще пример:

   
    mov  byte ptr Label, 90h
    jmp  Label
Label:
    cli

Здесь 3-я исполняемая команда не CLI, как могло бы показаться на первый взгляд, а NOP, т.к. код именно этой команды формируется на месте 3-й команды в процессе выполнения.

Под "значимой командой" будем понимать команду, выжную с точки зрения выполнения алгоритма. Это самая "нечеткая" часть определения. Рассмотрим пример алгоритма кодирования блока памяти:

   
    mov  cx, Length
    nop
    mov  si, offset Block
    jmp  Label
Label:
    xor  [si], 0AA55h
    add  si, 2
    mov  ax, ax
    loop Label

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

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

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

Этот метод крайне сложен и трудоемок. Желающих использовать его в своих антивирусных опусах могу направить за консультацией к Игорю Данилову, Евгению Касперскому или Валентину Колесникову. ;-)

Мы же попробуем применить другой метод, более простой. Он основан на использовании механизма трассировочных прерываний.

Микропроцессоры Intel имют следующую полезную особенность: при установленном в единицу бите Т регистра флагов, после исполнения каждой команды автоматически вызывается 1-е прерывание.

Отладчики используют этот механизм для пошагового исполнения программ. Вирусы используют этот механизм для определения оригинального адреса обработчиков прерываний с целью обхода резидентных сторожей. Мы попытаемся использовать этот механизм для последовательного исполнения команд программы, подозреваемой на вирус. Для запуска механизма достаточно установить свой обработчик 1-го прерывания и выставить бит Т регистра флагов, например так:

   
    push 303h
    popf

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

К данному тексту прилагается исходный текст простой программы trasser.com, демонстрирующей использование этого механизма для проверки сигнатур полиморфных вирусов.

Программа trasser использует также недокументированную возможность DOS, позволяющую загрузить программу в память, но не выполнять ее, а просто вернуть точку входа. Эта возможность доступна через подфункцию 1 функции 4Bh прерывания 21h. Загрузив программу в память, мы выполняем запуск механизма трассировки и передаем управление загруженной программе, тем самым запуская ее на выполнение. По завершении контролируемой программы из стека извлекается флаг без бита Т, и к моменту возврата в trasser трассировка оказывается отключенной.

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

Программа trasser умеет распознавать программу easymut во всех ее модификациях, отличая ее ото всех других программ.

Разумеется, необходимость запуска подозрительных на вирус программ выглядит несколько забавной для использования в "серьезном" антивирусе. Кроме того, настоящий вирус всегда может легко и просто "обломить" трассировку, например, использовав команду pop ss. Но для автора было важным только продемонстрировать саму идею.

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

[Вернуться к списку] [Комментарии (0)]
deenesitfrplruua