# binpac ###### tags: `summary`, `binpac` [TOC] --- ## Введение ***binpac*** — декларативный язык описания формата сообщений с самостоятельным синтаксисом (не YAML/XML/etc.). Ориентирован на анализ сетевого трафика. Внедряется в виде плагинов для [IDS Zeek (Bro)](https://www.zeek.org/). Развитая инфраструктура языка открытая ([GitHub](https://github.com/zeek/binpac)). Обзорная статья [binpac: A yacc for Writing Application Protocol Parsers](https://www.icsi.berkeley.edu/pubs/networking/binpacIMC06.pdf) | Достоинства | Недостатки | | -------- | -------- | | возможность описывать stateful протоколы | несамостоятельность инфраструктуры | | нацеленность на бинарные форматы | сложный синтаксис | ## Описание языка Описание формата в *binpac* состоит не только из непосредственного описания структуры бинарного файла, но и из вспомогательных секций, отвечающих за механизмы организации потока байт и соединений. Это позволяет моделировать ситуации чтения пакетов протокола, идущих в двух направлениях (например, если анализатор развернут на промежуточном узле) или добавлять различные пользовательские обработчики разобранных данных. Все эти определения записываются в один `.pac` файл, подающийся на вход компилятору. ### Секции `analyzer`, `connection` и `flow` В этих секциях описывается глобальный контекст парсера. Примеры: ``` analyzer HTTP withcontext { connection : HTTP_analyzer; flow : HTTP_flow; }; ``` ``` connection HTTP_analyzer { upflow = HTTP_flow (true); downflow = HTTP_flow (false); }; ``` ``` flow HTTP_flow(is_orig) { flowunit = HTTP_PDU(is_orig) withcontext (analyzer, this); }; ``` ### Секция `type` Основная секция, отвечающая непосредственно за описание формата. Типы в *binpac* могут быть составными из любых других ранее определенных типов. Список встроенных (*примитивных*) типов: `int8`, `int16`, `int32`, `uint8`, `uint16`, `uint32`, `bytestring`, регулярное выражение (`RE/[[:alnum:][:punct:]]+/`) При описании пользовательского типа задается: * имя типа * параметры (опционально, для параметрических типов) * класс типа (составной, условны и т.д.) * список членов типа (подтипы, из которых он составлен) * дополнительные атрибуты типа (порядок байт и т.п.) Синтаксис определения одного типа: ``` type <Name>{(<optional type parameter(s)>)} = <compositor or primitive class> { ... cases or members declaration. } <optional attribute(s)>; ``` Классов типов в *binpac* четыре: * примитивный (`primitive`) * составной (`record`) * условным (`case`) * массив (`array`) Примитивные типы можно только использовать (переопределять нельзя). Пример `.pac` файла: ``` analyzer HelloWorld withcontext { connection : HelloWorld_analyzer; flow : Myflow; }; connection HelloWorld_analyzer { upflow = Myflow (true); downflow = Myflow (false); }; flow Myflow(side: bool) { datagram = myType withcontext (HelloWorld_analyzer, this); }; type myType = record { data: uint8; }&byteorder = bigendian; ``` ### Возможности языка Ниже приводятся примеры, описывающие возможности языка. #### Порядок байт ``` type myType = record { data: uint8; }&byteorder = bigendian; ``` #### Выравнивание байт ``` type RPC_Opaque = record { length: uint32; data: uint8[length]; pad: padding align 4; # выровнять до 4-х байт }; ``` #### Расширение атрибутов типов Имея некоторый заданный тип, есть возможность расширять его атрибуты с помощью ключевой секции `refine`: ``` refine typeattr HTTP_RequestLine += &let { process_request: bool = process_func(method, uri, version); }; ``` Здесь мы добавили типу `HTTP_RequestLine` атрибут `&let` (о нем ниже). Также можно расширять условия в условных типах и условные функции: ``` refine casetype RPC_Params += { RPC_SERVICE_PORTMAP -> portmap: PortmapParams(call); }; refine casefunc RPC_BuildCallVal += { RPC_SERVICE_PORTMAP -> PortmapBuildCallVal(call, call.params.portmap); }; ``` #### Определение функций #### Встраивание кода C++