2016-04-24 13 views
0
0x08048b94 <+0>:push %esi 
0x08048b95 <+1>: push %ebx 
0x08048b96 <+2>: sub $0x34,%esp 
0x08048b99 <+5>: lea 0x18(%esp),%eax 
0x08048b9d <+9>: mov %eax,0x4(%esp) 
0x08048ba1 <+13>: mov 0x40(%esp),%eax 
0x08048ba5 <+17>: mov %eax,(%esp) 
0x08048ba8 <+20>: call 0x804920a <read_six_numbers> 
0x08048bad <+25>: cmpl $0x0,0x18(%esp) 
0x08048bb2 <+30>: jne 0x8048bbb <phase_2+39> 
0x08048bb4 <+32>: cmpl $0x1,0x1c(%esp) 
0x08048bb9 <+37>: je  0x8048bda <phase_2+70> 
0x08048bbb <+39>: call 0x80490d7 <explode_bomb> 
0x08048bc0 <+44>: jmp 0x8048bda <phase_2+70> 
0x08048bc2 <+46>: mov -0x8(%ebx),%eax 
0x08048bc5 <+49>: add -0x4(%ebx),%eax 
0x08048bc8 <+52>: cmp %eax,(%ebx) 
0x08048bca <+54>: je  0x8048bd1 <phase_2+61> 
0x08048bcc <+56>: call 0x80490d7 <explode_bomb> 
0x08048bd1 <+61>: add $0x4,%ebx 
0x08048bd4 <+64>: cmp %esi,%ebx 
0x08048bd6 <+66>: jne 0x8048bc2 <phase_2+46> 
---Type <return> to continue, or q <return> to quit---<return> 
0x08048bd8 <+68>: jmp 0x8048be4 <phase_2+80> 
0x08048bda <+70>: lea 0x20(%esp),%ebx 
0x08048bde <+74>: lea 0x30(%esp),%esi 
0x08048be2 <+78>: jmp 0x8048bc2 <phase_2+46> 
0x08048be4 <+80>: add $0x34,%esp 
0x08048be7 <+83>: pop %ebx 
0x08048be8 <+84>: pop %esi 
0x08048be9 <+85>: ret 

これはバイナリボンデフレーションラボの特定のフェーズのための私のアセンブラダンプです。私は6つの数字を入力してコードを解読し、次の段階に進む必要があります。誰かが、このアセンブリコードの実行と私を助けることができれば2進爆弾の崩壊。デコードダンプ - フェーズ2

0x08048bc2 <+46>: mov -0x8(%ebx),%eax 
0x08048bc5 <+49>: add -0x4(%ebx),%eax 

それは素晴らしいことだ:私は、特に以下の部分では、こののデコードとこだわっています。ありがとう!

答えて

2

OK、これは長い答えのようなものになるだろうが、ここであなたが行くされています

私は+ 46/+ 49ラインについてのあなたの特定の質問を見てみましょう。彼らは何をやっていることは次のとおりです。

  • アドレスのメモリの値を書く[EBX-8]、EAX
  • に効果的に、そう
  • をEAXするアドレスに[EBX-4]メモリの値を追加します。 eax = [ebx-8] + [ebx-4]を設定します。ここで、[]は参照/メモリアクセスを意味します。

あなたのコードは、AT & T構文を使用しています。私は個人的にそれを非常に混乱させ、読むのは難しいと思っています。これからは、Intel構文を使用します。すみませんが、私たちは& ATにとどまった場合、私はおそらく私の答えで、さらに行くことができないであろうT.

主な違いは以下のとおりです。& AT

  • Tは、最後にソースを置き、インテルはそれを逆にしています。たとえば、 Tのmov %ebx,%eaxは、Intelではmov eax, ebxになります。
  • インテルは、私は個人的に多くを見つけた(インテル[base+offset]を使用しながら、& AT Tはoffset(base)使用して、メモリアクセス(またはleaと実効アドレスの計算)について
  • など、*、など%などの接頭語のほとんどを持っていません読みやすい)ので、例のmov -0x8(%ebx),%eaxはIntel構文のmov eax, [ebx-8]になります。
  • lのようなコマンドサフィックスの代わりに、dword ptrのような構文を使用して必要なオペランドサイズを指定します(たとえば、cmplcmp dword ptrになります)。

これで、コード全体をちょっと整理しようとしました。

まず、私はインテルの構文にすべてを変換し、ラベルを作成しました:

phase_2: 
    push esi 
    push ebx 
    sub esp, 34 
    lea eax, [esp+18] 
    mov [esp+4], eax 
    mov eax, [esp+40] 
    mov [esp], eax 
    call read_six_numbers 
    cmp dword ptr [esp+18], 0 
    jne phase_2_39 

phase_2_32: 
    cmp dword ptr [esp+1c], 1 
    je phase_2_70 

phase_2_39: 
    call explode_bomb 
    jmp phase_2_70 

phase_2_46: 
    mov eax, [ebx-8] 
    add eax, [ebx-4] 
    cmp [ebx], eax 
    je phase_2_61 

phase_2_56: 
    call explode_bomb 

phase_2_61: 
    add ebx, 4 
    cmp ebx, esi 
    jne phase_2_46 

phase_2_68: 
    jmp phase_2_80 

phase_2_70: 
    lea ebx, [esp+20] 
    lea esi, [esp+30] 
    jmp phase_2_46 

