私はあなたに説明をしようとします。
問題は、昔はメモリがコードとデータバイトを区別していなかったことです。つまり、.comファイルの任意のバイトをコードとデータの両方として使用できます。デバッガには、どのバイトがコードとして実行され、どのバイトがデータとして使用されるかという手掛かりはありません。トリッキーなケースでは、バイトは実際にコードとデータの両方として使用できます。プログラムはメモリ内にコードとして有効なデータを作成し、ジャンプして実行することができます。
多くの場合(すべてではありません)、デバッガはコードとデータを実際に見つけることができますが、このコード解析は非常に複雑になりますので、ほとんどのデバッガ/ディスアセンブラにはそのようなコードフローアナライザがありません。このため、ファイル/メモリ内のオフセット(通常は現在の命令ポインタ)を選択し、このオフセットから一連の連続バイトをアセンブリ命令として連続してデコードしますjmp
命令デバッガは、十分な数の逆アセンブルされた行で完全に埋められます。ダム逆アセンブラ/デバッガは、逆アセンブルされたバイトがプログラムの命令またはデータとして実際に使用されるかどうかは気にしません。命令として扱います。
プログラムをデバッグしているときにブレークポイントでデバッガが停止すると、現在の命令ポインタが使用され、そのオフセットからプリミティブ「デバッガ画面を埋める」メソッドを使用して再度ダム逆アセンブリが実行されます。
この連続したバイトのシリアル・ディスアセンブルは、ほとんどの時間で動作する単純な方法です。あなたが連続して非 - jmp
命令をデコードした場合、プロセッサはこの順序でプロセッサを実行することがほぼ確実です。ただし、jmp
命令に到達してデコードすると、次のバイトがコードとして有効であることを確認できません。しかし、コードの途中にデータが混在していないことを希望する命令としてデコードしようとすることもできます(ほとんどの場合、jmp
(または同様の制御フロー命令)の後にデータがありません)。 「おそらく有用な予測」としてのダム分解)。実際、コードの大部分は通常、条件付きジャンプで完全であり、コードの後のバイトを逆アセンブルするので、デバッガから非常に役立ちます。ジャンプ命令の後にコードの途中にデータを置くことは非常にまれですが、それを端点ケースとして扱うことができます。
のは、あなただけのいくつかのデータの上にジャンプして、int 20h
に存在するシンプル.COMプログラムを持っていると仮定しましょう:
jmp start
db 90h
start:
int 20h
逆アセンブラは、おそらくから始まる分解して、あなたに次のようなものを言うだろう0000をオフセット:
...のは、データを変更してみましょう:
--> 0000 eb 01 jmp short 0003
0002 90 nop
0003 cd 20 int 20h
クールな、これが今度は、プログラムビットを変更してみましょう...まさに私たちのasmソースコードのように見えます
jmp start
db cdh
start:
int 20h
は今、逆アセンブラはあなたにこれを表示します:
--> 0000 eb 01 jmp short 0003
0002 cd cd int cdh
0004 20 ...... whatever...
問題はいくつかの命令が2バイト以上で構成されており、バイトはあなたのためのコードやデータを表すかどうか、デバッガは気にしないということです。上の例では、逆アセンブラがオフセット0000からプログラムの終わりまで(データを含む)バイトを連続的に逆アセンブルすると、1バイトのデータは2バイトの命令に分解されます(実際のコードの最初のバイトを "盗む")ので、デバッガが逆アセンブルしようとする次の命令は、jmp
が通常ジャンプする0003の代わりに0004というオフセットになります。最初の例では、データが1バイト命令に逆アセンブルされ、が偶発的にのプログラムのデータ部分を逆アセンブルしたため、次のデバッガ命令の逆アセンブルが正確にターゲットのオフセット0003にあったため、あなたのjmp
の
しかし、この場合デバッガがあなたに示すものは、幸いにもあなたのプログラムが実行されたときに起こることではありません。 1つの命令を実行することによって、プログラムは実際にオフセット0003にジャンプし、デバッガはダム逆アセンブリを再度行うが、今度はオフセット0003から始まり、以前の不正確な逆アセンブリの命令の中間にある。
2番目のサンプルプログラムをデバッグし、その中のすべての命令を1つずつ実行します。あなたは一つの命令命令ポインタ(IP)0003への変更と、デバッガが実行を実行するために、「ステップ」コマンドをトリガしかし
--> 0000 eb 01 jmp short 0003
0002 cd cd int cdh
0004 20 ...... whatever...
:あなたは命令ポインタ== 0000でプログラムを起動すると、デバッガはこの示し
--> 0003 cd 20 int 20h
0005 ...... whatever...
結論:デバッガ画面までのオフセット0003から再び「ダム解体は、」そう、あなたがこれを見ます埋められjmp
で(あなたがダム逆アセンブラを持っているし、あなたのコードの真ん中にデータを混在させる場合には、周りによデータ)であれば、ダムディスアセンブラはあなたのデータをコードとして扱い、これは "マイナー" issuあなたは遭遇した。
流れ分析(Ida Proのような)を備えた高度な逆アセンブラは、ジャンプ命令に従って逆アセンブルを行います。 jmp
をオフセット0000で逆アセンブルした後、次の解体指示が0003のjmp
のターゲットであり、次のステップとしてint 20h
を逆アセンブルします。オフセット0002にあるdb cdh
バイトをデータとしてマークします。
補足説明:
あなたはすでに命令を気づいてきたように(かなり旧式)8086の命令セットは、どこか間の1-6バイトの長さにすることができますがjmp
またはcall
はバイト粒度でメモリ内の任意の場所にジャンプすることができます。命令の長さは、通常、命令の最初の1または2バイトから決定できます。しかし、プロセッサは、特殊なIP(命令ポインタ・レジスタ)を持つ命令の最初のバイトをターゲットにして、指定されたオフセットでバイトを実行しようとするときにのみ、命令に「つなぎ合わせる」。トリッキーな例を見てみましょう:オフセット0000のメモリにバイトeb ff 26 05 00 03 00があり、それを段階的に実行します。
--> 0000 eb ff jmp short 0001
0002 26 05 00 03 es: add ax, 300h
0006 00 ...... whatever...
プロセッサ命令ポインタ(IP)のポイントそれは命令をデコードし、そこバイトは、実行の時間は、「命令にくっつく」0000を相殺します。 (プロセッサは0000で命令デコードを実行する。)第1バイトはebであるので、命令長は2バイトであることが分かる。デバッガはこれも知っていますので、命令をデコードし、プロセッサがオフセット0002、次にオフセット0006などで命令を実行するという誤った前提に基づいて、いくつかの追加のバグの逆アセンブリを生成します。これは真実ではないことがわかります。プロセッサは、バイトをかなり異なるオフセットで命令にスティックします。
わかりやすいように、私のトリッキーなバイトコードには、jmp
命令の途中にあるオフセット0001にジャンプするjmp
が含まれています。しかし、これはまったく問題ではありません。プロセッサはそれを気にせず、幸いなことに0001をオフセットするようにジャンプするので、次のステップで命令をデコードしようとする(または「バイトをスティックする」)。のは、プロセッサが0001で見つける命令の種類を見てみましょう:
あなたは私たちが0001で私たちの次の命令を持っていると、デバッガは私たちに誤った前提に基づいてオフセット0005でのいくつかのごみの分解を示して見たよう
--> 0001 ff 26 05 00 jmp word ptr [5]
0005 03 00 add ax, word ptr [bx+si]
そのプロセッサますある時点でそのオフセットに到達します...
0001の命令は、オフセット0005からワードを取り出し、そこにジャンプするオフセットとして解釈します。ご覧のとおり、word ptr [5]
の値は3(リトルエンディアンの16ビット値)なので、プロセッサは3をIPレジスタに入れます(0003にジャンプ)。のは、それが0003オフセットで見つけたものを見てみましょう:
--> 0003 05 00 03 add ax, 300h
実際の命令がで実行されるので、私のトリッキーなバイトコード26デバッガのスタイルで05 00 03 00 FF EBのための分解を示すことは困難であろうプロセッサは重複するメモリ領域にあります。最初に、プロセッサは0000-0001バイト、次に0001-0004バイト、最後に0003-0005バイトを実行しました。
いくつかの新しいriscアーキテクチャでは、命令の長さが修正されており、メモリ領域が一直線になっている必要があり、デバッガの仕事がx86の場合よりずっと簡単なのでどこでもジャンプすることはできません。
コードの後ろに変数を置くことができるので、ジャンプする必要はありません。 '.com'プログラムはロードされている場所であるアドレス' 100h'で実行を開始します(最初のことは命令でなければならないことを意味します)。 'Result'の初期値は上書きされるので意味がありません('? 'が使われている理由です)。 – Jester
私はあなたがジャンプを必要としないことをJesterに同意します。コードの後ろにデータを置くだけです。私は.comファイルの先頭にデータを置くこのパターンを使用したことはありません。デバッガはコンパイルされた.comファイルのバイトを一連の命令としてデコードし、ジャンプ命令とデータは気にしないので、デバッガで表示されるガベージが発生します。データをコードとしてもデコードします。 IDA Proのような高度なデバッガでは、フロー解析が行われ、コード内のジャンプが考慮され、データが決して実行されないことがわかります。 – pasztorpisti