Глава 4.

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

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

4.1. Построение дерева свертки РПС и набора характерных признаков.

 Первоочередной задачей для реализации ИСАИПК является построение максимально полного дерева свертки, общая структура которого была представлена в предыдущей главе на рис. 7. Для реализации системы анализа необходимо наполнить это дерево конкретными узлами, присущими РПС. Для системы SubLVA оказалось достаточно ограничить глубину этого дерева 5 уровнями (т.е. N=4).

Ниже приведено используемое в системе дерево свертки (с сокращениями), представленное описанием в форме Бэкуса-Науэра (БНФ).

Уровень 0.

 ПСР ::= ПСР.Одиночная | ПСР.Сетевая

ПСР.Одиночная ::= ПСР.Резидентная | ПСР.Файловая | ПСР.Бутовая

ПСР.Сетевая ::= Не_Рассматр

 

ПНУ ::= ПНУ.Файл | ПНУ.Видео | ПНУ.Порт | ПНУ.Клавиатура | ПНУ.Система

ПНУ.Файл ::= ПНУ.Формат | ПНУ.BOOT_MBR_FAT | ПНУ.Сектор | ПНУ.Исполн_Файл

ПНУ.Видео ::= Не_Рассматр

ПНУ.Клавиатура ::= ПНУ.Блокировка_Клав | ПНУ.Запись_Клав

ПНУ.Система ::= ПНУ.Зависание | ПНУ.Перезагрузка |

ПНУ.Замедление | ПНУ.Блокировка_Запуска

 

ППЗ ::= ППЗ.Системная | ППЗ.Дополнительная

ППЗ.Системная ::= Пусто

ППЗ.Дополнительная ::= ППЗ.Вход | ППЗ.Программа | ППЗ.Антивирус

 

Уровень 1.

 ПСР.Резидентная ::=

Проверить_Наличие.Память

Выделить.Память

Перехватить.Прер

Скопироваться.Память

Замести_Следы.Память

ПСР.Файловая ::=

Найти_Жертву.Файл

Проверить_Тип.Файл

Проверить_Наличие.Файл

Скопироваться.Файл

Замести_Следы.Файл

ПСР.Бутовая ::=

Проверить_Наличие.BOOT

Найти_Место.BOOT

Скопироваться.BOOT

Замести_Следы.BOOT

 

ПНУ.Формат ::= Отформатировать_Дорожку

 

ПНУ.BOOT_MBR_FAT ::= Записать.BOOT | Записать.MBR | Записать.FAT

 

ПНУ.Сектор ::= Записать.Абс

 

ПНУ.Исполн_Файл ::= Записать.Исполн_Файл

 

ПНУ.Блокировка_Клав ::= Не_Рассматр

ПНУ.Запись_Клав ::= Записать_Буфер.Клав

ПНУ.Зависание ::= Бесконеч_Цикл

ПНУ.Перезагрузка ::= Вызвать_Перезагр

ПНУ.Замедление ::= Замедлить.Таймер

 

ПНУ.Блокировка_Запуска ::=

Проверить.EXEC

Сравнить.EXEC

 

Уровень 2.

 Проверить_Наличие.Память ::= Вызвать.Нестд_прер | Вызвать.Нестд_функц |

Сканировать.Память

 

Выделить.Память ::= int21.AA | Манипуляция.MCB | Системная_Область

 

Скопироваться.Память ::=

Найти_себя.Память

Скопировать.Память |

Остаться_Резидентным

 

Перехватить.Прерывание ::= int21.25 | Запись_Табл

 

Замести_Следы.Память ::= Пусто

 

Найти_Жертву.Файл ::= Поиск.PATH | Поиск.Тек_каталог | Поиск.Корень |

Поиск.Дерево | Поиск.Все_диски | Поиск.COMMAND

 

Поиск.PATH ::=

Считать.PATH

Сканировать.PATH

Проверить.Файл

 

Поиск.Тек_каталог ::= Проверить.Файл

Поиск.Корень ::=

ChDir.Root

Проверить.Файл

Поиск.Дерево ::=

