3
8B EC 56 8B F4 68 00 70 40 00 FF 15 BC 82 40   

上記のようなセンセーションはさまざまな方法でセグメント化でき、各セグメントは対応するアセンブリ命令に変換できますが、各バイナリ実行可能ファイルには唯一のDEFINITEアセンブリがあります。あいまいさを回避する数学的原理は何ですか?

アップデート

ほとんどの票の答えは、実際には私の質問にまったく答えません。

4

9 に答える 9

14

あなたの出発点を知る。

つまり、命令の特定の開始バイトが与えられると、その命令がどこで終了するかが明確になり、次の命令の開始バイトが得られ、続行できるようになります。任意のメモリブロックが与えられると、最初の命令がどこから始まるかを知らずに、それを個々の命令に分割することは不可能です。

より数学的な観点からは、バイトが別の有効な命令のプレフィックスである有効な命令はありません。したがって、が有効である場合、それは有効ではないことがabわかります。したがって、1つの命令である必要があり、次の命令の開始です。ab cdabcd

于 2010-10-12T16:55:30.190 に答える
4

私があなたの質問を正しく理解しているなら、あなたはその理由を理解しようとしています

8B EC 56 8B F4 68 00 70 40 00 FF 15 BC 82 40

たとえば、分割することができます

8BEC 568BF4 68007040 00FF 15BC 8240

言うのではなく、

8B EC568B F4 68007040 00FF 15BC 8240

これは、アーキテクチャのISAによって完全に指定されます。このドキュメントでは、一連のバイトから命令がどのように一意に構築されるかを正確に説明しています。

ISAが適切に形成されるためには、単一の一連のバイトが最大で単一の一連のデコードされた命令に対応できます(無効な命令がある場合はそれより少なくなる可能性があります)。

もう少し具体的に説明するために、x86の例を見てみましょう。各バイトが何に対応するかを知りたい場合は、こちらをご覧ください。

たとえば、00で始まる命令は加算であることがわかります(追加のパラメータは特定のエンコーディングで次のバイトにあります)。

また、一部の値は、実際には次の命令を変更するプレフィックスであることがわかります(0F-オペコードスペースを拡張するプレフィックス、26、2E、36、3E、64、65、66、67、F0、F2、F3)、およびそれらのいくつかは、正確な次の指示に基づいて異なる意味を持ちます。これらはオペコードではありませんが、オペコードの引数のエンコーディングを変更したり、完全に新しいオペコードスペースを導入したりすることができます(たとえば、SSEは0Fを使用します)。

全体として、逆アセンブラのおかげで、x86エンコーディングは非常に複雑です。

于 2010-10-13T16:20:03.243 に答える
3

まず、RISCアーキテクチャとCISCアーキテクチャを区別する必要があります。

RISCアーキテクチャでは、通常、同じサイズの命令があるため、あいまいさを表現することはできません。CPUは、たとえば命令ごとに4バイトをフェッチします。これは、どこかから開始する必要があるため(CPUには、提示したようなシーケンスだけではなく、確実に開始点があります)、正しい位置合わせは問題ありません。

CISC命令セットで何が起こるかは基本的に同じです。プログラムのエントリポイントから開始して、オペコードに従って命令をフェッチします。次の命令の長さや最後の命令がどこで終了したかがわからないということは起こらないので、あいまいさを数学的に区別する方法を知る必要はありません。

したがって、すべての命令を分離する方法を尋ねるのは、

thepenisonthetable

数学的な証明はありませんが、どの文字が一緒に正しいか、どの文字が意味をなさないかはわかっています。前の文には「son」が含まれていますが、「ison」から取得されていることがわかります。意味のあるフレーズがなければそう言うことはできませんが、CPUは意味のあるプログラムしか実行しないので、ポイントは何ですか?

したがって、CPUが前の文で機能する場合、最初の意味のある命令「the」、次に「pen」、「is」、「on」が検出され、「son」はとにかく認識できませんでした。

