Единственная по-настоящему защищённая система - это та,
которая отключена от розетки, замурована в бетон
и запечатана в свинцовую комнату с вооружённой охраной -
но и тогда у меня есть сомнения.
Eugene H. Spafford
Эта глава, скорее, подходит Факультету Прикладной Демонологии, но, тем не менее, к программированию она тоже имеет непосредственное отношение. Поэтому она и входит в наш курс.
Термин «компьютерный вирус» был введен в обращение Фредом Коэном в 1985 году в его диссертации Computer Viruses[1], написанной в Южно-Калифорнийском университете. Диссертация была посвящена исследованию саморазмножающихся программ[2]. Компьютерный вирус – это и есть программа, призванная воспроизводить саму себя, внедряясь в тело «доброкачественных» программ, точь-в-точь как настоящий, человеческий вирус.
Структура и философия нашего курса была бы нарушена, если бы мы не рассмотрели подробно работу и разновидности компьютерных вирусов. Ничуть не умаляя вред, причиняемый вирусами, и злонамеренность их авторов, хочется тем не менее отметить необыкновенную изобретательность и виртуозность, необходимую каждому, берущемуся за неправое дело создания вируса. Вирусы уже потому достойны рассмотрения, что они несут на себе отпечаток гения их создателей в той же мере, в какой несут его на себе лучшие образцы таких сложнейших созданий человеческого разума, как, например, операционные системы.
История, однако, не заканчивается на вирусах. Целая индустрия родилась на почве борьбы с компьютерной «чумой» - индустрия программ-антивирусов, назначение которых выслеживать вирусы, деактивировать их и предупреждать владельцев об их появлении. Авторы вирусов, в свою очередь, не оставили нападение без ответа. Вирусы стали более изощренными, они научились мутировать и притворяться, прятаться в закоулки компьютерной памяти и даже использовать сами антивирусные программы для своих собственных целей.
Вся эта киберпанковая война, разворачивающаяся на мировой компьютерной арене, напоминает гигантскую шахматную партию, находящуюся в патовой ситуации, в сложные перипетии которой мы сейчас и погрузимся.
***
Начнем с самого простого типа вирусов, которые называются «вирусами прямого действия» (direct-action viruses). Эти вирусы без лишних проволочек атакуют файлы компьютера, в который попадают. Типичный вирус прямого действия состоит из двух частей: процедуры поиска и процедуры копирования. Первая призвана отыскать на зараженном компьютере диски или программы, которые пригодны для инфицирования, вторая служит для размножения вируса путем копирования самого себя в обнаруженную программу-жертву. Кроме того, более сложные вирусы могут содержать процедуры «необнаружения», или «анти-анти-вирусные» процедуры, с которыми мы вскоре познакомимся.
Простейший пример вируса прямого действия – это так называемый «перезаписывающий» (overwriting) вирус, который обнаруживает exe- или com-файлы в какой-либо директории и копирует самого себя в точку их запуска. При запуске любой такой инфицированной программы, вирус, заменивший собой исходный выполняющийся код, проделывает ту же самую процедуру, таким образом постепенно приводя в негодность все программы на диске. Вирус такого типа исключительно прост и занимает всего несколько строчек на ассемблере. Если вы вспомните программу «Близнецы», которую мы подробно разобрали в главе об игре Core Wars, то поймете, что написать такой вирус действительно ничего не стоит.
Естественно, обнаружить присутствие вируса, который просто уничтожает инфицированные программы довольно просто. Более хитроумный вариант называется «вирус-компаньон» (companion). Он не уничтожает программы, которые заражает, а переименовывает их в скрытые файлы, скрываясь при этом под именем настоящей программы. Более подробно этот процесс можно описать так:
При запуске первой инфицированной программы, вирус-компаньон ищет другие исполняемые программы, копирует их под другими именами, и копирует сам себя под именами настоящих программ. Когда пользователь запускает такую лжепрограмму (которая на самом деле злобный вирус), она передает управление оригинальной программе (спрятанной под другим именем) таким образом, что пользователь не замечает подмены. Конечно, лжепрограмма-вирус может тем временем незаметно заниматься своими черными делами.
Однако самым продвинутым типом вируса прямого действия является «вирус-паразит» (parasitic). Этот вирус не оставляет таких явных следов как уничтоженные программы или новые, хоть и скрытые файлы. Вирус-паразит внедряется в зараженную программу и живет в ней. Заметить его присутствие можно только по изменению размера файла.
Существует две разновидности вирусов-паразитов. Паразиты первого типа внедряются в самое начало зараженного файла и, таким образом, с их кода начинается выполнение программы-жертвы. Паразиты второго типа присоединяются в самый конец файла. Они обладают тем преимуществом (с точки зрения вируса, конечно!), что не должны зачитывать в память весь заражаемый файл для того, чтобы присоединиться к нему.
Удивительно, но для вируса-паразита существует проблема «перезаражения», то есть многократного внедрения в один и тот же файл. Для перезаписывающего вируса такой проблемы нет, потому что инфицированная программа всегда выглядит одинаково – ее код уничтожен самим вирусом. Вирус-компаньон не заражает скрытые файлы, а они и есть уже обработанные им программы. Паразиту же необходимо определить, заражал ли он уже программу, или нет. Ведь если размер файла будет беспрецедентно расти от многократных перезаражений, то паразит просто перестанет быть невидимым.
Простое решение этой проблемы состоит в том, чтобы сравнить несколько начальных байтов потенциальной программы-жертвы с кодом самого вируса.
Как видим, проблема интроспекции очень остро стоит у компьютерных вирусов!
«Вирусы-резиденты» (memory resident viruses) отличаются от вирусов прямого действия тем, что вместо немедленного проникновения в файлы они прячутся в памяти компьютера (отсюда и название). Резидентные вирусы гораздо более мобильны и неуязвимы, чем вирусы прямого действия, хотя бы потому, что, все время находясь в памяти, они могут «следить» за действиями пользователя, следовать за ним по директориям и поджидать выполнения интересующих их программ.
Очевидный способ забраться в память и оставаться там неопределенно долго для вируса заключается в использовании механизма TSR (Terminate and Stay Resident)[3], при котором программа, завершающая выполнение, не освобождает память, а остается в ней. Один из самых известных вирусов такого типа – Jerusalem – проделывал такой трюк, перезапуская копию зараженной им программы, чтобы создать иллюзию нормального процесса, при этом сам завершал выполнение и оставался резидентным в памяти.
Вирус, находящийся в памяти, должен также решить проблему с «местом проживания» так, чтобы не быть обнаруженным антивирусной программой. Для этого обычно используются «дыры в памяти» (memory holes) – труднодоступные места, которые не используются нормальными программами. Так, например, вирус может спрятаться в ITV (Interrupt Vector Table) – таблице векторов прерываний, в той ее части, которая используется только старыми DOS-приложениями. Обнаружить его там будет очень нелегко.
Раз уж мы заговорили о прерываниях, нужно объяснить что это такое. Как и следует из его названия, прерывание – это некое событие, которое прерывает обычный ход выполнения процесса. Например, если пользователь нажимает клавишу на клавиатуре во время выполнения какой-либо программы, это действие вызывает аппаратное прерывание. Кроме того, существуют программные прерывания, то есть такие прерывания, которые можно вызвать программным путем (с помощью специальной инструкции), а не только производя какое-либо механическое действие. В таблице ITV, упомянутой выше, находятся адреса в памяти тех процедур, которые должны обрабатывать то или иное прерывание. Например, после нажатия клавиши, происходит обращение к определенной строчке таблицы прерываний, в которой хранится адрес процедуры, ответственной за обработку нажатия клавиши.
Прерывания используются вирусами для подмены процедур обработки определенных событий их собственным, вирусным кодом. При этом вирус не просто подменяет собой исходный процесс; он подставляет в определенную строчку таблицы прерываний адрес собственной процедуры, в которой он может творить все, что ему угодно, а после этого передает процесс управления на настоящую процедуру обработки прерывания, так, что пользователь (или антивирусная программа) ничего не замечает. Такая техника называется interrupt hooking – «ловля прерывания на крючок». Наиболее полезными прерываниями для вирусов-резидентов являются те, которым в качестве аргумента операционная система передает названия файлов. Вот вам и дешевый способ поиска будущих жертв!
Более того, применяя технику «ловли на крючок», вирус-резидент может воспользоваться самой программой-антивирусом для достижения своих неблаговидных целей. Представьте, что вирус «сел» на прерывание, которое используется операционной системой для открытия файла. Программа-антивирус, последовательно сканирующая все exe-файлы на диске, откроет для нашего вируса двери во все такие файлы совершенно бесплатно. Вирусу-резиденту даже не потребуется процедура поиска файла для заражения – антивирус все сделает за него сам!
Следующая разновидность вирусов – «boot-sector virus» – поселяется на носителе, с которого загружается операционная система. Обычно такие вирусы сочетают в себе качества вирусов прямого действия или вирусов-резидентов, отличаясь от них только способом проникновения в компьютер.
Boot-sector[4] на диске – это та его часть, в которой находится самый начальный код операционной системы, тот, который ответственен за загрузку всей операционной системы. Boot-sector необычайно мал – обычно всего 512 байт, поэтому сам по себе он может содержать только необходимую информацию о типе операционной системы, и программу, помогающую загрузить в память компьютера основную часть ОС. Иногда, процесс загрузки многоступенчат, то есть boot-sector загружает промежуточный загрузчик, или же распаковщик, который динамически распаковывает остальную ОС.
Простой перезаписывающий вирус, поселившийся в boot-sector’е, может просто-напросто уничтожить ОС, не дав компьютеру загрузиться, а заодно инфицировать и файлы на жестком диске, просто подменив их своим собственным кодом. Это, конечно, не слишком большое достижение для уважающего себя вируса. Гораздо занимательней те вирусы, которые, как и резиденты типа interrupt-hooking, начинают выполнять самые первые инструкции после включения компьютера, чтобы затем без потерь передать управление настоящему boot-sector’у.
Таким образом действовал, например, вирус Stoned (одна из разновидностей которого известна также под именем Michelangelo). Поселяясь на диске, Stoned перезаписывал настоящий boot-sector в другое место диска, подменяя его собой. После того как компьютер включали, управление передавалось на boot-sector диска, где уже притаился Stoned, который и выполнял свои собственные вирусные задачи, после чего загружал код настоящего boot-sector’a, который, в свою очередь, загружал операционную систему.
Какие же важные задачи выполнял Stoned перед загрузкой ОС ? В определенном месте компьютерной памяти, существует ячейка, содержащая число килобайт физической памяти компьютера. Это число устанавливается BIOS’ом[5]. Операционная система считывает это число и инициализирует свои внутренние структуры управления памятью в соответствии с ним. Вирус Stoned пользовался этим фактом и изменял число килобайт физической памяти таким образом, что в ней образовывалась «дыра» (обычно размером в 2 килобайта), недоступная для операционной системы и, естественно, для любого антивируса. Так как Stoned работал прежде, чем начинала выполняться самая первая инструкция операционной системы, ОС не имела никакой возможности узнать реальное положение дел с памятью, полагаясь в этом на BIOS. Нетрудно догадаться, что Stoned «заползал» в эту самую созданную им «черную дыру», превращаясь в резидента и оттуда продолжал следить за происходящим.
До сих пор мы рассмотрели только механизмы поиска и саморазмножения компьютерных вирусов, но никак не коснулись того, что является основной задачей автора вируса, а именно того действия, которое должен произвести вирус, находясь в инфицированном компьютере на радость своему создателю. Такое действие называется «логической бомбой» (logic bomb), и радиус ее действия ограничен только фантазией автора вируса. В основном, конечно, вирусы используются для воровства: банковских данных пользователя, паролей или даже для взятия данных компьютера "в заложники" - преступники не дают прочитать файлы с диска до тех пор, пока пользователь-жертва не переведёт на их счёт определённую сумму. Однако, есть случаи и поинтересней. Так, например, самый знаменитый на сегодняшний день компьютерный вирус Stuxnet - первый в истории вирус, который использовался для разрушения физической инфраструктуры, а именно для уничтожения иранских центрифуг обогащения ядерного топлива путём проникновения в программируемые контроллеры центрифуг:
***
Чтобы перейти к рассмотрению следующей разновидности вирусов, а именно вирусов-мутантов, мы должны совершить разворот в обсуждении и рассмотреть главных противников вирусов в непростых «боях в памяти» - программы-антивирусы.
Существует три основных типа антивирусов: сканеры (scanners), тестеры поведения (behavior checkers) и тестеры целостности (integrity checkers).
Сканеры – самая старая разновидность антивирусов. Идея тут крайне проста – просматривать все исполняемые файлы на диске, пытаясь найти такую последовательность байтов в коде, которая совпала бы с одной из известных сканеру последовательностей, принадлежащих какому-либо распространенному вирусу.
Конечно, такая техника не слишком эффективна – ведь сканер-антивирус нужно обновлять каждый раз вместе с появлением на свет новых типов вирусов. Кроме того, мы скоро познакомимся с весьма успешным способом, с помощью которого вирусы противостоят сканерным атакам. Однако антивирусы-сканеры обладают тем преимуществом перед другими видами антивирусов, что они могут предупредить появление вируса до того, как тот начнет выполняться и заражать другие файлы.
Антивирусы-тестеры поведения – это обычно резидентные программы, которые, постоянно находясь в памяти, следят за необычным и подозрительным поведением других программ. Подозрительным может быть попытка программы, не имеющей обыкновения оставаться в памяти после завершения, стать резидентной (а мы уже видели, что именно так и поступают резидентные вирусы-паразиты – пытаются остаться в памяти после завершения зараженной ими программы). Другим примером подозрительного поведения может быть открытие исполняемого (то есть exe или com) файла в режиме, позволяющем записывать в этот файл. Программа, пытающаяся записать информацию в boot-sector - тоже потенциальный вирус.
Наконец, антивирусы-тестеры целостности составляют базу данных, содержащую информацию обо всех исполняемых файлах, которая в большинстве случаев не должна меняться. Такая информация может включать в себя размер файла, последнюю дату его изменения или контрольную сумму (то есть сумму всех числовых значений байтов, составляющих файл). Периодически сравнивая состояние файлов с базой данных, антивирус может предупредить пользователя о неожиданном изменении какого-либо из этих параметров.
Было бы наивно полагать, что с развитием антивирусных технологий, авторы вирусов сидели, сложа руки. Конечно же, у них нашелся ответ на все возможные атаки.
Одной из основных техник борьбы вирусов с антивирусами является «незаметность» или «невидимость» - техника stealth.
Представьте себе, что программа-антивирус считывает код, находящийся в boot-sector’е, чтобы сравнить его с «мастер-кодом» в ее базе данных на предмет возможных изменений. Хитрость заключается в том, что, для того, чтобы прочитать данные с диска (в частности, из boot-sector’а), антивирус должен осуществить программное прерывание, выполняющее функции чтения. Но мы уже видели раньше, что, используя технику interrupt hooking, вирус может подменить своим собственным кодом процедуру обработки прерывания для чтения с диска. Вставив в такую процедуру простое условие проверки на попытку какой-либо программы читать данные из boot-sector’a, вирус может отсылать эту программу в совершенно другой сектор диска, куда он предусмотрительно перепишет код из boot-sector’а. Таким образом, программа-антивирус, читает данные из ложного boot-sector’а (выданного вирусом за настоящий) и пребывает в ложном убеждении, что boot-sector не изменился. Тем временем, победоносный вирус не только перехватил прерывание чтения и переписал подложный boot-sector в другое место – он и в самом деле заразил настоящий boot-sector!
Каждому действию есть противодействие, и антивирусы тоже не лыком шиты. Для борьбы с техникой stealth антивирусы используют метод под названием interrupt tunneling. Этот метод позволяет узнать адрес настоящей процедуры прерывания, записанной в памяти BIOS. Если инициировать процедуру чтения с диска напрямую, без использования механизма прерываний, вирус уже не сможет подменить ее код, и чтение будет производиться из настоящего boot-sector’а (или откуда бы то ни было).
Победа за антивирусами ? Не тут-то было – партия продолжается!
Дело в том, что, независимо от способа, которым была вызвана процедура чтения с диска, после того, как необходимые данные найдены, и диск готов предоставить данные для чтения, происходит другое прерывание, а именно, вызывается процедура, уведомляющая вызывающий процесс о готовности диска. И ничто не мешает вирусу подменить и это прерывание, вместе с прерыванием чтения. Теперь, если вирус замечает, что вызвана процедура готовности диска без предварительного вызова процедуры чтения (за которой он тоже следит), становится понятно, что некто применил interrupt tunneling, то есть попытку чтения с диска напрямую в обход прерывания. И, как легко догадаться, этому «некто» такая хитрость теперь обойдется недешево.
Конечно же, и этот новый поворот событий может быть предусмотрен антивирусными программами, на что, в свою очередь, найдут ответ еще более изощренные вирусы – процесс этот поистине бесконечен.
В следующей части этой главы мы познакомимся с вирусами-мутантами, также называемыми полиморфными.
[1] Fred Cohen. Computer Viruses. ASP Press, Pittsburgh, 1986.
[2] Вспомните саморазмножающуюся программу из главы о Core Wars.
[3] Речь идет о довольно старой терминологии DOS.
[4] Интересно, что слово это произошло от bootstrapping, что означает вытягивать самого себя за волосы с сапогами.
[5] BIOS – Basic Input Output System – программа, впечатанная в чип, с которой начинается процесс работы компьютера после его включения и до чтения кода из boot-sector’а. BIOS отвечает за инициализацию компьютера сразу после включения питания безотносительно к операционной системе, и он-то и передает управление на boot-sector.