ZF

             СИНТАКСИЧЕСКИЙ АНАЛИЗ ПОЛИМОРФНОГО КОДА

                 по мотивам рассказов Хижняка
                     2001, Sassa для ZF#4

          (Материалы к статье см. в каталоге \PLUGIN)


Содержание

    Часть 1. Генератор полиморфного кода
    Часть 2. Детектор полиморфного кода
    Приложение А. Результаты моделирования

Раздаточный материал

    Инструменты для генерации и детекции полиморфного кода

    Blender.jar Генератор полиморфного кода
    Av.jar      Детектор полиморфного кода (рассказ о плаг-ин)

Результаты экспериментов

    Приложение А. Результаты моделирования
    Приложение Б. Протокол обработки каталога с вирусами

Исходные тексты утилит

    Генератор Blender.java и весь пакет poly : Blender.jar
    Детектор utils.tool.Belkodav.java        : Av.jar

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

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

      Прагматический компилятор и синтаксический анализ
                  или по-рабоче-крестьянски
      Полиморфный генератор и детектор полиморфного кода

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

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

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

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

                    Полиморфный генератор

1. Постановка задачи

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

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


2. Анализ задачи

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

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

3. Методы решения

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

3.1 Основы манипулирования данными

    Каждый сейчас может сказать "да мы же это  все  знали  и  так",  но
тогда можно просто вытереть этот файл.

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

    - Автомат  может  создавать только константы (все остальные ресурсы
заданы на вход)

    - Ячейка может быть свободной или занятой.  Свободна -  в  ней  нет
ничего   существенного;  занята  -  содержит  конечное  значение  (цель
достигнута) или промежуточный результат на пути  к  цели.  (Да-да,  это
всего лишь мьютекс!)

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

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

- Константы всегда заняты

    - Стек   всегда   свободен   для   записи   (разве  ограничение  на
переполнение можно ввести)

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

    - Цели   можно   определить   такие:   Свободен(Х),   Постоянен(Х),
Достичь(Х, Значение)

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

- Атрибуты ресурсов:
      Только_чтение(Х), Только_запись(Х)
      (только_чтение - это "всегда занят"; константа)
      (кто бы мог быть "только_запись"? некоторые порты, например)

- Начальные состояния ресурсов:
      Свободен(Х), Занят(Х, Значение)

    Заметим, что  даже  предложенная реализация генератора соответсвует
не всем этим требованиям модели в полной мере.

3.2 Правила работы автомата

      -- это цель;
Достичь(X, Y) ::= Поместить(Х, Y)

      -- это правило; может показаться, что оно эффективно повторяет
      -- цель, но оставим, как есть, чтобы отличать цель и правило
Поместить(Х, Х)  ::=  положить(X,  X)  -- положить - атомарная
операция
Поместить(X, Y) ::= (Свободен(X),  положить(X,Y)) | -- вот вам
раздробление цели
            (Поместить(Z, Y), положить(X,Z)) |
            (Занять(Z), Изменить(F, Y, Z), Поместить(X, Y),
            F(X, Z), Восстановить(F, Y, Z), Освободить(Z))

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

Изменить(F, X, Y) ::= (F=Добавить, Вычесть(X, Y)) |
                  (F=Вычесть, Добавить(X, Y)) | ...

Восстановить(F, X, Y) ::= F(X, Y)

Добавить(X, Y) ::= ...
            -- изголяемся в соответствии с фантазией
            -- сюда записываем варианты сложения двух чисел.
            -- нынче же сяду записывать сложение в виде синтаксической
            -- конструкции, чтобы автомат мог вычислять формулу
            -- самостоятельно

Если язык выхода - ассемблер, то:

положить(X, X) ::= ""
положить(стек, Y) ::= "push Y"
положить(X, стек) ::= "pop X"
положить(X, Y) ::= "mov X,Y"

    Добавим вслух,  что  вот  здесь  и происходит делегация занятости и
целей.  То есть, после "mov X,Y" все, что предназначалось выполнить над
Y, будет выполнено над X.

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


3.3 Алгоритм работы автомата

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

4. Реализация

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

    Процессор реализован на языке Java.  Вопросов интерфейса  программы
касаться не станем ввиду узкости рамок этой статьи.

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

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

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

    Существует также  проблема генерации кода на соответствующем языке.
Пока что эта проблема решена только  для  языка  ассемблер  процессоров
Intel,  хотя само решение не кажется удачным:  это всего лишь ветвление
по типу операндов на этапе,  когда готов мета-код следующей  инструкции
(мета-код  присутствует  в  виде состояния системы в целом (в том числе
IP) и не выражен четко ни в каком коде в обычном смысле  этого  слова).
Заметим, однакоже, что использование в качестве целевых языков высокого
уровня позволило бы реализовать генерацию кода более элегантно.

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

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

                  Детектор полиморфного кода

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