編集

明確にするために、CISCアーキテクチャでは、あいまいさを持たないようにする必要がある唯一の制約は、別のプレフィックスである命令を持たないようにすることです。16進数ではなく文字azで構成される有限のアルファベットを想定しましょう(実用的な目的のため)。

プログラムカウンターが

abbcbcaabdeffabd

あなたはそれabbが完全な指示であることができます。その場合ab、有効な命令にはなりません。そうでない場合、CPUはどこで停止するかを知ることができず、同時にabbc命令にもなり得ないか、問題が発生する可能性があります。それを維持することは、例えば、それcaが次の命令である可能性があり、cできなかったし、cbcどちらでもない。

この引数を文字列全体に拡張できます。CPUが、バイナリの次のバイトが命令の最初のバイトを指している状態にあり、他の命令のプレフィックスである命令がない場合、次の状態でプログラムカウンターが表示されます。次の正しい命令の最初のバイトを指します。

于 2010-10-13T13:41:35.067 に答える
2

バイナリを16進エディタで開き、データの一部をコピーして逆アセンブラに貼り付けると、おそらく完全な命令をコピーすることはできません。あなたの例を使ってみましょう..WindowsXP32ビットSP3英語で、私が組み立てると、次の8B EC 56 8B F4 68 00 70 40 00 FF 15 BC 82 40ようになります。

Hex dump          Command
8BEC              mov ebp,esp
56                push esi
8BF4              mov esi,esp
68 00704000       push 407000
FF15 BC824000     call dword ptr ds:[4082bc]

あなたがそれが完全に異なって組み立てられたのを見ることができるように、以下の他の人の答え...

さて、組み立てる代わりに、最初にオペコード8B EC 56 8B F4 68 00 70 40 00 FF 15 BC 82 40
を追加したとしましょう。C0C0 8B EC 56 8B F4 68 00 70 40 00 FF 15 BC 82 40

次に、命令で1バイトが何をしたかを以下で確認します。

Hex dump             Command
C08B EC568BF4 68     ror byte ptr ds:[ebx+f48b56ec],68
0070 40              add byte ptr ds:[eax+40],dh
00FF                 add bh,bh
15 BC824000          adc eax,4082bc

ご覧のとおり、完全に変更されています。

プロセッサは、どこから開始し、オペコード命令によって命令に対してどの引数を取るかを知っています。最初の例では、最初のオペコードは8B、その後に別のバイトが続く可能性があることを認識しているためです。このバイトがECそうである場合、命令はここで終了し、それはを意味しmov ebp, espます。

2番目の例では、それはで始まり、C0その後に別のバイト、つまり別の命令を続けることができます。次にC08B、は命令であり、EC568BF4 68は引数です。

プロセッサの内部に巨大な(しかしナノの)回路があり、if(条件)のチェーンのように動作し、16進コード(またはオペコード)の値に応じて「どこに行くか」または「どのように振る舞うか」を知っていると想像してください。 。

于 2010-10-14T14:06:13.727 に答える
1

あなたの質問に対する答えは、やや派手な「出発点を知っている」のように聞こえますが、もう少し冗長なものが必要な場合もあります。あなたの文字列を考えると:

8B EC 56 8B F4 68 00 70 40 00 FF 15 BC 82 40

そして開始点(8Bが開始点であるとしましょう)バイトの可能な解釈は1つだけです。

したがって、1つの操作が8B EC 56 8B(操作の長さなどによって異なります)であるとすると、次の操作はF4 68です...この場合、マシンが操作を解釈しようとすることはできません56 8BF468。その時点で操作が終了することはありませんでした。

ここで、開始点が56の場合、そのグループを取得することはできますが、前述のグループのいずれも取得することはできません。

メモリのレイアウトは非常に具体的であり、開始点/ジャンプ点は正確で寛容ではありません。これらはコード自体と同じくらい確実に必要です。