FindFirst

Проверить.Дир

Считать.DTA

Поиск.Дерево

Записать.DTA

FindNext

 

Поиск.Все_диски ::=

Найти_колич.Диск

Перейти.Диск

Поиск.Дерево

 

Поиск.COMMAND ::= Считать.COMSPEC | C.COMMAND.COM

 

Проверить_Тип.Файл ::=

Считать_Начало.Файл

Проверить.MZ

 

Проверить_Наличие.Файл ::= Проверить.Дату_Время | Проверить.Сигнатуру

 

Скопироваться.Файл ::= Скопироваться.COM | Скопироваться.EXE |

Скопироваться.BAT | Скопироваться.SYS

 

Скопироваться.COM ::=

Проверить.Условие_Заражения.COM

Снять.Read_Only.Файл

Запомнить.Дату.Файл

Открыть_Запись.Файл

Считать_Начало.Файл

Записать_Конец.Файл

Записать_Начало.Файл

Закрыть.Файл

Поставить.Дату.Файл

Поставить.Read_Only.Файл

Скопироваться.EXE ::=

Проверить.Условие_Заражения.EXE

Снять.Read_Only.Файл

Запомнить.Дату.Файл

Открыть_Запись.Файл

Считать_Заголовок.Файл

Записать_Конец.Файл

Записать_Заголовок.Файл

Закрыть.Файл

Поставить.Дату.Файл

Поставить.Read_Only.Файл

Замести_Следы.Файл ::= Не_рассматр

 

Проверить_Наличие.BOOT ::=

Считать.BOOT

Проверить.Сигнатуру |

Считать.Метку_Диска

Проверить_Сигнатуру

 

Найти_Место.BOOT ::= Свободный_Сектор | Системный_Сектор | Новый_Сектор

 

Новый_Сектор ::= Форматировать_Дорожку

 

Скопироваться.BOOT ::=

Считать.BOOT

Записать.Сектор

Записать.BOOT

 

Замести_Следы.BOOT ::= Пометить_Сбойный.Сектор

 

Пометить_Сбойный.Сектор ::=

Поиск_FAT.Сектор

Пометить_BAD.FAT

 

Замедлить.Таймер ::=

Считать.Прер.Таймер

Обработчик_Цикл

Записать.Прер.Таймер

 

ППЗ.Вход ::= Пусто

ППЗ.Программа ::= ППЗ.Программа.ADM | ППЗ.Программа.Stacker |

ППЗ.Программа.Перебор

 

ППЗ.Программа.ADM ::= Вскрыть.ADM

ППЗ.Программа.Stacker ::= Вскрыть.Stacker

ППЗ.Программа.Перебор ::=

Сформировать.Пароль

Вызов.Программы

 

ППЗ.Антивирус ::= ППЗ.Aidstest | ППЗ.Scan | ППЗ.Adinf | ППЗ.Сторож | ППЗ.Детектор

 

ППЗ.Aidstest ::= Не_зараж.Aidstest

ППЗ.Scan ::= Не_зараж.Scan

ППЗ.Adinf ::= Не_зараж.Adinf | Испортить_файлы.Adinf

ППЗ.Сторож ::= Найти_обработчик.21 | Найти_обработчик.13

ППЗ.Детектор ::= Корректировать_длину.Файл | Корректировать.BOOT

 

Уровень 3.

 

Вызвать.Нестд_прер ::=

INT [00-09 34-55 87-C0] (1)

Вызвать.Нестд_функц ::=

MOV ah, [6D-FF]

int 21

Сканировать.Память ::=

MOV si, o?

MOV di, o?

MOV cx, o?

rep scas

int21.AA ::=

MOV ah, AA

int 21

Манипуляция.MCB ::= Не_рассматр

 

Системная_Область ::=

MOV x1, [0000 A000]

MOV s?, x1

Найти_себя.Память ::=

call 0000 (2)

pop x?

Скопировать.Память ::=

MOV si, o?

MOV di, o?

MOV cx, o?

rep movs

int21.25 ::=

MOV ah, 25

int 21

