Як ви налагоджуєте двійковий формат?


11

Я хотів би мати можливість налагодити побудову бінарного будівельника. Зараз я в основному роздруковую вхідні дані до двійкового аналізатора, а потім заглиблююся в код і роздруковую відображення вводу на вихід, потім беручи відображення виводу (цілі числа) і використовуючи це, щоб знайти відповідне ціле число у двійковій. Досить незграбний, і вимагає, щоб я глибоко змінив вихідний код, щоб потрапити на відображення між входом і виходом.

Здається, ви могли переглянути дворядне зображення в різних варіантах (у моєму випадку я хотів би переглянути його у 8-бітових фрагментах у вигляді десяткових чисел, оскільки це досить близько до вводу). Насправді деякі числа 16-бітні, деякі 8, деякі 32 і т. Д. То, можливо, був би спосіб переглядати двійкові дані з кожним із цих різних чисел, виділених в пам'яті певним чином.

Єдиний спосіб я бачив, що це можливо, це якщо ви фактично побудуєте візуалізатор, характерний для фактичного бінарного формату / макета. Тож воно знає, де в послідовності повинні бути 32-бітові числа, а де повинні бути 8-бітні числа тощо. Це велика робота та певна хитрість у деяких ситуаціях. Тож цікаво, чи існує загальний спосіб це зробити.

Мені також цікаво, що зараз є загальним способом налагодження такого типу речей, тому, можливо, я можу отримати деякі ідеї щодо того, що спробувати з цього.


75
Ви отримали одну відповідь: "використовуйте hexdump безпосередньо, і зробіть це і те додатково" - і ця відповідь отримала багато відгуків. І друга відповідь, через 5 годин (!), Сказавши лише "використовувати гекс-дамп". Тоді ти прийняв другий на користь першого? Серйозно?
Док Браун

4
Хоча у вас може бути вагома причина використовувати двійковий формат, задумайтесь, чи можете ви просто використовувати існуючий текстовий формат, наприклад JSON. Читання людини налічує багато, а машини та мережі, як правило, досить швидкі, що використання користувацького формату для зменшення розміру сьогодні не потрібне.
jpmc26

4
@ jpmc26 є ще багато користі для бінарних форматів і завжди буде. Читання людини зазвичай є вторинним рівнем продуктивності, вимог зберігання та продуктивності мережі. І все ж є багато областей, де особливо продуктивність мережі низька, а обсяг пам’яті обмежений. Крім того, не забувайте про всі системи, що мають взаємодію зі застарілими системами (як апаратні, так і програмні) та підтримують їх формати даних.
відбувся

4
@jwenting Ні, насправді час розробника зазвичай є найдорожчою програмою. Звичайно, це може бути не так, якщо ви працюєте в Google або Facebook, але більшість додатків не працюють у такому масштабі. А коли ваші розробники витрачають час на речі - це найдорожчий ресурс, читабельність людини нараховує набагато більше, ніж 100 мілісекунд додатково, щоб програма її розібрала.
jpmc26

3
@ jpmc26 Я не бачу нічого в питанні, яке б підказало мені, що ОП є тим, що визначає формат.
JimmyJames

Відповіді:


76

Для спеціальних перевірок просто використовуйте стандартний шестигранник і навчитеся його оком.

Якщо ви хочете підготувати належне розслідування, я зазвичай пишу окремий декодер на щось на зразок Python - в ідеалі це буде керуватися безпосередньо з документа специфікації повідомлення або IDL і бути максимально автоматизованим (так що немає шансів ввести вручну однакова помилка в обох декодерах).

І нарешті, не забувайте, що ви повинні писати одиничні тести для свого декодера, використовуючи відомі правильні консервовані дані.


2
"просто використовуйте стандартний шестигранник і навчитеся його". Так. На мій досвід, на дошці для групового порівняння можна записати кілька розділів, що мають до 200 біт, що іноді допомагає розпочати подібні речі.
Щогла

1
Я вважаю, що окремий декодер вартий зусиль, якщо двійкові дані відіграють важливу роль у застосуванні (або загалом у системі). Це особливо актуально, якщо формат даних є змінним: дані у фіксованих макетах можна помітити в шестигранному просторі з невеликою кількістю практик, але швидко потрапляють на стіну практичності. Ми налагодили трафік USB та CAN за допомогою декодерів комерційних пакетів, і я написав декодер PROFIBus (де змінні поширюються по байтах, цілком нечитабельні в шестигранному дампа), і вважаю, що всі три з них надзвичайно корисні.
Пітер -

10

Першим кроком для цього є те, що вам потрібен спосіб знайти або визначити граматику, яка описує структуру даних, тобто схему.

Прикладом цього є мовна особливість COBOL, яка неофіційно відома як "copybook". У програмах COBOL ви б визначали структуру даних у пам'яті. Ця структура відображалася безпосередньо до способу зберігання байтів. Це є загальним для мов тієї епохи, на відміну від поширених сучасних мов, де фізична компонування пам'яті викликає занепокоєння щодо впровадження, яке віддаляється від розробника.

Шукати мову схеми бінарних даних Google в Google знайде ряд інструментів. Приклад - Apache DFDL . Для цього також може бути призначений інтерфейс користувача.


2
Ця функція не зарезервована до мов «давньої» епохи. Структури C та C ++ можуть бути вирівняні в пам'яті. C # має StructLayoutAttribute, який я використовую для передачі двійкових даних.
Каспер ван ден Берг

