После того, как мы проследили за подробностями борьбы вирусов-невидимок с антивирусами-тестерами поведения, пришло время взглянуть на поле битвы антивирусов-сканеров со своими собственными противниками – вирусами-мутантами или так называемыми «полиморфными вирусами» (polymorphic viruses).
Как уже было сказано, тактика сканера состоит в поиске известных ему последовательностей двоичного представления инструкций вирусов в исполняемых файлах. Очевидно, что простая тактика, которую можно противопоставить таким действиям сканера – это замена одной или нескольких инструкций вируса на другие без изменения его поведения[1]. Такая практика, однако, не слишком эффективна, потому что выпущенный в свет вирус с измененным кодом вскоре станет известен антивирусным программам. Гораздо более далеко идущий подход состоит в том, чтобы динамически изменять код вируса при каждом его копировании (то есть в процессе размножения). Вирусы, действующие таким образом, и называются полиморфными.
В начале 1990-х болгарский хакер, называвший себя Dark Avenger, выпустил для всеобщего использования специальную процедуру в виде скомпилированного модуля под названием Mutation Engine. Этот модуль компилировался вместе с каким-либо вирусом, позволяя последнему вызывать процедуру мутации каждый раз при копировании самого себя. После этого копия вируса выглядела отлично от его предыдущей версии. Mutation Engine вызвала настоящий переполох в антивирусных компаниях.
Полиморфный вирус состоит из двух частей – «дешифровщика» (decryptor) и основного тела вируса. Тело вируса включает в себя собственно вирус (со всеми необходимыми атрибутами), «шифровальщика» (encryption routine) и генератор «дешифровщика» (decryptor generator). Полиморфность вируса достигается тем, что при каждом новом копировании основное тело вируса «шифруется» по-другому с помощью процедуры-шифровальщика. Задача «дешифровщика» заключается в том, чтобы «расшифровать» тело вируса перед выполнением. Обратите внимание на проблему, которая возникает при таком раскладе: если сам «дешифровщик» при этом остается неизменным, смысл полиморфного вируса полностью теряется. Ведь сканер сможет обнаружить такой вирус простым поиском известного ему кода «дешифровщика». Эту проблему призван решить генератор «дешифровщика». При каждом новом копировании вируса он заново генерирует код «дешифровщика», вставляя в него случайным образом «бесполезный» код, который никак не влияет на поведение «дешифровщика».
Обычно «шифровальщики» и «дешифровщики» полиморфных вирусов основаны на очень простых принципах. Например, процесс «шифрования»[2] основного тела вируса может заключаться в произведении операции xor над каждым двоичным представлением инструкции вируса с некоей константой. В этом случае процесс «дешифровки» абсолютно идентичен процессу «шифрования»[3]. Выбирая другую константу при каждом копировании, процедура «шифрования» будет производить все новые и новые варианты вируса-мутанта.
На рисунке показан пошаговый процесс действий полиморфного вируса: [if !supportLineBreakNewLine] [endif]
Шаг 1. Вирус начинает выполняться. В этот момент его основная часть существует только в зашифрованном виде. Процесс начинается с расшифровки основной части вируса в осмысленный код (программа изменяет сама себя в памяти!).
Шаг 2. Дешифровщик закончил работу, и вирус готов к своей основной деятельности. Как обычно, эта деятельность ограничена только фантазией автора.
Шаг 3. Пришло время мутации и переселения. Поисковые процедуры находят файл-жертву для проникновения.
Шаг 4. Генератор дешифровщика начинает производить новый код со случайно распределенными бесполезными инструкциями.
Шаг 5. Процедура-шифровальщик заново кодирует тело вируса, и вместе с новым кодом дешифровщика эти две части составляют новую версию вируса-мутанта.
Шаг 6. Мутация завершена, вирус копирует свое новое «я» в файл, на котором он будет паразитировать.
Может показаться, что сканеры бессильны против вирусов-мутантов. Однако не будем спешить с выводами. Существуют чрезвычайно хитроумные способы обнаружения мутантов – от спектрального до эвристического анализа.
Спектральный анализ означает статистическое исследование распределения машинных инструкций, используемых в программе. Вирусы часто пользуются эзотерическими машинными инструкциями, которые редко или никогда не генерируются нормальными компиляторами – эти-то инструкции и вызовут отклонения в распределении, анализируемом антивирусом.
Эвристический анализ подразумевает нахождение характерных паттернов поведения в коде исследуемой программы. Так, например, процедура-шифровальщик вируса-мутанта почти наверняка будет проходить в цикле по всем инструкциям тела вируса и изменять их. Такое поведение может служить уликой против вируса.
В то время как вирусы-мутанты пытаются спрятаться от сканеров, на сцене появляются новые герои – «вирусы-мстители» (retaliating viruses). Идея работы «мстителей» крайне проста – она позаимствована у сканеров. Почему бы не начать преследовать преследователя его собственными методами ?
У вируса-мстителя есть своя собственная база данных, содержащая характерные участки кода известных программ-антивирусов. Действуя точно по схеме антивируса-сканера, вирус-мститель ищет в памяти компьютера запущенную программу-антивирус[4]. Если противник обнаружен, у вируса есть несколько возможностей для дальнейших действий: либо затаиться до времени, пока антивирус по какой-либо причине не будет запущен, либо немедленно взорвать «логическую бомбу» (например, отформатировать жесткий диск), либо – и так поступают наиболее уважаемые «мстители» - захватить контроль над антивирусом, скажем, путем перехвата прерываний, которые отслеживаются последним, - и параллельно поставлять ему ложную информацию, не отрываясь от важных вирусных дел.
***
Не удивлюсь, если у некоторых читателей возникла потребность попробовать себя в качестве авторов компьютерных вирусов. Таким читателям можно посоветовать замечательный, подробнейший учебник Марка Людвига «The Giant Black Book of Computer Viruses»[5], который шаг за шагом учит писать компьютерные вирусы, поднимая по ходу дела некоторые философские вопросы, связанные с искусственными организмами и будущим компьютеризированного мира.
[1] Самое простое, что можно придумать в таком случае – это вставить в код вируса бесполезные инструкции, такие как nop – no operation. Более элегантный способ может заключаться в многократной инициализации «мусором» какого-либо регистра или ячейки памяти перед тем, как записать туда действительно необходимые данные.
[2] Слово «шифрование» все время употребляется в кавычках, потому что этот процесс имеет мало общего с настоящим шифрованием.
[3] Логическая функция xor определяется с помощью следующей таблицы: 0 xor 0 = 0
0 xor 1 = 1
1 xor 0 = 1
1 xor 1 = 0
Легко убедиться, что функция xor, примененная к какому-либу двоичному числу дважды с одной и той же константой просто вернет это число к своему первоначальному значению.
[4] Мы говорим о поиске в памяти, а не на диске, потому что такой род суперактивности, как просмотр всех файлов на диске для вируса слишком опасен; хотя, и такой вариант не исключен.
[5] Mark Ludwig. The Giant Black Book of Computer Viruses. American Eagle Publications, 1995.