Запись_Табл ::=

MOV s1, 0 (3)

MOV s1:[a1], o?

MOV s1:[a1+2], o?

Остаться_Резидентным ::=

MOV ah, 31

MOV al, o?

MOV dx, o?

int 21 |

MOV dx, o?

int 27

 

Считать.PATH ::= Не_рассматр

Сканировать.PATH ::= Не_рассматр

 

Проверить.Файл ::=

MOV ax, 22 (4)

MOV dx, a1

int 21h

a2: *

MOV ax, 23

int 21h

*

JNE a2

a1: db '*.COM' |

db '*.EXE' |

db '*.*'

ChDir.Root ::=

MOV dx, a1

MOV ah, 3b

int 21

a1: db '\'

Считать.DTA ::=

MOV ah, 2f

int 21

c? o?, es:[bx]

Записать.DTA ::=

MOV ah, 1a

int 21

C.COMMAND.COM :: =

db 'COMMAND' | (5)

db 'COMMAND.COM' |

db 'C:\COMMAND.COM'

Считать_Начало.Файл ::=

MOV dx, o?

MOV ah, 3f

int 21

Проверить.MZ ::=

CMP x?, 'MZ' | (6)

CMP x?, 'ZM'

Проверить.Дату_Время ::=

MOV AX, 5700

int 21

Проверить.Сигнатуру ::= Не_рассматр

 

Проверить.Условие_Заражения.COM ::= (7)

MOV cx, 0

MOV dx, 0

MOV ax, 4202

int 21

ADD ax, o?

CMP ax, FFFF

Снять.Read_Only.Файл ::=

MOV ax, 4300 (8)

MOV dx, o?

int 21

AND cx, FFFE

MOV ah, 01

int 21

Запомнить.Дату.Файл ::=

MOV ax, 5700 (9)

int 21

[ c? o?, cx ]

c? o?, dx

Открыть_Запись.Файл ::=

MOV ah, 3d

MOV al, 2

int 21

Считать_Начало.Файл ::=

MOV ah, 3fh

MOV cx, o?

MOV dx, o?

int 21

Записать_Конец.Файл ::=

MOV cx, 0

MOV dx, 0

MOV ax, 4202

int 21

MOV ah, 40h

MOV cx, o?

int 21

Записать_Начало.Файл ::=

[ MOV cx, 0

MOV dx, 0

MOV ax, 4200

int 21

]

MOV ah, 40h

MOV cx, o?

int 21

Закрыть.Файл ::=

MOV ah, 3eh

int 21

Поставить.Дату.Файл ::=

MOV dx, o?

MOV ax, 5701

int 21

Поставить.Read_Only.Файл ::=

MOV ax, 4301

int 21

Считать.BOOT ::=

MOV ax, 0201 (10)

MOV cx, 0

MOV bx, o?

MOV dx, o?

int 13

Записать.BOOT ::=

MOV ax, 0301 (11)

MOV cx, 0

int 13

Записать.Сектор ::=

MOV ah, 03 (12)

int 13

Проверить.Сигнатуру ::= Не_рассматр

 

Считать.Метку_Диска ::=

MOV ah, 4e

MOV dx, o?

MOV cx, 8

int 21

Найти_обработчик.21 ::= Не_рассматр

 

Найти_обработчик.13 ::=

MOV dx, o? (13)

MOV ah, 13

int 2f

c? o?, ds

c? o?, dx

int 2f

 

Уровень 4 (Пример).

int21.AA ::=

mov ah, AA

int 21

int21.AA ::=

mov ax, AA??

int 21

 

Уровень 0 представляет собой конкретизацию трех необходимых процедур РПС до их наиболее общих типов, например, ПСР.Резидентная, ПНУ.Файловая, ППЗ.Антивирус. Свертка исполняемого кода до такого уровня характеризует свойства РПС (в предложенном примере это будет резидентный вирус, наносящий ущерб файловой системе и имеющий средства борьбы с антивирусными программами).

Уровень 1 описывает алгоритм каждого из типов РПС наиболее общим образом (Найти_Жертву, Скопироваться и т.п.).

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

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

