バイナリ形式をどのようにデバッグしますか?


11

バイナリビルダーのビルドをデバッグできるようにしたいと思います。現在、私は基本的に入力データをバイナリパーサーに出力し、次にコードを深く掘り下げて、入力から出力へのマッピングを出力し、出力マッピング(整数)を取得して、それを使用して対応する整数を見つけますバイナリで。かなり不格好で、入力と出力の間のマッピングを取得するには、ソースコードを深く変更する必要があります。

さまざまなバリアントでバイナリを表示できるようです(私の場合、入力にかなり近いため、8ビットチャンクで10進数として表示したいと思います)。実際、いくつかの数値は16ビット、いくつかの8、いくつかの32などです。したがって、これらの異なる数値のそれぞれが何らかの方法でメモリ内で強調表示された状態でバイナリを表示する方法があります。

可能なことを確認できる唯一の方法は、実際のバイナリ形式/レイアウトに固有のビジュアライザーを実際に構築する場合です。そのため、32ビット番号がシーケンスのどこにあるべきか、8ビット番号がどこにあるべきかなどを知っています。これは多くの作業であり、状況によっては注意が必要です。だからそれを行う一般的な方法があるのだろうか。

また、現在このタイプのことをデバッグする一般的な方法が何であるか疑問に思っているので、それから何を試すべきかについてのアイデアを得ることができるかもしれません。


75
「hexdumpを直接使用し、これを追加して実行する」という回答が1つありました。その回答には多くの賛成票が寄せられました。そして、5時間後に(!)、「hexdumpを使用する」とだけ言う2番目の回答。それから、最初のものを支持して2番目のものを受け入れましたか?マジ?
Doc Brown

4
バイナリ形式を使用する正当な理由があるかもしれませんが、代わりにJSONのような既存のテキスト形式を使用できるかどうかを検討してください。人間の可読性は非常に重要であり、マシンとネットワークは一般に十分に高速であるため、今日ではカスタム形式を使用してサイズを縮小する必要はありません。
jpmc26

4
@ jpmc26バイナリ形式にはまだ多くの用途があり、常に使用されます。人間の可読性は、通常、パフォーマンス、ストレージ要件、およびネットワークパフォーマンスの二次的なものです。また、特にネットワークパフォーマンスが低く、ストレージが限られている領域はまだ多くあります。さらに、すべてのシステムがレガシーシステム(ハードウェアとソフトウェアの両方)とインターフェースを取り、データ形式をサポートする必要があることを忘れないでください。
1

4
@jwentingいいえ、実際には、開発者の時間は通常、アプリケーションの最も高価な部分です。もちろん、GoogleやFacebookで作業している場合はそうではないかもしれませんが、ほとんどのアプリはその規模では動作しません。そして、開発者がものに時間を費やすことが最も高価なリソースである場合、人間の可読性は、プログラムがそれを解析するための100ミリ秒をはるかに超えるものとしてカウントされます。
jpmc26

3
@ jpmc26 OPがフォーマットを定義するものであることを示唆する質問には何も表示されません。
ジミージェームズ

回答:


76

アドホックチェックの場合は、標準のhexdumpを使用して、目で見て確認するだけです。

適切な調査のために準備したい場合、私は通常、Pythonのようなもので別のデコーダーを作成します-理想的には、これはメッセージ仕様文書またはIDLから直接駆動され、可能な限り自動化されます(したがって、手動で導入する機会はありません両方のデコーダで同じバグ)。

最後に、既知の正しい定型入力を使用して、デコーダーの単体テストを作成することを忘れないでください。


2
「標準の16進ダンプを使用して、それを目で確認するだけです。」うん。私の経験では、最大200ビットまでの複数のセクションをグループ化された比較のためにホワイトボードに書き留めることができます。これは、この種の作業を開始するのに役立つことがあります。
マスト

1
バイナリデータがアプリケーション(またはシステム全般)で重要な役割を果たしている場合、努力する価値のある別のデコーダーが見つかります。これは、データ形式が可変である場合に特に当てはまります。固定レイアウトのデータは、少し練習すれば16進ダンプで見つけることができますが、実用性の壁にすばやくぶつかります。USBとCANのトラフィックを市販のパケットデコーダーでデバッグし、PROFIBusデコーダー(変数がバイトに分散し、16進ダンプでは完全に読み取れない)を作成し、3つすべてが非常に役立つことを発見しました。
ピーター-モニカの復活

10

これを行うための最初のステップは、データの構造、つまりスキーマを記述する文法を検索または定義する方法が必要であることです。

