x86は可変命令長であるため、分解が非常に困難です。これが最初の逆アセンブラである場合はお勧めできません。
つまり...私が採用しているアプローチは、オペコードの最初のバイトであるバイトをバイナリで識別し、オペコードまたはデータの2番目または他のバイトであるバイトからそれらを分離する必要があるということです。バイナリの先頭から開始して、オペコードを逆アセンブルできることがわかったら。
他のバイトからオペコードをどのように把握しますか?考えられるすべての実行パスをたどる必要があり、再帰の問題のように聞こえますが、そうである必要はありません。割り込みベクタテーブルおよび/またはコードへのすべてのハードウェアエントリポイントを確認します。これにより、オペコードバイトの短いリストが得られます。非再帰的アプローチは、オペコードとマークされた各バイトを調べてバイナリを何度も通過させ、消費するバイト数を知るのに十分なだけデコードすることです。また、無条件分岐、条件分岐、return、callなどであるかどうかを知る必要があります。無条件分岐またはreturnでない場合は、この命令の後のバイトが次の命令の最初のバイトであると見なすことができます。ある種のブランチまたはコールに遭遇したときはいつでも、宛先アドレスを計算し、そのバイトをリストに追加します。リストに新しいバイトを追加しないパスを作成するまで、パスを作成し続けます。また、3バイトの命令であるバイトが見つかったが、その後のバイトが命令としてマークされている場合は、問題があることを確認する必要があります。決して分岐しないことを保証する何かが先行する条件分岐のようなもの。高水準コードをバイナリーにコンパイルしたとしても、これはあまりわかりませんが、手書きのアセンブラーの古き良き時代、またはコードを保護したい人は、このようなことをします。
残念ながら、バイナリだけの場合、可変長のインストルメンテーションセットの場合、完全に分解することはできません。一部の分岐先は実行時に計算されます。手動でコード化されたアセンブリは、戻る前にスタックを変更して、次に実行するコードを変更する場合があります。それがそのコードへの唯一のパスである場合は、プログラムでそれを理解できない可能性があります。コードをシミュレートします。また、シミュレーションを使用しても、すべての実行パスをカバーすることはできません。
たとえば、ARMのような固定長の命令セットを使用すると(腕であり、腕と親指が混在していない限り)、バイナリの先頭から開始して、単語がなくなるまで分解できます。データワードを有効または無効、あるいは使用される可能性が低い命令に分解する場合がありますが、それは問題ありません。
エルフのどこかに、バイナリのどの部分が実行可能で、どの部分がデータであるかを示す何かがあったとしても、私は驚かないでしょう。データパスをたどる必要がないほどでも、objdumpがおそらくelfファイル内の何かを使用するようなタスクを実行するのではないかと思います。
elfファイル形式は多くの場所で文書化されています。基本的な構造があり、ベンダーはベンダーによって文書化される特定のブロックタイプを追加する場合があります。