Наконец, уровень 4 представляет наиболее очевидные реализации макроассемблерных функций в виде регулярных ассемблерных сигнатур. Необходимо помнить, что возможно бесконечное количество способов представления выражений уровня 3 на уровне 4. Для реального дерева выбираются наиболее очевидные из них, обычно взятые из реальных РПС.

Таким образом, все конструкции уровня 4 представляют собой сигнатуры РПС в виде регулярных ассемблерных выражений и могут использоваться как признаки РПС, т.к. каждая содержится в том или ином РПС. Однако многие из них вполне невинны и могут содержаться в любой программе (например, как int 21.25, Открыть_Запись.Файл и т.п.). Другие же, наоборот, весьма характерны для РПС: Найти_Себя.Память, Проверить.MZ (они помечены номерами 1..13). Многие не представленные на 4 уровне выражения также будут являться характерными для РПС признаками: Пометить_BAD.FAT, Обработчик_Цикл, Не_зараж.Aidstest, Не_зараж.Scan, Не_зараж.Adinf, Испортить_файлы.Adinf, Найти_обработчик.21, Корректировать_длину.Файл, Корректировать.BOOT и т.д.

4.2. Подходы к построению системы анализа без использования динамических методов исследования.

Как уже отмечалось в п. 3.2, в ИСАИПК различают два основных метода анализа: восходящий с прямой цепочкой рассуждений и нисходящий с обратной цепочкой рассуждений. Рассмотрим возможные реализации каждого из них с учетом ограничения на неиспользование динамических методов исследования.

4.2.1. Подход 1: метод поиска характерных признаков.

Это восходящий метод с прямой цепочкой рассуждений.

Основная его идея состоит во том, что после построения дерева свертки из уровня N берутся наиболее типичные для РПС фрагменты (для нашего дерева это будут помеченные номерами выражения). (Вообще говоря, дерево можно не строить, а получить такие фрагменты, анализируя исходные тексты РПС и представляя основные алгоритмы их работы) и они ищутся на этапе лексического анализа. Этап синтаксического анализа практически отсутствует, а на этапе анализа семантики применяются правила, позволяющие сделать вероятностный вывод о наличии и типе РПС в исходном коде.

Для полученных нами характерных признаков это могут быть правила типа:

ЕСЛИ Проверить. Файл

ТО ПСР. Файловая с вер. 40%.

ЕСЛИ Проверить. Файл

ТО ПСР. Резидентная с вер. 20%.

 

ЕСЛИ Найти_Себя. Память

ТО ПСР. Резидентная с вер. 80%.

 

ЕСЛИ Записать. BOOT

ТО ПСР. Бутовая с вер. 70%.

 

Этот метод привлекает своей простой и очевидностью алгоритмов, и первые "универсальные" антивирусы применяли именно его (первая такая программа, Lie Detector, была представлена в 1989 г.). Более того, простота в данном случае не ухудшает их эффективности в поиске неизвестных вирусов, — Lie Detector занял первое место на Всесоюзной киевской конференции по вирусам 1990 г., детектируя 97% от известных тогда вирусов. Эта программа использовала даже более простые правила, которые сводились по сути только к присваиванию различных весов различным признакам вирусов и потом их суммированию. Если сумма превышала некоторый лимит, то программа объявлялась подозрительной.

Как характерные для вирусов признаки Lie Detector использовал следующие: "базирование", т.е. определение текущего IP (Найти_себя.Память), открытие файла на запись или смещения указателя записи в конец (Открыть_Запись.Файл и Записать_Конец.Файл), изменение даты или атрибутов у файла (Поставить.Дату.Файл, Поставить.Read_Only.Файл), строки типа “*.COM”, “*.EXE”, “*.”', “MZ”, “ZM” (Проверить_Файл, Проверить.MZ). Как мы видим, все эти признаки присутствуют и в нашем дереве на уровне 4, что подтверждает практическую его ценность.

