2016-04-24 13 views
1

コードのこの断片が「発生」した瞬間に何が起こるかを理解するためには助けが必要です。「jmp Begin」。 私は、.comファイルを64kbにすることができるので、すべてを1つのセグメントに入れたいと思っています。変数を入れたい場合はjmpにする必要があります。しかし、私がそれについて調べると、多くのガイドはjmp Beginがデータをスキップするだけで、それ以外のことはコメントではないと言っています。逆アセンブルされたデータが命令になるのはなぜですか?

enter image description here

この

 mov  al, a 
     mov  bl, b 
     sub  al, bl 

実行されますが、それはターボデバッガで次のようになり、なぜ私が理解できないと表示されます。 まさにこの瞬間に何が起こる:そしてここで私の質問です。 結果の開始値を変更したときは? 0より大きい値には何かに変更され、たとえば90に変更すると完全に正常に見えます。私は組立のために全く新しいです、そして、私はそれをまったく理解できないようです。私の全体のコードはここにあります:

  .MODEL TINY 

Code  SEGMENT 

      ORG 100h 
      ASSUME CS:Code, DS:Code 

Start: 
       jmp  Begin 
a    EQU  20 
b    EQU  10 
c    EQU  100 
d    EQU  5 
Result   DB  ? 


Begin: 

      mov  al, a 
      mov  bl, b 
      sub  al, bl 
      mov  bl, c 
      mul  bl 
      mov  bl, d 
      div  bl    
      mov  Result, al 
      mov  ah, 4ch 
      int  21h 

Code  ENDS 
      END    Start 
+2

コードの後ろに変数を置くことができるので、ジャンプする必要はありません。 '.com'プログラムはロードされている場所であるアドレス' 100h'で実行を開始します(最初のことは命令でなければならないことを意味します)。 'Result'の初期値は上書きされるので意味がありません('? 'が使われている理由です)。 – Jester

+0

私はあなたがジャンプを必要としないことをJesterに同意します。コードの後ろにデータを置くだけです。私は.comファイルの先頭にデータを置くこのパターンを使用したことはありません。デバッガはコンパイルされた.comファイルのバイトを一連の命令としてデコードし、ジャンプ命令とデータは気にしないので、デバッガで表示されるガベージが発生します。データをコードとしてもデコードします。 IDA Proのような高度なデバッガでは、フロー解析が行われ、コード内のジャンプが考慮され、データが決して実行されないことがわかります。 – pasztorpisti

答えて

3

私はあなたに説明をしようとします。

問題は、昔はメモリがコードとデータバイトを区別していなかったことです。つまり、.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の場合よりずっと簡単なのでどこでもジャンプすることはできません。

+0

可能な限り最長の8086命令(冗長プレフィックスなし)は実際には6バイトと短いですか?構造的には、15バイトがinsnの長さの上限ですが、私が見つけた最長の非冗長符号化は14Bです。 '65 67 66 47 0f ​​3a 0f 94 28 00 01 00 00 08 palignr xmm10、XMMWORD PTR gs:[r8d + r13d * 1 + 0x100]、0x8'(GNU' objdump -Mintel'構文)プレフィックスは、GSセグメントのオーバーライド、アドレスサイズ、オペランドサイズ(palignrエンコーディングの必須部分)、およびREXです。アドレッシングモードはSIBバイトとdisp32を使用し、insnはimm8を使用します。そしてそれはSSSE3のinsnだから、すでに長いです。 –

+0

@PeterCordes一般的なx86ファミリではなく古い16ビットの8086について言えば、接頭辞なしの6バイトは私の知る限り正しいものです。私は15バイトの長さの非冗長命令を見たことはありませんが、正直言って私はそれを探していません。 :-D – pasztorpisti

+0

私は編集中に何かを残しました。つまり、私が見つけた14Bの最大の例は、一般的にはx86のためのものでした。 x86-32(REXなし、アドレス・サイズまたはSIBバイトのいずれかが失われます)では小さくなります。そして、ええ、私は8086、80186ではなく、まさに80386について尋ねていました。即時オペランドとメモリオペランドを取ることができるマルチバイトオペコードを持つ8086インinsがありませんか? '81 80 34 12 78 56 add word [bx + si + 0x1234]、0x5678'は、シングルバイトオペコードの6Bです。 –

1

バイトシーケンスは、x86命令としてデコードできます。 x86マシンコードでは、ほとんどのコーディングスペースが使用されるため、有効な命令ではないビットパターンがいくつかあります。 (bad)またはバイトが有効なx86命令を表していないことを示すような指示が表示されることがあります。

CPUがコードとして実行したくないバイトがある場合は、実行が決してそれらに届かないようにしてください。 jmpを非コードブロックに置き換え、実行前にシステムコールをexitにして、コードが非コードになるようにします。 (または永遠にループ)。

逆アセンブラの中には、コードとして分解するバイトをスマートにしようとするものもありますが、x86コードはレジスタ内のアドレスを計算してジャンプするため、ジャンプ先のアドレスを必ずしも知ることができません。

関連する問題