1
@KaspervandenBerg Якщо ви не скажете, що C і C ++ додали їх недавно, я вважаю, що це ж епоха. Справа в тому, що ці формати були не просто для передачі даних, хоча вони для цього використовувались, вони безпосередньо відображали, як код працює з даними в пам'яті та на диску. Це взагалі не те, як нові мови, як правило, працюють, хоча вони можуть мати такі особливості.
JimmyJames

@KaspervandenBerg C ++ робить це не так багато, як ви думаєте, що це робить. Можна вирівняти та усунути прокладки (і, правда, все частіше стандарт додає функції для подібних речей), а порядок членів є детермінованим (але не обов'язково таким же, як у пам'яті!).
Гонки легкості на орбіті

6

ASN.1 , Абстрактне позначення синтаксису перше, забезпечує спосіб визначення бінарного формату.

  • DDT - Розробка за допомогою вибіркових даних та одиничних тестів.
  • Текстовий дамп може бути корисним. Якщо в XML ви можете згортати / розширювати підгір'я.
  • ASN.1 насправді не потрібен, але декларативнішу специфікацію файлів на основі граматики простіше.

6
Якщо нескінченний парад уразливостей безпеки в парсерах ASN.1 є будь-якою ознакою, його прийняття, безумовно, забезпечить хорошу вправність у відладці бінарних форматів.
Марк

1
@Mark багато малих байтових масивів (і це в різних деревах ієрархії) часто не обробляються правильно (надійно) в C (наприклад, не використовують винятки). Ніколи не варто недооцінювати низьку ступінь, властиву небезпеці C. ASN.1, наприклад, java не піддає цій проблемі. Оскільки розбір граматики, спрямованого на граматику ASN.1, можна зробити безпечно, навіть C можна зробити з невеликою та безпечною базою коду. І частина вразливих місць притаманна самому двійковому формату: можна використовувати "легальні" конструкції граматики формату, які мають руйнівну семантику.
Joop Eggen

3

Інші відповіді описували перегляд шестигранного дампа чи виписування структур об'єктів у JSON. Я думаю, що поєднання обох це дуже корисно.

Використання інструменту, який може зробити JSON поверх шестигранного дампа, справді корисно; Я написав інструмент з відкритим кодом, який аналізував бінарні файли .NET під назвою dotNetBytes , ось подання прикладу DLL .

Приклад dotNetBytes


1

Я не впевнений, що цілком розумію, але це здається, що у вас є аналізатор цього бінарного формату, і ви керуєте кодом. Отже, ця відповідь побудована на тому припущенні.

Аналізатор певним чином заповнить структури, класи або будь-яку структуру даних, якою володіє ваша мова. Якщо ви реалізуєте a ToStringдля всього, що розбирається, тоді ви отримуєте дуже простий у використанні і легко підтримується спосіб відображення цих бінарних даних у читаному для людини форматі.

Ви по суті мали б:

byte[] arrayOfBytes; // initialized somehow
Object obj = Parser.parse(arrayOfBytes);
Logger.log(obj.ToString());

І це все, з точки зору його використання. Звичайно, це вимагає, щоб ви реалізували / переосмислювали ToStringфункцію для свого Objectкласу / struct / що завгодно, і вам також доведеться це робити для будь-яких вкладених класів / структур / whatevers.

Ви можете додатково використовувати умовний оператор, щоб запобігти виклику ToStringфункції у коді випуску, щоб ви не витрачали час на те, що не буде зареєстровано поза режимом налагодження.

Ви ToStringможете виглядати так:

return String.Format("%d,%d,%d,%d", int32var, int16var, int8var, int32var2);

// OR

return String.Format("%s:%d,%s:%d,%s:%d,%s:%d", varName1, int32var, varName2, int16var, varName3, int8var, varName4, int32var2);

У вашому первісному запитанні звучить так, що ви дещо намагалися це зробити, і ви вважаєте, що цей метод є обтяжливим, але ви також в якийсь момент реалізували розбір бінарного формату та створили змінні для зберігання цих даних. Отже, все, що вам потрібно зробити, - це надрукувати ці існуючі змінні на відповідному рівні абстракції (клас / структура змінної є).

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

Аналогічно: деякі мови мають надійні функції для перетворення класів у XML або JSON. C # особливо добре в цьому. Вам не доведеться відмовлятися від бінарного формату, ви просто виконуєте XML або JSON у виписці з журналу налагодження і залишаєте код звільнення в спокої.

Я особисто рекомендую не їхати з шестигранним дамп-маршрутом, тому що він схильний до помилок (ви починали з правого байта, чи впевнені, читаючи зліва направо, що "бачите" правильну витримку тощо) .

Приклад: Скажіть, що ToStringsвиплюнули змінні a,b,c,d,e,f,g,h. Ви запускаєте свою програму і помічаєте помилку g, але проблема справді почалася c(але ви налагоджуєте, так що ви ще цього не зрозуміли). Якщо ви знаєте вхідні значення (і вам слід), миттєво побачите, cзвідки починаються проблеми.

У порівнянні з шестигранним смітником, який просто вам каже 338E 8455 0000 FF76 0000 E444 ....; якщо ваші поля різної величини, з чого cпочинається і яке значення - шестнадцятковий редактор скаже вам, але, на мою думку, це схильність до помилок і затрата часу. Мало того, але ви не можете легко / швидко автоматизувати тест через шестигранний переглядач. Друк рядка після аналізу даних точно скаже вам, що ваша програма «думає», і буде одним кроком по шляху автоматизованого тестування.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.