Если к программе такого рода добавить первый этап (снятие защиты), то она может быть вполне конкурентоспособной и сегодня, а по скорости проверки с ней не сможет сравниться ни один метод. (По таблице 3 оценка такой программы равнялась бы 5.0 баллам, уступая только ревизорам!)

Недостатки у такого подхода тоже очевидны. Это все те же ошибки первого и второго рода (см. п. 1.1): программа будет подвержена многим ошибкам второго рода, срабатывая на ни в чем неповинные программы; и, реже, первого рода, если вирус применит некоторые алгоритмы, не попадающие в характерные.

4.2.2. Подход 2: метод полного дизассемблирования.

Второй метод будет относиться к нисходящим с обратной цепочкой рассуждений.

Итак, строится полное дерево свертки и процесс синтаксического разбора идет сверху (иначе говоря, предполагается, что в программе присутствует РПС и последовательно строятся гипотезы, что он относится к типу 1, 2 и т.д.). Одновременно весь входной код полностью дизассемблируется (на этапе 3 рис. 5 и 8 предыдущей главы) и переводится, насколько это возможно статическими методами, на макроассемблер. Теперь для подтверждения текущей гипотезы последовательно ищутся сигнатуры, которые ее подтверждают, уже на языке макроассемблера (для нашего дерева — это уровень 3), а дизассемблированный текст дает некоторую информацию о передачах управления в программе, т.е. о порядке следования этих сигнатур. Таким образом, эта методика включает 2, 3, 4, 5 и 6 этапы.

По достоинствам и недостаткам этом метод полностью противоположен первому, т.к. он будет весьма надежно оценивать наличие РПС в программе (если дерево свертки полно), но применяемые алгоритмы будут нетривиальны и медленны. По таблице 3 оценка такой системы равнялась бы 5.3 баллам.

4.3. Принципы работы системы SubLVA.

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

Для работы системы SubLVA необходимо полное дерево свертки (как и в подходе 2). В исполняемом коде на этапе лексического анализа ищутся наиболее важные или характерные для РПС сигнатуры, задаваемые регулярными ассемблерными выражениями уровня 4 (как в подходе 1). Но далее они поступают не в машину логического вывода, а на следующие этапы. На этапе 3 происходит дизассемблирование кода, расположенного непосредственно вблизи от найденной сигнатуры (выбирается некоторое фиксированное смещение). Этап синтаксического анализа заключается в переводе этого небольшого фрагмента ассемблерного текста на макроассемблер (как в подходе 2). Если перевод будет успешным, т.е. найденная ассемблерная сигнатура является действительно сворачивается в соответствующий не терминал уровня 3, то именно эти нетерминалы поступают на этап семантического анализа, правила которого практически аналогичны правилам из подхода 1.

Таким образом, данный подход сочетает в себе достоинства описанных выше:

4.4. Обзор алгоритмов, необходимых для реализации системы.

4.4.1. Этапы лексического и синтаксического анализа.

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

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

Этап синтаксического анализа является самым сложным для программирования. Как было подчеркнуто, выражения уровня 3 на макроассемблере могут приводить к бесконечному числу выражений уровня 4. Очевидно и обратное — свертка сигнатур уровня 4 в сигнатуры уровня 3 может иметь бесконечное число решений. К счастью, мы знаем обе сигнатуры — найденную и ту, в которую пытаемся свернуть. Поэтому остается только доказать, что фактически найденная сигнатура может получаться из макроассемблерного выражения.

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

Рассмотрим макроассемблерное выражение уровня 3:

 MOV ax, 0С

MOV es, 00

 Это выражение приводит к вполне естественному представлению на уровне 4:

 mov ax, 0C

mov x1, 00

mov es, x1

 И в качестве конкретной сигнатуры могла бы быть найдена такая:

 mov ax, 0C

mov ax, 00

mov es, ax

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

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

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

