Вышел (http://kaitai.io/news/2017-02-04.html) очередной релиз языка разбора произвольных бинарных файлов Kaitai Struct 0.6, приуроченный к конференции FOSDEM 2017. Язык позволяет создавать спецификации произвольных бинарные форматы файлов / пакетов / протоколов, после чего можно:
- Визуализировать данные в этом формате в виде интерактивного дерева объектов, наблюдая каким байтам в дампе соответствуют какие значения:- Получить наглядную диаграмму формата с помощью GraphViz
- Скомпилировать спецификацию формата в готовую библиотеку парсинга формата на языках C++, C#, Java, JavaScript, Perl, PHP, Python, Ruby
Среди основных нововведений версии 0.6 можно выделить:
- Поддержка невыравненного (unaligned) побитового чтения (удобно поддерживать битовые поля, можно читать потоки битов и т.д.)- Мощная поддержка метаинформации в .ksy (добавлены поля "title", "license", "ks-version", "ks-debug"), поддержка "doc" на уровне типов
- Enum'ы выведены на уровень типов - т.е. теперь они корректно раскрываются, их можно использовать между разными уровнями вложенности типов (даже если поддержки такой области видимости нет в целевом языке - например, в Python, PHP или JavaScript)
- Идентификатор (id) в атрибутах в seq теперь опционален; если его не указывать, будет автоматически сгенерирован численный идентификатор - это удобно для быстрой разметки неизвестных форматов
- Вычислимые instances теперь могут использовать "if" и "enum"
- Поддержка ссылок на внешние типы данных - можно использовать "type: xxx", где xxx не объявлен в текущем .ksy-файле - это создаст правильные инструкции import / include в предположении, что тип xxx описан в каком-то другом .ksy файле, который будет также собран вместе с этим проектом
- В языке выражений теперь можно использовать числовые литералы с визуальными разделителями (например, "123_456_789" или "0b0101_0011"), а также метод "to_s" у целочисленных типов для преобразования к строке
- Множество других исправлений ошибок и оптимизаций (как по проекту в целом, например, в механизме выведения типов, так и по компиляторам конкретных целевых языков)
URL: http://kaitai.io/news/2017-02-04.html
Новость: http://www.opennet.me/opennews/art.shtml?num=45982
Слакбилд уже есть: https://github.com/saahriktu/saahriktu-slackbuilds/tree/mast... .Только без рантайма оно не очень полезно. Надо ещё рантайм притащить.
> Слакбилд уже есть: https://github.com/saahriktu/saahriktu-slackbuilds/tree/mast...Спасибо! В Arch, кстати, тоже кто-то пакет сделал: https://aur.archlinux.org/packages/kaitai-struct-compiler/ - да еще и весьма оперативно его обновляет. Надо, что ли, список неофициальных пакетов завести?..
> Только без рантайма оно не очень полезно. Надо ещё рантайм притащить.
Какой из? Все 8? Рантайм в норме обычно приносится пакетной системой целевого языка, обычно его незачем паковать (а для C++ еще и довольно вредно - типично его лучше статически линковать, чтобы все однострочные функции инлайнить, но некоторые все равно динамически хотят).
> Надо, что ли, список неофициальных пакетов завести?..А эт твоё, Миш? :)
>> Надо, что ли, список неофициальных пакетов завести?..
> А эт твоё, Миш? :)Ну теперь уже командное ;) А так - начинал несколько лет назад я, да и до сих пор, наверное, процентов 80 кода написано мной.
Биты это интересно. Вот примеров маловато, и в документации мышь повесилась... Но всё равно спасибо.
Документация потихоньку новая пишется в районе https://github.com/kaitai-io/kaitai_struct_doc/blob/master/u...
средство для реверс-инжиниринга?
Не обязательно, но в том числе. Позволяет быстро проверять тучу гипотез при black-box-style реверсинге, чем сильно упрощает жизнь.
А насколько оно удобно для разбора битых данных? Т.е. позволяет с легкостью вклинивать логику, когда что-то не сходится (и этой самой логикой, те кодом на обычном языке переместится на нужное смещение, обходя проблему)?Ну то есть, например, написать на нем средство для извлечения максимума корректных данных из битой postgresql-базы (просто в качестве абстрактного примера). Т.е. чего-то, где в середине потока "едут" заголовки и нужно на внешнюю логику отдавать неразобранные битые куски и просить ее синхронизировать до следующего осмысленного заголовка.
Просто реально горит :) написать такую штуку, парзер со всеми нюансами с нуля писать очень лень (= нет возможности выделить на это время), да и декларативное описание очень бы помогло, т.к. формат может меняться и совершенствоваться. Побитости - реальность (ведется он так, в append-only режиме, и возможны куски с мусором. Основная реализация при считывании этих данных умеет умно обходить этот мусор), но мне ее использовать не выйдет, т.к. она слишком объединена с логикой того продукта.
Читал https://github.com/kaitai-io/kaitai_struct/issues/49 но это не то, нужно именно "если что-то идет не так, отдавать в руки внешней логики, пусть она получает весь битый бинарный блок до следующего заголовка и принимает от нее информацию, что вот тут следующий заголовок". (там возможны разного вида и под-заголовки, и "большие" заголовки, и описать синхронизацию декларативно сложновато).
Да и кусок нужен предыдущий (т.е. корректный заголовок показал, что дальше 2 МБ данных, считали, вернули в приложение, а следующего заголовка не нашлось - значит битость именно в заголовке, который "2 МБ" сообщил была, а следующий кусок - когда найдем заголовок - может и нормальный. Но битые данные все равно нужны).Меня пока интересует просто оценка, можно ли малой кровью такое сделать или лучше не пытаться и писать сразу весь разбор с нуля.
Вопрос про "разбор битых данных" задают примерно с регулярностью пару раз за релиз ;)Проблема в том, что это в целом достаточно комплексная задача, которую надо делить на части:
1. Определять ситуацию, что данные "битые". Это в ближайшее время появится, можно будет поувешивать все всевозможными проверками, чтобы точно гарантировать общую вменяемость читаемого.
2. Реагировать на провалившиеся проверки. Это сложнее, тут надо думать. Самое тупое решение в лоб - провалившаяся проверка выкидывает exception - дальше вызывавший его код ловит и делает какие-то действия по восстановлению, если сочтет нужным, сам. Например, двигается куда-то по стриму и вызывает парсинг повторно. Варианты автоматизации и реализации таких действий внутри ksy надо продумывать. Очевидные варианты типа "сдвинуться на +1, +n, (+n) % m байт и попробовать заново" в принципе можно описать, но иногда бывают и куда более хитрые алгоритмы восстановления, синхронизации и realingment. Например, с откатом назад и особенно противно - с полноценным backtracking, когда надо перепарсить все на несколько уровней вверх, приняв альтернативную парадигму.
Я бы на вашем месте попробовал - вызывать парсинг по кусочкам по идее несложно, никто ж не требует прямо одним вызовом все сделать на автомате. Ресинхронизацию и пропуск при exception'е сделать во внешней вызывающей программе по идее должно быть не так сложно, если я правильно понимаю схему бинарного лога PgSql. В самом крайнем случае никто не мешает взять сгенерированный код и что-то в нем поправить руками. Все лучше, чем совсем вслепую копаться по старинке, без автоматической визуализации и т.д.
Поднимается, потому что задач таких много, а KS штука вкусная, судя по описаниям, и хочется совместить :)1 - сигнатуры следующего заголовка нет в нужном месте, значит битые. С точки зрения автоматического анализа - в том-то и фишка, что тут нужно передать всю бинарщину в "умный" код (не декларативный). А от него в итоге получить в ответ, где следующий заголовок
2 - да, именно это и хочется. Чтобы выкинули и можно было самостоятельно сдвинуться, потом вернуть управление. Все равно "весь" объект разом разбирать не надо (он может быть длиной и в терабайт, и вообще быть живым дописываемом в процессе обработки потоком). Т.е. так сейчас можно?
Условно говоря, иметь внешний алгоритм синхронизации и хук в него. Зачем мучаться и описывать декларативно возможные ошибки, если в обычный код эвристику впихнуть намного проще?3 - pgsql это просто был пример для обрисовывания картины, реально речь идет про другую значительно менее известную штуку (хотя тоже что-то типа БД).
Про поправить код руками я, конечно, понимаю, но вот именно для этого и хотелось бы использовать Kaitai Struct. Там время от времени формат файла меняется, и есть необходимость поддерживать разные версии. Если бы не этот факт, можно было и не брать декларативные описания.. а в текущих реалиях без них просто будет некрасиво. И, конечно, хотелось бы использовать сколько-либо готовое решение, я не писать с нуля автомат. А если код менять руками, то как потом менять описание?.. Поменяешь и опять вручную на этот код накатывать изменения.. брр.
> Поднимается, потому что задач таких много, а KS штука вкусная, судя по
> описаниям, и хочется совместить :)
> 1 - сигнатуры следующего заголовка нет в нужном месте, значит битые. С
> точки зрения автоматического анализа - в том-то и фишка, что тут
> нужно передать всю бинарщину в "умный" код (не декларативный). А от
> него в итоге получить в ответ, где следующий заголовокНу, в самом тупом виде, в управляющей программе что-то типа:
while (!io.eof()) {
long oldPos = io.pos();
try {
Packet pkt = new Packet(io);
} catch (InvalidDataException e) {
// попали на битые данные
long newPos = oldPos + somePacketSize;
// в этот момент от oldPos до io.pos() - читающаяся часть пакета
// весь пакет, видимо, от oldPos до newPos// если он нужен как бинарный кусок
io.seek(oldPos);
byte[] brokenPkt = io.readBytes(newPos - oldPos);// если просто нужно перейти к парсингу с новой позиции
io.seek(newPos);
}
}Это все на самом деле можно уже сейчас. Простая проверка сигнатур есть в KS чуть лин е самого начала.
> 2 - да, именно это и хочется. Чтобы выкинули и можно было
> самостоятельно сдвинуться, потом вернуть управление. Все равно "весь" объект разом разбирать
> не надо (он может быть длиной и в терабайт, и вообще
> быть живым дописываемом в процессе обработки потоком). Т.е. так сейчас можно?Выше пример привел.
> Про поправить код руками я, конечно, понимаю, но вот именно для этого
> и хотелось бы использовать Kaitai Struct. Там время от времени формат
> файла меняется, и есть необходимость поддерживать разные версии. Если бы не
> этот факт, можно было и не брать декларативные описания.. а в
> текущих реалиях без них просто будет некрасиво. И, конечно, хотелось бы
> использовать сколько-либо готовое решение, я не писать с нуля автомат. А
> если код менять руками, то как потом менять описание?.. Поменяешь и
> опять вручную на этот код накатывать изменения.. брр.В общем, если алгоритм выше устраивает - вперед ;)
Ага, спасибо. Ну мне, как человеку, еще не работавшему с этим - не очевидно было, что можно сделать внешний seek() и оно продолжит работать, т.е. оно не хранит кучу внутренних состояний и позволяет вот так просто вмешиваться в обработку. Если позволяет, то все ок. Надо попробовать.
Ну, там все банально. "io" в данном случае - это нативный стрим-объект языка, обернутый в т.н. KaitaiStream. Он и хранит все эти внутренние состояния и т.д., сгенерированный код работает исключительно с ним. Но никто не мешает и самому этот io дергать, в том числе попеременно со сгенерированным кодом.
Попробуй еще construct https://github.com/construct/constructПравда, когда приходится крутиться со всякими сложными условиями: например вставить свой код, для вычисления какой-либо хитрой штуки - хочется выйти в окно автора за то, что он не придумал более легкого способа.
> Попробуй еще construct https://github.com/construct/construct
> Правда, когда приходится крутиться со всякими сложными условиями: например вставить свой
> код, для вычисления какой-либо хитрой штуки - хочется выйти в окно
> автора за то, что он не придумал более легкого способа.Спасибо. Тоже, вероятно, годно. Хотя синтаксис там несколько шизоидный :) https://github.com/construct/construct/blob/master/construct... - тот еще "питон". Но с ExprAdapter фокусы прикольные.
Реально шизоидный - это в Construct3, они там кучу всего накрутили с syntactic sugar ;)А так - инструмент как инструмент для своих задач. Если нужно оставаться строго в рамках одного языка - таких как раз масса. Вот человек тут коллекционирует: https://github.com/dloss/binary-parsing
документация краткая, предлагаю первым пунктом вставить ссылку на тесты, несущие куда больше знаний:
https://github.com/kaitai-io/kaitai_struct_tests/tree/master...а в визуализаторе нет вывода текущего атрибута "doc" в нижнюю строку терминала.
> документация краткая, предлагаю первым пунктом вставить ссылку на тесты, несущие куда больше
> знаний:
> https://github.com/kaitai-io/kaitai_struct_tests/tree/master...Более полная есть пока тут:
https://github.com/kaitai-io/kaitai_struct_doc/blob/master/u...> а в визуализаторе нет вывода текущего атрибута "doc" в нижнюю строку терминала.
Сделаем...
где-то видел список поддерживаемых кодировок для строк, теперь найти не могу.для строк можно будет вызывать внешний перекодировщик-дешифровщик? или свою таблицу как-нибудь впилить?
КОИ-7Н2 не поддерживается :(
> где-то видел список поддерживаемых кодировок для строк, теперь найти не могу.Нет общего списка, есть далекое желание его создать и медленно рихтуемая табличка https://docs.google.com/spreadsheets/d/1l87kGi9_U4Xrgaw2CGaT...
Сейчас строчка передается в поддержку кодировок целевого языка как есть - строчкой.
> для строк можно будет вызывать внешний перекодировщик-дешифровщик? или свою таблицу как-нибудь
> впилить?Если целевой язык поддерживает - то все будет работать. Если нет - то, соответственно, нет. Большинство языков опираются на iconv → нужна поддержка в glibc.
С другой стороны - кто вам мешает забрать строчку в ASCII или просто как массив байт и что-то с ней потом сделать самостоятельно?
> КОИ-7Н2 не поддерживается :(
Честно говоря, сомневаюсь, что оно хоть где-то когда-то будет поддерживается. Стандартизующей организации нет, формальных документов нет, отвечать некому.
>> где-то видел список поддерживаемых кодировок для строк, теперь найти не могу.
> Нет общего списка, есть далекое желание его создать и медленно рихтуемая табличкаЕщё есть {lib,}{recode,enca,rcd}...
>>> где-то видел список поддерживаемых кодировок для строк, теперь найти не могу.
>> Нет общего списка, есть далекое желание его создать и медленно рихтуемая табличка
> Ещё есть {lib,}{recode,enca,rcd}...Есть, но (1) к счастью, большинство языков таки используют реализацию из iconv, (2) пока хоть это бы к чему-нибудь более-менее стандартному свести.
Пока общим знаменателем, кажется, получается система codepages в Windows - она вроде бы меньше всех, правда, там встречается местами какая-то дикая экзотика. Сам по себе современный iconv совершенно гигантский - см. табличку.