phase_2_80: 
    add esp, 34 
    pop ebx 
    pop esi 
    retn 

read_six_numbers: 
    int3 

explode_bomb: 
    int3 

それから私は実際にコードをコンパイルして、IDA(素敵な逆アセンブラ)にそれをロード。私は変数のいくつかのラベルを設定し、基本的に6 DWORD番号の配列である構造SIX_NUMBERS_STRUCTを作成しました。私にとっては、phase_2関数が1つのパラメータ、おそらく入力した数字の文字列のようなものを取るように見えます。次にread_six_numbersを呼び出し、前述の入力ポインタを6つのDWORD値([esp + 3C-24] = [esp + 18]に保持されている)の構造体へのポインタとともに渡します。したがって、[esp +18]、[esp + 1C]、[esp + 20]、[esp + 24]、[esp + 28]、[esp + 2C]次に、このデータに対していくつかのチェックと計算が実行されます。

以下の部分について少し説明します:通常は、ローカル変数を持つ関数の基本ポインタ(ebp)は、新しい値がプッシュされても常にスタック上の同じポイントを参照するポインタを持ちます。ここではそうではありません。代わりに、開始時に0x37がespから減算されます。さらに、前には2つありました。つまり、スタックポインタは、関数の先頭にあったポイントより0x3Cバイト下になります。このため、IDAはesp-3Cを基準にしたすべてのスタックオフセットを計算しています。私ものCのような表現を生成することで逆コンパイラを実行した Flowchart

OKはとてもまず、ここでは(あなたがプログラムより簡単に流れを理解することができるはずです)可視化アセンブリコードでありますコードと少しそれを注釈付き:

void __cdecl phase_2(void *argument_to_phase_2) 
{ 
    int *currentNumber; // [email protected] 
    SIX_NUMBERS_STRUCT numbers_struct; // [sp+18h] [bp-24h]@1 
    int dummy_var_after_numbers; // [sp+30h] [bp-Ch]@6 

    read_six_numbers(argument_to_phase_2, &numbers_struct); 
    if (numbers_struct.numbers[0] || numbers_struct.numbers[1] != 1) 
     explode_bomb(); 
    currentNumber = &numbers_struct.numbers[2]; 
    do 
    { 
     if (*currentNumber != *(currentNumber - 1) + *(currentNumber - 2)) 
      explode_bomb(); 
     ++currentNumber; 
    } 
    while (currentNumber != &dummy_var_after_numbers); 
} 

だから、ここに私の洞察力は、次のとおりです。

  • まず、コードREA数字を保持する6 * DWORD構造体へのポインタと共に、argument_to_phase_2([esp + 0]、スタック上の引数リストの一部として[esp + 0]に保存される)をread_six_numbersに渡すことによってstructは[esp + 18]で始まり、スタック上の引数リストの一部として[esp + 4]に格納されます)。
  • 次に、コードは、最初の数字は(numbers[0] = [ESP + 18])が0であり、第二の数(numbers[1] = [ESP + 1C])は次に1
  • であり、コードが静止をループしていることを確認各数字が前の2つの合計であることを確認して、数字の3番目から6番目の数字を返します。numbers[n - 1] + numbers[n - 2]。ループは、現在チェックされている番号を指すポインタと、未使用のダミー変数であるdummy_var_after_numbers([esp + 30])とを比較することによって行われる、数字のリストの最後を超えたときに終了します。数字のリストの直後(「7番目の数字」の種類)。

もしあなたが数学の背景を持っていれば、この論理はあなたの心の中ですぐにあなたの心に思いを吹き込みます:フィボナッチ!そうです、基本的にゼロを含むフィボナッチシリーズの最初の6つの数字を入力する必要があります:0, 1, 1, 2, 3, 5 - それはあなたのコードです!

|-------------------------------------------------------------------------------------| 
| rel. to current esp | rel. to esp at start | usage         | 
|-------------------------------------------------------------------------------------| 
|     +0 |     -3C | First parameter to read_six_numbers | 
|     +4 |     -38 | Second parameter to read_six_numbers | 
|     +8 |     -34 | (unused)        | 
|     +C |     -30 | (unused)        | 
|     +10 |     -2C | (unused)        | 
|     +14 |     -28 | (unused)        | 
|     +18 |     -24 | First number       | 
|     +1C |     -20 | Second number       | 
|     +20 |     -1C | Third number       | 
|     +24 |     -18 | Fourth number       | 
|     +28 |     -14 | Fifth number       | 
|     +2C |     -10 | Sixth number       | 
|     +30 |     -C | Dummy, compared against to end loop | 
|     +34 |     -8 | Original esi register from "push esi" | 
|     +38 |     -4 | Original ebx register from "push ebx" | 
|     +3C |     0 | Return address from function "phase_2" | 
|     +40 |     +4 | Argument to function "phase_2"   | 
|-------------------------------------------------------------------------------------| 

私は、コードがどのように動作するか、この答えは、それが明確になる願っています。参考のため

が、これは彼らが使用しているスタックとどのように変数のリストです。繰り返しますが、ここで別の構文(Intel)を使用することを余儀なくされて申し訳ありませんが、AT & T構文では私がうんざりしてしまい、私はそれを扱うことができません。

+0

優れた - 優れた作品! – DarthGizka