на C#, Java, PHPКочуркин Иван, Positive Technologies
Почему сигнатурный анализ кода?–Проще в разработке, поддержке и тестировании.–Масштабируемость на другие языки.–Обработка файлов с синтаксическими ошибками.
Зачем унифицированное AST?–Не все паттерны описываются регулярными
выражениями.–Возможность реализации taint-анализатора
благодаря связанности AST.3
Ivan Kochurkin
При матчинге в чистом исходном коде. Кроме того, унифицированноть позволяет избежать "мусора" различных языков. Например, знак доллара перед переменной в PHP.
Ivan Kochurkin
В регулярном выражении очень неудобно пользовать бек-референсами.
Ivan Kochurkin
В динамическом анализе требуется не просто валидный синтаксис исходного кода, но и семантическая связанность кода, потому что происходит его выполнение. Сигнатурный анализ кода способен обрабатывать файлы даже с синтаксическими ошибками (например, отсутствующая точка с запятой).
Ivan Kochurkin
Благодаря разработанной архитектуре нашего ядра, для поддержки нового языка или диалекта потребуется разработать модуль конвертера и парсера. матчинг разрабатывать уже не придется.
Workflow
4
Ivan Kochurkin
В начале я опишу этапы работы нашего ядра паттерн-матчинга или PM. Это:1. Парсинг в зависимое от языка AST2. Преобразование в независимое от языка AST3. непосредственно паттерн-матчинг.Пока что наше ядро функционирует для языков C#, Java и PHP
Теория парсинга• Лексер + Парсер:
stmt -> e ; | if (e) stmt | for (e ; e ; e) | other ...
Лексический анализ (сканирование) - группировка символов исходного кода в значащие последовательности, называющиеся лексемами. Токен - это лексама + ее тип. Например, в типичных ЯП токенами являются: Id, String, Integer, Keyword (If, While, For), скобки и т.д.Синтаксический анализ - создание древовидного представления или AST, которое описывает грамматическую структуру токенов. Листями в этом дереве являются токены, а узлами - тип операции. Например, IfElseStatement, арифметическое выражение и т.д.Семантический анализ - проверка на согласованность с определениями языка. Например, соблюдение согласованности типов.В данном примере токенами являются sp, =, 100, ;Для описания создания различных узлов AST дерева используются правила из потока токенов, объединение которых называют грамматикой. Здесь правилом является assign и stat.
Ivan Kochurkin
Преимущества и недостатки LL и LR парсинга (в LL нельзя использовать левую рекурсию, однако в ANTLR4 - можно, несмотря на то, что там LL (из-за улучшенного алгоритма)).
Проблематика парсинга
• Распознавание ключевых слов, как идентификаторов.id : ID | null | if | else | … /* другие keyword */ … ;
• Контекстно-зависимые– C#, Java (если проверка семантики на этапе парсинга: T a = new T())– PHP конструкция Heredoc: $x = <<<EOL Hello world EOL;– Пример: anbnсn
8
Ivan Kochurkin
"Регулярное выражение не умеет считать".
Ivan Kochurkin
КС-грамматика может считать две вещи, а не три.
Ivan Kochurkin
На этом слайде представлены виды грамматик и различия между ними.
• Roslyn + Высокая производительность. + Продуманность и качественный парсинг, Microsoft. + Хорошая поддержка на GitHub. - Парсинг только C# и VisualBasic.
• ANTLR + Универсальность (много грамматик и различные runtime). + Хорошая поддержка на GitHub. - Плохая производительность в случае глубокой рекурсии. - Искусственный код парсера из-за автогенерации.
10
Ivan Kochurkin
Другого выбора особо и не было =)
Ivan Kochurkin
Зато этот код можно отлаживать. Для остальных языков.
Visitor vs Listener (Walker)
• Listener• Visitor
11
Ivan Kochurkin
Перейдем к этапу преобразования AST в унифицированный формат. Для обхода AST существует две стратегии: Visitor и Listener (или Walker в Roslyn). Разница между ними состоит в том, что в Visitor необходимо в ручную вызывать методы обхода потомков синтаксических узлов, а в Listener все узлы обходятся автоматически. Более того, в Visitor возможно возвращать значение при обходе. Таким образом, код преобразования дерева получается более функциональным и лаконичным (не использутеся глобальный стэк). На рисунке видим, что, например, при преобразовании языка PHP, ненужные узлы HTML и CSS не посещаются.
Преобразование AST
// ifStatement// : If parenthesis statement elseIfStatement* elseStatement?// | If parenthesis ':' innerStatementList elseIfColonStatement* elseColonStatement? EndIf ';'// ;public override AstNode VisitIfStatement(PHPParser.IfStatementContext context){ throw new NotImplementedException();}
• parenthesis – единичное выражение.• elseIfStatement* – массив из нуля или большего количества.• elseStatement? – если отсутствует, то null.• throw new ShouldNotBeVisitedException() – если узел не должен
посещаться.• VisitAndReturnNullIfError() – используется для коллекций.
12
Ivan Kochurkin
На этом слайде описан пример метода-визитора VisitIfStatement. В комментариях описано правило соответствующего посещающегося узла. AstNode - тип узла унифицированного дерева. Как понятно из описания, на этом этапе, весь код уже представлен в структурированном виде.
Структура типов унифицированного AST
13
Ivan Kochurkin
Все узлы являются наследником AstNode. Особое внимание стоит уделить Expression, Statement и Literal.Expression - обычное ариметическое выражение, вызов метода, обращение к члену объекта и тд.Literal - примитивное строковое, числовое или другое значение.Statement - выражения, заканчивающиеся точкой с запятой в типичных ЯП. Будет использоваться главным образом для тайнта.Узлы с приставкой Pattern - являются искусственными и предназначены для того, чтобы при обработке матчить не просто одно литеральное значение или выражение, а целый класс. Например, литерал по регулярному выражению или диапазон чисел.
Алгоритм сопоставления в AST
14
Ivan Kochurkin
На этом слайде описаны основные принципы сравнения узлов в AST. Паттерн представляет кусочек AST.При обходе унифицированного AST (оно обходится в глубину), каждый кусочек "пытается" наложится на кусочек AST текущего узла.В начале сравнивается тип узла. Если узел является конечным (терминальным), то существует 4 типа сравнений.1. Рекурсивное сравнение потомков.2. Сравнение простых литеральных типов (идентификатор, строки, числа).3. Сравнение расширенных литеральных типов (регулярные выражения, диапазоны). Комментарии входят в эту группу.4. Сравнение сложных расширенны типов (выражения, последовательность Statement).Благодаря разработанной технологии матчинга (представление типа узла в виде Enum (то есть целого числа), удалось достигнуть высокой производительности при сравнительно небольшом количестве кода (метод Compare реализован для базового класса и терминалов).Стоит отметить, что сравниваются узлы, а не строки.
Язык описания паттернов
• Hardcode+ Отсутствие какого-либо парсинга.- Громоздкий и нечитаемый синтаксис.- Невозможность добавления своих паттернов без перекомпиляции.
• DSL.+ Краткий и лаконичный синтаксис.- Необходимость разрабатывать грамматику и конвертер для нее.
15
Ivan Kochurkin
Все паттерны или "кусочки дерева" необходимо загружать в программу каким-либо образом. И на этом слайде описаны подходы для описания паттернов.Во-первых, паттерны можно записывать в ручную прямо в коде (что делается у нас в других ядрах).Для этого не требуется разрабатывать какой-либо парсер. Конечно, данный подход не подходит для записи паттернов "не программистом", однако может использовать и использовался для написания юнит-тестов. Кроме того, для внесения новых паттернов, требуется перекомпиляция всей программы.Во-вторых, паттерны можно представлять в распространненом универсальном формате передачи данных, JSON. При этом подходе паттерны можно загружать извне, однако при этом синтаксис будет громоздким и неприемлимым для редактирования человеком. Однако данный формат можно использовать для сериализации древесных структур (сохранения и загрузки с диска).И, наконец, третий подход заключается в разарботке специального предметно-ориентированного языка (или DSL), который можно было бы легко редактировать, который был бы лаконичным, но который при этом был бы достаточно выразительным для описания существующих или будущих паттернов. Недостатком такого подхода является разработка синтаксиса, а потом и парсера для такого языка.
Целесообразность DSL?
16
Ivan Kochurkin
Часто задают вопрос, а нужен ли наш собственный DSL, если существуют регулярные выражения? Зачем еще один 15 формат?
DSL ≠ Regex
DSL – это регулярные выражение + синтаксические конструкции.
• DSL - не нужно больше думать о проблемах пробелах.
17
Ivan Kochurkin
Ответ: да, нужен! DSL - это больше, чем регулярные выражения, это регулярные выражения, смешанные с синтаксическими конструкциями, похожие на конструкции из стандартных языков программирования. С помощью регулярок тяжело записывать длинные и хоть сколько-нибудь сложные выражения. Ну и у DSL название говорит само за себя: это язык, предназначенный для конкретной предметной области, он внутренний и не будет использовать в качестве какого-либо стандарта.
Синтаксические конструкции в DSL
• expr(args) – вызов метода.• expr.Id – обращение к члену объекта (поле, метод).• Id expr = expr – инициализация переменной.• expr + expr – конкатенация.• new Id(args) – создание объекта.• expr[expr] – обращение по индексу или ключу.
18
Ivan Kochurkin
Можно руководствоваться этим: https://wiki.ptsecurity.com/display/AI/DSL
Ivan Kochurkin
Что же представляют собой регулярные выражения с синтаксическими конструкциями?Прежде расскажу о синтаксических конструкциях: это обычные операции, заимствованные из типичных языков программирования: типа вызов метода, обращения к члену объекта, инициализации переменной .... На этом слайде в эти конструкции вставлены такие аббревеатуры, как expr, args и Id.
Синтаксис DSL• <[]> - оператор расширенного выражения (<[md5|sha1]> или <[0..2048]>)• # или <[expr]> - любое Expression• ... или <[args]> - произвольное количество любых аргументов• (expr.)?expr – эквивалентно expr.expr или просто expr• ~expr – отрицание условия.• expr (<[||]> expr)* - объединение нескольких условий (ИЛИ).• Comment: “regex” – поиск по комментариям.
Типы значений в DSL Id, String, Int, Bool, Null
19
Ivan Kochurkin
Прежде всего expr, или expression может являться обыкновенным примитивным значением типа Id, String, Int, Bool, Null. Однако если нужно матчить не просто одну строку, идентификатор или число, а строку, идентификатор по регулярному выражению или диапазон числе, то необходимо воспользоваться так называемым расширенным выражением, который обозначается так <[]>.Также в нашем DSL для многих частоиспользуемых конструкций предусмотрен сокращенный синтаксис, называемый "синтаксическим сахаром". Например, решетка обозначает любой Expression, три точки - произвольное количество любых аргументов.Интересными дополнительными конструкциями являются отрицание условия (например, когда требуется, чтобы в качестве аргумента указывалась не строка) и объединение нескольких условий, т.е. логическое ИЛИ для паттернов. Также была введена конструкция для поиска именно по комментариям, а не по коду: Comment.Стоит отметить, что синтаксис нашего DSL все еще в процессе фидбека, разработки и упрощения.
• Insecure Cookie (Java):Cookie <[cookie]> = new Cookie(...);...~<[cookie]>.setSecure(true);...response.addCookie(<[cookie]>);
22
Ivan Kochurkin
Рассказать про back-reference в регулярных выражениях и <[cookie]>.
Тестирование
• All In One – Тестирование парсинга разнообразного синтаксиса.• Patterns – Детекция всех известных паттернов в подготовленных файлах.• Patterns with Errors – то же, что и Patterns, но с синтаксическими