于 2010-10-12T17:38:31.303 に答える
1

リストしたシーケンスは、正確に1つの番号を示しています。バイナリでは、 100010111110110001010110100010111111010001101000000000000111000001000000000000001111111100010101101111001000001001000000です。10進数では、726522768938664460674442126658667072です。これらはすべて、まったく同じ値を書き込むためのまったく異なる方法です。特定のアーキテクチャのISAは、ビットをフィールドに分割し、それらに意味を割り当てます。ほとんどのプロセッサには、これらのフィールドの各ビットに割り当てられた意味を説明するマニュアルが簡単に入手できます。

于 2010-10-12T17:01:00.543 に答える
1

逆アセンブルをコードの実行順序と直線的に混同しないでください。バイナリは、既知の場所から開始して実行順にデコードされます。さまざまな理由による意図的なハッキングを除いて、あいまいさはありません。

可変ワード長の命令セット用の逆アセンブラを作成してみてください。結局のところ、実行順に実行する必要があります。実行時に計算されたアドレスに基づくブランチもあるため、プログラムの一部を逆アセンブルすることしかできません。最新のコンパイラで生成されたコードは、古い手作業で組み立てられたコードよりもはるかに優れています。たとえば、古いスタンドアップアーケードゲームでは、条件付きブランチの前に、条件の1つだけが満たされることを保証する命令があり(なぜそこにあるのか?私たちは決してわかりません)、条件付きブランチに続くデータは、他のオペコードとの衝突に遭遇するような方法。

逆アセンブラを無効にしようとする古いdosプログラムには自己変更コードがあり、1つステップで変更が行われる場合、1つの命令が1つまたは2つの命令の前に別の命令を変更しますが、フルスピードで実行すると、その命令はすでにパイプラインにフェッチされています。メモリ内の変更/破損したものは使用されませんでした。かなりきちんとしたトリック。

とにかく、あなたの質問への答えは、線形順序でバイトを見るのではなく、ベクトルテーブルのリセットおよび他のベクトルによって定義されたアドレスから始まる実行順序でそれらを見るということです。

于 2010-10-12T17:13:01.660 に答える
0

有効な開始アドレスが何であるかについて、他の場所にもいくつかの手がかりがあるかもしれません。常にリセットベクトルアドレスがあり、通常は割り込みベクトルアドレスがあり、これらはすべてコードブロックの有効な開始点である必要があります。さらに便利なことに、デコードしようとしているブロック内のアドレスを参照するジャンプまたは呼び出し命令を他の場所で見つけた場合、それは別の開始アドレスを提供します。

私はあなたの心配を知っていると思います、そして私が正しいことを知っている限り-プログラムカウンターが1つによって動揺し、それが無効な命令または意図しない命令を実行する原因になる場合、プログラムはおそらくクラッシュします。Trueであり、データブロックに遭遇してそれを実行しようとした場合も同様です。少なくとも後者は、コードとデータが別々のメモリスペースにあり、ビット幅が異なる可能性があるハーバードアーキテクチャを使用することで回避できます。

于 2010-10-13T16:43:19.503 に答える
0

他の方向について考えるのは面白いかもしれません。他の人のために簡単にセグメント化できるようにコードをどのように設計する必要がありますか?UTF-8のように、シーケンスを開始するバイトの最上位ビットを0にし、シーケンスの途中のビットを1にする必要があります。次に、ランダムな位置から開始すると(バイトがどこにあるかがわかっていると仮定して)、次のシーケンスを簡単に見つけることができます。さらに一歩進んで、シーケンスの開始を簡単に見つけられるように、純粋なビットストリームをどのようにコーディングしますか。そのようなコーディングによって何ビットが無駄になりましたか?

数学についてお伺いしましたが、関連するトピックは「符号理論」「可変長コード」「プレフィックスコード」だと思います。

塩基対のシーケンスからどのように遺伝子を見つけますか?

于 2010-10-14T14:28:28.007 に答える