Семьянов П.В., Центр Защиты Информации СПбГТУ АВТОМАТИЗАЦИЯ ПОИСКА НЕИЗВЕСТНЫХ РАЗРУШАЮЩИХ ПРОГРАММНЫХ СРЕДСТВ В ИСПОЛНЯЕМОМ КОДЕ С появлением и продолжающейся интенсивной и экстенсивной эво- люцией разрушающих программных средств (РПС), к которым относятся компьютерные вирусы, троянские кони и средства НСД, особенно ак- туальной становится задача поиска неизвестных РПС в поступающем программном обеспечении, т.е. в исполняемом программном коде. Она разбивается на ряд подзадач: 1) задача представления РПС, которая состоит в создании адек- ватных моделей, удобных для практического применения. Оказалось, что удобно использовать разработанную в СЦЗИ т.н. функциональная модель, в которой структура РПС описывается как набор функциональных элементов. В качестве базовых функциональных элементов в ней выбраны процедура нанесения ущерба (ПНУ), харак- терная для компьютерных вирусов; процедура саморепродукции (ПСР), характерная для троянских коней; и процедура преодоления защиты (ППЗ), присущая средствам НСД. Тогда РПС на функциональном уровне можно определить как: "РПС будем называть алгоритм, реализующий по крайней мере одну из этих процедур". 2) разработка теоретических основ анализа и общей структуры автоматизированной интеллектуальной системы анализа исполняемого кода программ (ИСАИПК); Решение этой задачи будет рассмотрено в настоящем докладе. Итак, с учетом выбранной модели, для отнесения входной прог- раммы к классу РПС достаточно найти в ее составе код, реализующий одну из трех процедур. Таким образом, решаемая задача может быть конкретизирована как анализ исполняемого кода на предмет нали- чия заданной функции. В дальнейших рассуждениях все время будет проводиться анало- гию между анализом программ в исполняемых кодах и анализом прог- рамм на языке высокого уровня, т.е. теорией компиляторов и фор- мальных языков. При анализе программ на любом языке обязательно присутствуют три этапа: лексического анализа, синтаксического анализа и семан- тического анализа. Оказывается, что эта схема может применяться и для решаемой задачи, причем указанные три этапа являются ос- новными и обязательными, а перед каждым из них соответственно мо- жет включатся по еще одному предварительному этапу: выделения чистого кода, дизассемблирования и перевода на язык более высоко- го уровня (ЯБВУ). Таким образом, мы получаем шестиэтапную схему анализа исполняемого кода (рис. 1): Исходный исполняемый код ║ V ┌──────────────────────┐ ┌──────────────────────────────┐ │ 1. Снятие защиты │ │ 2. Лексический анализ │ ╞══════════════════════╡ ╞══════════════════════════════╡ │ Получение чистого ╞═══> │ Поиск и распознавание │ │ исполняемого кода │ │ │ сигнатур РПС │ └─────╥────────────────┘ │ └─────────╥────────────────────┘ ║ чистый │ ║ лексическая V ─────── код ───┘ V информация ┌──────────────────────┐ ┌──────────────────────────────┐ │ 3.Дизассемблирование │ │ 4. Синтаксический анализ │ ╞══════════════════════╡ ╞══════════════════════════════╡ │ Получение ассемблер- ╞═══> │ Свертка нетерминалов в терми-│ │ ного кода │ │ │ налы возможно более высокого │ │ │ │ │ уровня │ └─────╥────────────────┘ │ └─────────╥────────────────────┘ ║ ассемблерный │ ║ синтаксическая V ──── текст ─────┘ V информация ┌──────────────────────┐ ┌──────────────────────────────┐ │ 5. Перевод на ЯБВУ │ │ 6. Семантический анализ │ ╞══════════════════════╡ ╞══════════════════════════════╡ │ Получение алгоритма ╞═══> │ Завершение построения дерева │ │ на ЯБВУ │ │ │ грамматического разбора │ └──────────────────────┘ │ └─────────╥────────────────────┘ текст │ ║ на ЯБВУ ────┘ V Информация о наличии РПС и его свойствах Рис. 1. Поэтапная схема анализа исполняемого кода. Не останавливаясь на предварительных этапах, рассмотрим лишь основные. Этап лексического анализа является одним из наиболее важных, до сих пор практически все антивирусные программы ограничивались только им. В теории компиляторов лексический анализ - это процесс распознавания во входном тексте смысловых единиц: лексем. По ана- логии, лексическим анализом в нашем случае будем называть процесс поиска и распознавания лексем в исполняемом коде программ, ко- торыми в данном случае будут являться определенные регулярные последовательности, называемыми сигнатурами РПС. Сигнатурой РПС будем называть любую регулярную последовательность байт, встре- чающуюся в исполняемом коде РПС. Как видно, это понятие гораздо шире понятия "сигнатура вируса". Здесь проявляется первое отличие от лексического анализа в понимании теории компиляторов. Там для его реализации достаточно простых сканирующих алгоритмов. Здесь же специфика задачи приво- дит к тому, что могут потребоваться не только алгоритмы поиска регулярных числовых выражений, но и целый язык регулярных ас- семблерных и макроассемблерных выражений для представления лек- сем. Синтаксическим анализом при проектировании компиляторов называют процесс отождествления лексем, найденных во входной це- почке, одной из языковых конструкций, задаваемых грамматикой языка (или, иначе, процесс построения дерева грамматического раз- бора). Грамматика языка задает, по каким правилам из лексем (тер- минальных символов или терминалов) строятся его предложения. Т.е., применительно к РПС, мы должны формализовать "грамматику" РПС, описывающую, по каким правилам из лексем (сигнатур РПС) строятся "предложения" (т.е. различные типы РПС). Тогда, синтак- сический анализ исполняемого кода программ состоит в отождествле- нии сигнатур РПС одному из типов РПС. Представим грамматику РПС в виде т.н. грамматического дерева или дерева свертки (рис.2). (Не в коем случае не следует путать это дерево с деревом грамматичес- кого разбора!). ┌─────────┐ ┌───────────────┤ РПС ├────────────────┐ │ └────┬────┘ │ ┌───────┴───────┐ ┌────────┴───────┐ ┌───────┴─────┐ │ процедура │ │ процедура │ │ процедура │ │ саморепро- │ │ нанесения │ │ преодоления │ │ дукции │ │ ущерба │ │ защиты │ └───────┬───────┘ └────────┬───────┘ └───────┬─────┘ │ Уровень 0 │ │ │ │ │ ┌────┼────┐ ┌─────┼─────┐ ┌────┼────┐ ┌──┴──┐ │ ┌──┴──┐ ┌──┴──┐ │ ┌──┴──┐ ┌──┴──┐ │ ┌──┴──┐ │тип 1│...│тип K│ │тип 1│ ... │тип L│ │тип 1│...│тип M│ └──┬──┘ └──┬──┘ └──┬──┘ └──┬──┘ └──┬──┘ └──┬──┘ └───┐ │ Уровень 1 ┌──────┴──────┐ │ действие 1 ├────────────────────┐ ├─────────────┤ │ │ действие 2 ├─ │ ├─────────────┤ │ │ ... │ │ ├─────────────┤ │ │ действие D1 ├─ │ └─────────────┘ │ Уровень 2 │ ┌────────────────────────┼─────────────────┐ ┌─────────┴──────────┐ ┌──────────┴─────────┐ │ │ поддействие 1,1,1 │ │ поддействие 1,1,2 │ │ поддействие 1,2,1 │ │ поддействие 1,2,2 │ │ ... │ │ ... │ ... │ поддействие 1,P1,1 │ │ поддействие 1,P2,2 │ └─────────┬──────────┘ └──────────┬─────────┘ ... Уровень N ┌────┴────┐ ┌────┴────┐ ┌────┴────┐ │ сигн. 1 │ │ сигн. 2 │ ... │ сигн. S │ └─────────┘ └─────────┘ └─────────┘ Рис. 2. Макет грамматики РПС в виде дерева свертки. Уровень 0 представляет собой конкретные типы РПС, на уровне 1 они раскрываются достаточно общими функциями, описывающими их алгоритм, на уровне 2 каждая из этих функций конкретизируется функциями более низкого уровня и т.д. до уровня N-1, где появ- ляются сигнатуры, причем на уровне N-1 они представлены макроас- семблерными регулярными выражениями, а на N-ом уровне - ас- семблерными регулярными выражениями. Важно отметить, что специфи- ка макроассемблерных сигнатур такова, что каждому выражению уров- ня N-1 соответствует бесконечное или достаточно большое коли- чество сигнатур уровня N. Таким образом, уровни 0..N-2 содержат нетерминалы, а уровень N или N-1 (в зависимости от того, какой уровень представления сигнатур необходим) - терминальные символы. Если бы существовала полная аналогия между анализом программ в исполняемом коде и компиляторами, то на этом этапе анализ прог- рамм мог быть закончен, т.к. входная цепочка лексем (исполняемый код) должна была бы быть либо принята синтаксическим анализатором (РПС присутствует), либо отвергнута (РПС нет). Однако при компи- ляции программ цепочка задается абсолютно строго: известны ее длина, порядок следования терминальных символов и т.п. Так же строго задается и грамматика входного языка. При анализе исполняемого кода это не так: 1) могут быть не распознаны некоторые лексемы. Это следует из того, что конструкции уровня N-1 могут выражаться бесконечным количеством конструкций уровня N; 2) порядок следования лексем может быть известен с некоторой вероятностью или вообще неизвестен. Передачи управления в прог- рамме приводят к тому, что лексемы, стоящие рядом в программном файле, могут исполняться совершенно непоследовательно, и наобо- рот. 3) грамматика языка может обновляться, т.к могут возникать новые типы РПС или механизмы их работы. Таким образом, на этапе синтаксического анализа формально производится свертка в терминалы возможно более высоких уровней, а окончательное заключение об отсутствии или наличии РПС можно дать только на этапе семантического анализа. Семантический анализ - это анализ алгоритма программы. Фор- мально на этапе семантического должна быть закончена свертка в нетерминалы уровней 0, 1, 2. Очевидно, что нечеткость имеющейся информации приводит к появлению на этом этапе некоторой машины логического вывода со всеми присущими ей элементами: форме представления знаний и правил, алгоритмами логического вывода, вероятностными заключениями и т.п. Из объединения методов синтаксического разбора (существуют восходящие и нисходящие методы) и методов логического вывода (с прямой цепочкой рассуждений и с обратной цепочкой рассуждений) были получены два метода синтаксическо-семантической свертки: нисходящие с прямой цепочкой рассуждений и восходящие с обратной цепочкой рассуждений. Первые последовательно строят гипотезы, что РПС относится к типу 1, типу 2 и т.д. (т.е. начинает с корня дерева свертки) и постепенно спускаются вниз, на каждом этапе пытаясь опровергнуть или подтвердить свою гипотезу, ища соответствующие сигнатуры. Здесь машина логического вывода является первичной, а синтакси- ческий анализатор помогает ей в подборе фактов. Вторые начинают с терминалов уровня N и постепенно подни- маются вверх, сворачивая их в нетерминалы высших уровней с соот- ветствующими вероятностями. Здесь первичной будет синтаксическая свертка, которую облегчают вероятностные рассуждения семантичес- кого анализатора). Предложенная схема является общей теоретической схемой ана- лиза. Ее несомненным достоинством является инвариантность к опе- рационной системе и к возможным типам РПС к этой ОС. Для построения системы анализа для конкретной ОС необходимо: 1) построить дерево свертки для этой ОС; 2) определить необходимые для этапа семантического анализа вероятности (в т.ч. априорные вероятности появления всех типов РПС); Исходя из полученных данных, выбрать: 3) уровень представления сигнатур в этом дереве для этапа лексического анализа (N или N-1); 4) метод синтаксическо-семантической свертки (снизу-вверх или сверху-вниз); и 5) разработать конкретные алгоритмы основных и предвари- тельных этапов. * * *