Для моделирования работы процессора вводится следующие структуры данных: регистры (начальное состояние каждого — “неизвестно”, небольшой стек размером 32 слова (начальное состояние — пуст). Моделируются наиболее распространенные команды: mov, inc, dec, cmp, xchg, xor, add, sub, push, pop и некоторые другие. Приведем типичный пример ассемблерного текста, где предложенная схема правильно определит содержимое регистров (прог. 2):

 

push ss

; начало дизассемблируемого текста.

db F6

; Явно неправильное, т.к. попали в середину команды

...

 

mov ax, bx

; пока ни ax, ни bx не известны

mov bx, 6

 

push bx

; стек содержит 6

call 0123

 

xor ax, ax

; ax = 0

pop bx

; bx = 6, стек снова пуст

mov cx, ax

; cx = 0

int F5

; значения регистров ax, bx, cx известны

...

 

 

Прог. 2. Пример дизассемблированного текста на этапе синтаксического анализа.

 

4.4.2. Этап семантического анализа.

Как было отмечено, блок семантического анализа представляет собой небольшую систему логического вывода, состоящую из машины логического вывода, работающую с фактами (сигнатурами) уровня 3 и базы знаний. База знаний представляет собой правила, по которым может быть проведена вероятностная свертка с уровня 3 на уровень 0. Машина логического вывода оперирует этими правилами и производит эту свертку, получая на выходе выражение уровня 0, т.е. тип РПС с некоторой вероятностью.

4.4.2.1. Машина логического вывода.

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

Суть его состоит в следующем. Проверке подлежит некоторая гипотеза Н, с которой связаны некоторые утверждения А1, А2 и т.д. Тогда, проверив одно из них (A), мы можем пересчитать вероятность гипотезы Н с учетом истинности (форм. 4.1) или ложности (форм. 4.2) этого утверждения:

(4.1)

(4.2)

где:

— априорная вероятность H;

— априорная вероятность (не Н), т.е. 1 - p(H);

, — апостериорные вероятности гипотезы Н;

— вероятность A, если H верна;

— вероятность A, если H неверна;

— вероятность (не A), если H верна, т.е. 1 - p(A/H);

— вероятность (не A), если H неверна, т.е. 1 - .

Для нашего исследования гипотезами будут все выражения уровня 0 (например, пусть проверяется гипотеза H, что РПС — резидентный вирус), а утверждениями — сигнатуры уровня 3 (например, Остаться_Резидентным). Тогда для получения p(H/A) или необходимо знать:

1) априорную вероятность p(H), т.е. заранее заданную вероятность того, что случайно выбранная программа окажется резидентным вирусом. Эта величина, конечно, определяется очень грубо. Для нашего примера пусть это будет 0.001;

2) p(A/H), т.е. вероятность того, что резидентный вирус использует функцию Остаться_Резидентным. Для нашего примера этой вероятностью вполне может быть 0.5, т.к. только около половины вирусов используют для этого стандартные функции;

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

Таким образом, если в коде мы находим сигнатуру Остаться_Резидентным, то по формуле 4.1 получим вероятность того, что это — резидентный вирус, равную 0.024 (т.е. она увеличится 25 раз), а если нет, то по формуле 4.2 — 0.00051 (т.е. уменьшится вдвое). И теперь эта вероятность может выступать в качестве априорной p(H) на следующем шаге, при проверке следующего утверждения, увеличиваясь или уменьшаясь таким образом в зависимости от истинности или ложности текущего утверждения.

В конце мы получим конечные вероятности всех гипотез р(H1/A1A2...As), p(H2/A1A2...As) ... p(Hg/A1A2...As), где g=K+L+M (рис. 7), из которых выбираются те, вероятность которых превышает некоторый порог P (для системы SubLVA P=0.7).

Для данной предметной области байесовский подход имеет следующие преимущества:

1) в полученном дереве свертки, как уже отмечалось, далеко не все сигнатуры уровня 4 присущи исключительно РПС. Наличие вероятности для каждой гипотезы, отражающей тот факт, что эта сигнатура может встретиться в "полезной" программе, позволяет применять формулы 4.1 и 4.2, не задумываясь о том, насколько характерной для РПС является данная сигнатура;

