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
のような構文を使用して必要なオペランドサイズを指定します(たとえば、cmpl
はcmp 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のような表現を生成することで逆コンパイラを実行した
:
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構文では私がうんざりしてしまい、私はそれを扱うことができません。
優れた - 優れた作品! – DarthGizka