Организация эффективной системы борьбы с различными видами РПС невозможна без разработки формализованной модели, описывающей их структуру и принципы функционирования. Наличие такой модели позволит создать универсальные средства противодействия, повысить их эффективность и определить область применения. Кроме того, такая модель может быть использована для определения условий распространения РПС (в частности, компьютерных вирусов), а также и для построения различных средств противодействия их распространению.
Основные работы, которые касаются данной предметной области, можно условно отнести к трем направлениям:
1) Теоретические исследования, направленные на создание общих математических моделей, описывающих вирусы как специальные программы или математические объекты [10, 14].
2) Определение набора признаков, присущих конкретным вирусам и построение классификаций на базе этих признаков. В нашей стране самой фундаментальной работой такого типа является работа Н. Н. Безрукова [1]. Построение таких классификаций необходимо для отождествления вирусов друг с другом и формирования однородных классов и групп вирусов, однако они не могут помочь при решении задач их поиска и уничтожения.
3) Построение моделей, описывающих структуру вируса как набор функциональных элементов, определяющих типовые алгоритмы воздействия вирусов на другие объекты: программы, данные, операционную среду. Пример такого подхода показан в [15].
Последний подход, по мнению автора, выглядит наиболее перспективным, поскольку позволяет получить практические решения для задач поиска, обнаружения и ликвидации всех классов РПС, а не только компьютерных вирусов. Чтобы убедиться в правомерности такого предположения, рассмотрим ситуацию с компьютерными вирусами с точки зрения работ первого направления.
Совершенно очевидно, что основным свойством вируса, с помощью которого его и надо определять, является способность к саморепродукции (репликации), т.е. к размножению и порождению себе подобных. Однако оказывается, что строго это сделать не так просто, в частности, для этого необходимо иметь формальное определение "саморепродукции" в компьютерных системах. Попытка уйти от этого делается в классическом определении вируса.
Оно остается неизменным с 1984 г., когда оно было дано Ф.Коэном в своей основополагающей работе [9] и звучит как: “компьютерный вирус — это программа, которая может заражать другие программы, изменяя их посредством добавления своей, возможно модифицированной, копии, которая сохраняет способность к дальнейшему размножению".
Как мы видим, Ф. Коэн уходит от понятия "саморепродукция" изменяет его производным — "заражение" (раз вирус должен размножаться, то в результате этого некоторые объекты должны приобрести копию вируса, т.е. заразиться), считая его определяющим свойством вируса.
При внимательном разборе этого определения сразу возникает ряд вопросов:
1) что это за "копии", которые могут быть "модифицированными", т.е. не совпадать с оригиналом? (Напомним, что каждый потомок полиморфных вирусов не будет совпадать со своим родителем ни в одном байте!). Очевидно, здесь имеются в виду возможность вируса к мутациям, когда в новую копию он может вставлять случайные ничего не значащие команды, шифровать свое тело с другим ключом и т.д. Назовем такие изменения вируса, не затрагивающие его алгоритм, простыми мутациями.
2) далее, почему вирус должен ограничиваться только такими мутациями? Нетрудно представить себе вирус-родитель, который способен нанести ущерб компьютерной системе, и порождающий потомков, также способных к саморепродукции, но не предназначенных для нанесения ущерба, т.е. совершенно непохожих на родителя. Такие кардинальные изменения вируса назовем эволюционными мутациями.
3) почему разговор ведется только о программах? Как известно, новые типы вирусов могут внедряться в другие объекты, модифицируя их и вычислительную среду так, что при запуске программы они получают управление. (Пример — вирус Dir, ничего не меняющий в файлах, так модифицирует таблицу расположения файлов (FAT), что запуск любой программы приводит сначала к исполнению вируса).
4) сетевые черви полностью выпадают из этого определения, т.к. они заражают не отдельную программу, а целую компьютерную систему.
Неоднократно делались попытки улучшить это определение. Одна из таких последних попыток принадлежит П. Петерсону (P. Peterson) в редакции Б. Себорга (B. Seborg) [16], которые определяют компьютерный вирус как "саморепродуцирующуюся программу, которая может заражать другие программы, модифицируя их или их среду (environment) так, что запуск (call) зараженной программы приводит к запуску возможно претерпевшей эволюцию (evolved) и в большинстве случаев функционально подобной копии вируса".
Это попытка закрыть первые три дыры в определении Коэна, но при этом появляются другие неопределенные понятия: "копия, претерпевшая эволюцию", "функционально подобная копия", которые еще и рассматриваются только в "большинстве случаев".
Ф. Коэн, основываясь на своем определении, строго доказывает, что обнаруживать вирусы, используя только их основное свойство — заражение, невозможно.
Чтобы определить, что данная программа P является вирусом, должно быть установлено, что P заражает другие программы. Пусть D— это гипотетическая функция, которая возвращает значения "истина" тогда и только тогда, когда ее аргумент является вирусом. Тогда P сама может использовать эту функцию D и заражать другие программы только тогда, когда D(P) возвращает "ложь" (см. прог. 1).
VIRUS CV
{
VIRUS; // Признак наличия вируса в программе для повторного
// ее незаражения
если не D (CV) то
{
ЗАРАЗИТЬ_ПРОГРАММУ;
если УСЛОВИЕ_СРАБАТЫВАНИЯ то МАНИПУЛЯЦИИ;
}
передать управление зараженной программе;
}
Прог. 1. Вирус с противоречием.
Если процедура D принятия решения определяет, что CV — это вирус, то CV не будет заражать другие программы и не будет таким образом вирусом по определению. Если D определяет, что CV — не вирус, то CV будет заражать другие программы и поэтому будет вирусом. Отсюда следует, что процедура принятия решения D противоречива и создать ее невозможно. Таким образом, универсального антивируса не существует и точное обнаружение вируса без его запуска является неразрешимой задачей.
Не является ли тогда бессмысленными после приведенного доказательства все дальнейшие рассуждения (ведь мы строим, в принципе, универсальную программу, определяющий принадлежность входной программы к РПС)? Более того, какое бы формальное определение вируса мы не дали (например, "вирус — это программа, наносящая вред" или даже "вирус — это программа, выводящая "Hello, world"), всегда можно построить программу, аналогичную приведенной выше, чтобы убедиться в невозможности построения той злополучной функции D.
Вывод: надо искать в другой плоскости.
Очень заманчивым с точки зрения формализации кажется следующее определение вируса: "вирус — это программа, способная порождать другие вирусы". Увы, оно рекурсивно неразрешимо: нет условия окончания рекурсии.
Тут мы сталкиваемся с почти философской проблемой — как определить объекты, ключевым свойством которых является порождение себе подобных, причем подобие определяется с помощью того же свойства — порождать себе подобных и т. д.
Попытаемся дать более точное рекурсивное определение вируса. Во-первых, очень легко можно решить проблему простых мутаций вируса, если мы будем говорить, что понимаем под вирусом не программу, а алгоритм, число возможных реализаций которого на конкретном языке может быть любым. Тогда любые простые мутации вируса будут эквивалентны друг другу по своему алгоритму и простой мутирующий вирус можно определить как "алгоритм, который может порождать эквивалентный ему алгоритм и обеспечивать возможность получения им управления".
Во-вторых, что касается эволюционных мутаций, то здесь, наоборот, порождаются совершенно не эквивалентные по алгоритму вирусы.
Очень интересным здесь является вопрос о существовании алгоритмов, способных порождать бесконечное количество неэквивалентных алгоритмов. Например, пусть каждая новая копия вируса сдвигает срок своего проявления на один день. Таким образом, казалось бы, мы получим бесконечное число различных вирусов. Но это не так, потому что данные, где хранится дата проявления, имеют фиксированный размер, и рано или поздно произойдет переполнение. Размер данных не может увеличиваться до бесконечности, т.е. длина вируса тоже имеет ограничение, иначе, если длина каждой новой копии увеличивается, то однажды ему не хватит памяти для записи новой копии и таким образом будет нарушена способность к дальнейшему порождению. Эти, правда, не очень строгие рассуждения позволяют сделать заключение, что бесконечно неповторяющегося мутирующего вируса быть не может, и рекурсия заканчивается либо на повторении, либо на порождении алгоритма, неспособного к дальнейшему размножению.
Заметим также, что термин "порождать", хотя он не может быть строго определен, означает именно процесс рождения "из ничего", без всяких входных данных, иначе очень много обычных программ попали бы под определение вируса (например, транслятор, "порождающий" исполняемый код из исходного).
Тогда полное определение компьютерного вируса может звучать так: "Компьютерный вирус (саморепродуцирующийся алгоритм) — это алгоритм, который может порождать без использования входных данных:
а) либо алгоритм, неспособный к порождению других;
б) либо алгоритм, эквивалентный одному из его родителей, и обеспечивать возможность получения им управления;
в) либо новый компьютерный вирус, неэквивалентный по алгоритму любому из его родителей".
Vi — комп. вирус, если он порождает Vi+1 , где
б) Vi+1 — алгоритм | $ j, j=1..i, Vi ~ Vj
в) Vi+1 — комп. вирус | " j, j=1..i, Vi
Однако, как было показано (см. п. 2.1.2), и это свойство — порождение других алгоритмов — нельзя использовать для обнаружения компьютерных вирусов по внешнему виду. Поэтому мы перейдем к рассмотрению моделей и определений, пригодных с практической точки зрения.
Наиболее приближенным к практике является подход, представляющий РПС в виде системы концептов (понятий) на функциональном уровне. Хотя он и не позволяет получить результаты столь глобального характера, как при математическом моделировании, но с точки зрения создания теоретических основ поиска РПС появляются возможности: во-первых, создать обобщенную концептуальную модель РПС для структурирования знаний по РПС и выбора способа их представления; во-вторых, реализовать целенаправленный поиск функциональных элементов РПС в прикладных программах; в-третьих, решить задачу создания структурно-алгоритмического образа РПС. Тем самым становится возможным переход к построению программной модели (синтезу) РПС для исследования механизмов его работы.
При введении понятия РПС было замечено, что наиболее важной их характеристикой является способность к нанесению ущерба компьютерной системе. Однако важно отметить, что такую характеристику не так просто конкретизировать и сопоставить ей конкретные признаки. Связано это с двумя причинами: во-первых, любая прикладная программа, заведомо не относящаяся к числу РПС, потенциально может содержать в себе алгоритмические ошибки, проявление которых приведет к разрушению элементов вычислительной среды. Во-вторых, злоумышленник-пользователь может нанести ущерб элементам операционной или вычислительной среды, используя штатное программное обеспечение (системные или сервисные программы ОС), превращая его в РПС.
Таким образом, мы получаем первый функциональный элемент, часто встречающийся в РПС — процедуру нанесения ущерба (ПНУ, другие названия: нарушения целостности, нападения, разрушения), под результатом действия которой будем понимать нанесение ущерба компонентам вычислительной среды.
Не менее значимым для РПС является наличие еще двух элементов: процедуры саморепродукции (ПСР) и процедуры преодоления защиты (ППЗ), также являющиеся "вредными", т.е. не встречающиеся в "хороших" программах.
Как показано в п. 2.1, точного определения понятия саморепродукции по отношению к программам не существует. По аналогии с биологическими системами мы будем понимать под свойством саморепродукции способность к порождению некоторого программного кода. Это несколько расширяет приведенное рекурсивное определение саморепродуцирующегося алгоритма, но для практического применения является пригодным).
Под процедурой преодоления защиты понимается попытка преодоления системной и/или дополнительной защиты. Системной защитой будем называть систему безопасности, являющуюся частью ОС (система разделения пользователей, защита доступа к файлам, защита ядра ОС). Под дополнительной защитой понимаются все компоненты системы безопасности, не являющиеся частью ОС (обычно это системы криптографической защиты и т. д.). Факт преодоления системы защиты означает осуществление действий, запрещаемых системой защиты (получение неавторизованного доступ, подбор пароля и т. д.).
Тогда на основе этих трех элементов можно дать функциональное определение РПС: под РПС будем понимать программный код, реализующий одну из этих процедур.
Далее можно сформулировать условия необходимости и достаточности, позволяющее провести классификацию РПС по трем основным классам (иллюстрация на рис. 3):
а) Необходимым и достаточным условием для отнесения РПС к классу компьютерных вирусов является наличие в его составе процедуры саморепродукции.
б) Необходимым для отнесения РПС к классу троянских коней является наличие в его составе процедуры нанесения ущерба, а достаточным — отсутствие процедуры саморепродукции.
в) Необходимым условием для отнесения РПС к классу средств НСД является наличие в его составе процедуры преодоления защиты, а достаточным — отсутствие процедур саморепродукции и нанесения ущерба.
Приведенная классификация является единой формализованной формой представления различных видов РПС. Более того, такое представление не только не противоречит основному принципу современной технологии программирования — принципу модульности, — когда программа строится в виде совокупности процедур, но и хорошо согласуясь с ним, дает некоторую базу для создания методик поиска РПС (или элементов РПС) в исполняемых кодах программ, основанных на такой концептуальной модели РПС.
Введя дополнительный набор возможных функциональных элементов РПС: процедуру захвата управления, процедуру мутации, процедуру синтеза, процедуру шифровки и т.п., можно построить обобщенную концептуальную модель (рис. 4) в виде совокупности его функциональных элементов. Обобщенная модель отражает возможную структуру трех основных классов РПС.
Учитывая введенные три основные функции, присущие РПС, получаем 8 различных их комбинаций, т.е. видов РПС (9-ая не является РПС). В зависимости от того, какой признак мы будем рассматривать в качестве базового, можно построить три возможных классификаций РПС (табл. 4, 5, 6).
Наконец, можно классифицировать все известные виды РПС по предлагаемым признакам (табл. 7). Как видно, различий между троянскими конями и программными закладками здесь нет, что вполне естественно, т.к. как-то конкретизировать функции, присущие только программным закладкам, чрезвычайно трудно.
Итак, для определения принадлежности программы к РПС нам достаточно найти в ее составе одну из процедур: саморепродукции, нанесения ущерба, преодоления защиты. Чтобы определить, что данная процедура принадлежит к числу искомых, необходимо проанализировать ее семантический смысл, т.е. понять алгоритм.
Таким образом, задача, поставленная в главе 1, может быть конкретизирована как семантический анализ исполняемого кода программ на предмет наличия заданной функции.
Поэтому пора рассмотреть методы анализа исполняемого кода, что и будет сделано в следующей главе.