この例は、コピーブックとして非公式に知られているCOBOLの言語機能です。COBOLプログラムでは、メモリ内のデータの構造を定義します。この構造は、バイトの格納方法に直接マップされました。これは、メモリの物理的なレイアウトが開発者から引き離された実装の懸念事項である一般的な現代言語とは対照的に、その時代の言語に共通です。

バイナリデータスキーマ言語を Googleで検索すると、多くのツールが見つかります。例はApache DFDLです。このためのUIもすでに存在する可能性があります。


2
この機能は「古代」時代の言語に限定されていません。CおよびC ++の構造体と共用体は、メモリ境界で整列させることができます。C#にはStructLayoutAttributeがあり、これを使用してバイナリデータを送信します。
カスパー

1
@KaspervandenBerg CとC ++が最近これらを追加したと言わない限り、同じ時代だと思います。重要なのは、これらの形式は単にデータ送信用ではなく、そのために使用されたものであり、コードがメモリおよびディスク上のデータをどのように処理するかに直接マッピングされていることです。一般的に、新しい言語にはこのような機能があるかもしれませんが、新しい言語が機能する傾向はありません。
ジミージェームズ

@KaspervandenBerg C ++は、あなたが思うほどにはそれをしません。実装固有のツールを使用してパディングを調整および削除することが可能であり(そして、明らかに、この種の機能を追加する標準がますます増えています)、メンバーの順序は決定的です(ただし、必ずしもメモリ内と同じではない!
オービットのライトネスレース

6

ASN.1、抽象構文表記法1は、バイナリ形式を指定する方法を提供します。

  • DDT-サンプルデータと単体テストを使用して開発します。
  • テキストダンプが役立つ場合があります。XMLの場合、サブ階層を折りたたみ/展開できます。
  • ASN.1は実際には必要ありませんが、文法ベースのより宣言的なファイル仕様の方が簡単です。

6
ASN.1パーサーのセキュリティ脆弱性の終わりのないパレードが何らかの兆候である場合、それを採用することは確かにバイナリ形式のデバッグで良い練習になるでしょう。
マーク

1
多くの小さなバイト配列(およびさまざまな階層ツリーの配列)は、Cで正しく(安全に)処理されないことがよくあります(たとえば、例外を使用しません)。C. ASN.1の低レベルの固有の安全性を決して過小評価しないでください-たとえば-javaはこの問題を公開しません。ASN.1文法による構文解析は安全に実行できるため、Cでさえ小さく安全なコードベースで実行できます。また、脆弱性の一部はバイナリ形式自体に固有のものです。破壊的なセマンティクスを持つ形式の文法の「合法的な」構造を悪用する可能性があります。
ジョープエッ

3

他の回答では、16進ダンプの表示、またはJSONでのオブジェクト構造の書き出しについて説明しています。これらの両方を組み合わせることは非常に役立つと思います。

16進ダンプの上にJSONをレンダリングできるツールを使用すると非常に便利です。dotNetBytesと呼ばれる.NETバイナリを解析するオープンソースツールを作成しました。ここにサンプルDLLのビューがあります

dotNetBytesの例


1

完全に理解しているとは思いませんが、このバイナリ形式用のパーサーがあり、コードを制御しているようです。したがって、この答えはその前提に基づいています。

パーサーは何らかの方法で、構造体、クラス、または言語のデータ構造を埋めます。ToString解析されるすべてに対してfor を実装すると、そのバイナリデータを人間が読める形式で表示する、非常に使いやすく保守が簡単な方法になります。

基本的に次のものがあります。

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

そして、それを使用するという見地からです。もちろん、これにはクラス/構造/なんでもToString関数を実装/オーバーライドするObject必要があり、ネストされたクラス/構造/なんでも実装する必要があります。

さらに、条件付きステートメントを使用して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を実行し、リリースコードをそのままにしておきます。

エラーが発生する可能性があるため、16進ダンプルートを使用しないことを個人的にお勧めします(正しいバイトから開始したのか、左から右に読んでいるときに正しいエンディアンを「見ている」など) 。

例:ToStrings吐き出す変数を言いますa,b,c,d,e,f,g,h。プログラムを実行し、でバグに気付きますgが、問題は実際に始まりましたc(しかし、デバッグしているので、まだ理解できていません)。入力値がわかっていれば(そうすべきです)、すぐcに問題が始まることがわかります。

単にあなたに伝える16進ダンプと比較して338E 8455 0000 FF76 0000 E444 ....。フィールドがさまざまなサイズで、どこcから始まり、何が値であるか-16進エディターが教えてくれますが、これはエラーが発生しやすく、時間がかかります。それだけでなく、16進ビューアでテストを簡単/迅速に自動化することはできません。データを解析した後に文字列を印刷すると、プログラムが何を「考えている」かが正確にわかり、自動テストのパスに沿った一歩になります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.