2) формулы 4.1 и 4.2 легко видоизменяются с учетом неопределенностей, возникающей при поиске сигнатур. В блок синтаксического анализа может быть вставлена (и это предполагается сделать) вероятность свертки найденной сигнатуры уровня 4 на уровень 3. (В теории экспертных систем это носит название "неопределенность реакции пользователя").

Пусть вероятность свертки равна r, тогда из 4.1 и 4.2 получается одна общая формула

(4.3)

где:

p(H/A,r) — вероятность гипотезы H с учетом истинности утверждения A с вероятностью r;

p(H/A), — вычисляются по формулам 4.1 и 4.2;

p(A,r) — функция учета неопределенности утверждения A;

= 1 - p(A,r).

Функция p(A,r) должна удовлетворять следующим условиям:

p(A,0) = 0

p(A,1) = 1

p(A,0.5) ищется как решение уравнения

(4.4),

т.е. при отсутствии информации относительно A вероятность p(H/A) не изменяется и остается равной априорной p(H). Решением этого уравнения является

(4.5)

4.4.2.2. Форма представления знаний.

Итак, в базе знаний необходимо иметь следующую информацию: гипотезы принадлежности к тому или иному типу РПС (g штук) плюс априорную вероятность каждой из них p(H) и утверждения — сигнатуры (s штук) плюс две вероятности p(A/H), для каждой.

База знаний имеет следующий формат:

 

Тип_РПС.Уровень.0 ЕСЛИ p(H) // Комментарий

Сигнатура.Уровень.3 p(A/H)

 

а фрагмент ее приведен на рис. 9:

 

ПСР.Резидентная ЕСЛИ 0.001

// Уровень 1 - Проверить_Наличие.Память

Вызвать.Нестд_прер   0.4 0.03

Вызвать.Нестд_функц  0.4 0.03

Сканировать.Память   0.2 0.1

// Уровень 1 - Выделить.Память

int21.AA             0.3 0.9

Манипуляция.MCB      0.5 0.001

Системная_Область    0.2 0.01

// Уровень 1 - Перехватить.Прер

int21.25             0.35 0.95

Запись_Табл          0.65 0.01

// Уровень 1 - Скопироваться.Память

Найти_себя.Память    0.8 0.001

Скопировать.Память   0.95 0.95

Остаться_Резидентным 0.5 0.02

 

ПСР.Файловая ЕСЛИ 0.001

...

 

ППЗ.Дополнительная ЕСЛИ 0.0001

...

 

Рис. 9. Фрагмент базы знаний системы SubLVA.

При анализе приведенной базы можно выявить некоторые ее любопытные особенности. Например, сигнатура int21.AA у вирусов встречается реже, чем у нормальных программ, т.к. они чаще пытаются выделить под себя память незаметно, а все нормальные программы делают это "законными" методами. Это крайний случай того, о чем говорилось в п. 4.4.2.1. А сигнатура Скопировать.Память одинаково часто встречается и в вирусах, и в полезных программах(и, наверное, является вообще одним из самых распространенных действий) и поэтому ее поиск не даст, вообще говоря, никакой новой информации относительно наличия вируса (формулы 4.1 и 4.2 вырождаются в p(H/A) = = p(H) ).

4.5. Резюме.

Система SubLVA показала себя вполне работоспособной, несмотря на то, что использовала далеко не полное дерево свертки и очень грубые оценки вероятностей в базе знаний. Алгоритмы, применяемые в ней, действительно являются достаточно быстрыми, например, анализ средней программы в 30 Кбайт занимает несколько секунд на машине класса 80486DX2-66. Степень достоверности выдаваемой ей информации составила порядка 80% на тестовых примерах, состоящих из реальных экземпляров РПС. Она, бесспорно, подвержена ошибкам первого и второго рода, в частности, оказалось, что почти все программ защиты классифицировались ей как РПС. (Это, вообще говоря, неудивительно, если принять во внимание постоянное их соперничество).

Система нуждается в совершенствовании, как в доработке применяемых алгоритмов (см. п. 4.4.2.1, учет неопределенности при нахождении сигнатуры), так и в присоединении остальных блоков из общей схемы ИСАИПК (в частности, введении динамического анализа).