1. Постановка задачи

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

2. Анализ задачи

    Поскольку разработчики  антивирусов редко обладают исходным текстом
генератора,   то   сложность    вызывает    лишь    первичный    анализ
"замусоренного" кода:  анализ  лабораторного  экземпляра.  Здесь  мы не
можем   предложить   ничего,   кроме   разработки    специализированных
инструментов  для  упрощения  такого анализа;  надеемся,  что догадки и
предложения из этой статьи могут оказаться полезными в  этом отношении.
Миф  о  неуловимости полиморфиков-пермутантов-фуллморфиков развеивается
примером Belkodav. Это дополнение к антивирусу Av, иллюстрирующему идею
создания плаг-инов,  и он умеет ловить любые штаммы,  сгенерированные с
помощью кодогенератора VCG.1 "Белка",  причем  проводится  поиск  всего
пермутирующего   тела,   а  не  пятен,  как  это  практикуют  некоторые
антивирусы.  Дополнение не проводит  лишь  поиск  самого  генератора  в
"родительских"  штаммах;  однако,  это  не  существенно,  поскольку это
дополнение  иллюстрирует  лишь  анализ  мутирующего   кода,   а   поиск
вышеупомянутого  кода - обычное сравнение двоичных образов.  Тем более,
он все равно отличает "родительские" штаммы от "порожденных",  хотя, на
лечение это не влияет в случае VCG.1.a, например.

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


3. Методы решения

    Проанализировав кодогенератор  VCG.1,  можно  высказать  следующее:
несмотря на  запутанность  в  случайном  выборе  регистров,  сами  коды
цепочки   инструкций   мутации   находятся  в  строгой  зависимости  от
начального.  Таким образом,  варианты мутаций легко уловить  с  помощью
лексического  анализа  ЛА(1),  поскольку  первый  токен  определяет всю
остальную цепочку синтаксических элементов.  К этому  следует  добавить
лишь  соответствие регистров в тех местах,  где он выбирается случайным
образом.  Однако,  эта  задача  упрощается,  коль  скоро  на  основании
лексического анализа мы знаем вид инструкций, которые следует ожидать в
определенном месте кода.  В этом смысле задача подтверждения гипотез  о
регистрах упрощается.

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

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

- определение границ кода,  подлежащего эвристическому анализу

- определение используемых в нем ресурсов

- ограничение возможного набора инструкций

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

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

4. Реализация

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

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

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

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

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

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

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

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

                        Выводы

Выводы о написанном нижеследующие:

    - усложнение генераторов с учетом многозадачности исполняемого кода
обещает значительно усложнить его анализ

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

    Выводы же о прочитанном, конечно, делать вам самим J


                        Приложение А.

Результаты моделирования

# java -cp Blender.jar Blender -f result -d ax 1 bx 2 cx 3 bp 4 -l 15

      mov      bp, 4            ; bp:=4
      mov      cx, 1            ; cx:=1
      mov      ax, cx           ; ax:=1
      mov      si, ax           ; si:=1
      mov      bx, 3            ; bx:=3
      mov      di, bx           ; di:=3
      mov      bx, si           ; bx:=1
      mov      si, bx           ; si:=1
      mov      ax, si           ; ax:=1
      mov      cx, 2            ; cx:=2
      push      ax              ; <==1
      pop      dx               ; dx:=1
      mov      bx, cx           ; bx:=2
      mov      ax, dx           ; ax:=1
      mov      cx, di           ; cx:=3
---
      mov      dx, 3            ; dx:=3
      push      1               ; <==1
      mov      di, dx           ; di:=3
      mov      dx, 2            ; dx:=2
      pop      cx               ; cx:=1
      mov      si, 4            ; si:=4
      mov      ax, dx           ; ax:=2
      mov      bp, si           ; bp:=4
      mov      si, ax           ; si:=2
      mov      dx, cx           ; dx:=1
      mov      ax, dx           ; ax:=1
      mov      bx, di           ; bx:=3
      mov      cx, bx           ; cx:=3
      mov      bx, si           ; bx:=2
---
      mov      bx, 3            ; bx:=3
      mov      si, bx           ; si:=3
      push      4               ; <==4
      mov      bx, 2            ; bx:=2
      mov      cx, si           ; cx:=3
      push      1               ; <==1
      pop      di               ; di:=1
      mov      si, di           ; si:=1
      pop      bp               ; bp:=4
      mov      di, si           ; di:=1
      mov      ax, di           ; ax:=1
      mov      si, ax           ; si:=1
      mov      di, si           ; di:=1
      mov      dx, di           ; dx:=1
      mov      ax, dx           ; ax:=1

---------------------------------------------------------------
(с) Sassa, Апрель 2001
sassauk@mail.ru
sassa@apiary.sumy.ua


(C) NF, 1998-2004