APP爆弾ラボ



App Bomb Lab



実験タイトル:CS:APP爆弾実験室

目的:

バイナリ爆弾は、6つで構成されるLinux実行可能Cプログラムです。
フェーズ。各フェーズでは、学生が特定の文字列を入力することを期待しています
stdinで。学生が期待される文字列を入力した場合、そのフェーズ
解散します。そうでなければ爆弾はBOOMを印刷することによって爆発します!!!。
学生の目標は、できるだけ多くのフェーズを解除することです。:

実験環境:ubuntu12.04(32ビット)環境

実験内容と操作手順

最初に質問を受け取ったとき、。/ bombコマンドを使用して実行しようとしましたが、アクセス許可が不十分であることが示されました。インターネットでいくつかの解決策を確認し、sudoを使用しようとしましたが、それでも機能しませんでした。ティーチングアシスタントは、chmod777爆弾を使用して許可を与えることができると教えてくれました。スムーズに解決しました。次に、次の手順を実行して実行可能ファイルを逆アセンブルし、結果をファイルに出力します。



Linux:> Objdump -D bomb >test.txt

すべての準備が整いました。位相関数を1つずつ分析し始めます。

Phase_1:文字列の比較

08048b50 <phase_1>: 8048b50: 83 ec 1c sub $0x1c,%esp 8048b53: c7 44 24 04 64 a2 04 movl $0x804a264,0x4(%esp) //String parameter When I get angry, Mr. Bigglesworth gets upset. 8048b5a: 08 8048b5b: 8b 44 24 20 mov 0x20(%esp),%eax //Original string 8048b5f: 89 04 24 mov %eax,(%esp) 8048b62: e8 1d 05 00 00 call 8049084 <strings_not_equal> //Judging the input string 8048b67: 85 c0 test %eax,%eax 8048b69: 74 05 je 8048b70 <phase_1+0x20> //If the result is 0, detonate the bomb, otherwise jump to 8048b70 8048b6b: e8 26 06 00 00 call 8049196 <explode_bomb> 8048b70: 83 c4 1c add $0x1c,%esp //Return esp+0x1c 8048b73: c3 ret

①Phase_1関数の内容を確認すると、入力文字列0x20(%esp)と即値が指す文字列が。という関数に渡されていることがわかります。この関数の機能は、2つの文字列が等しいかどうかを判断することです。 gdbデバッグを有効にして、次のコマンドを入力します。



Linux:> x/s 0x804a264

画像

出力結果は次のようになります。私が怒ったとき、Bigglesworth氏は動揺し、検証のために結果を入力したところ、答えが正しいことがわかりました。

Phase_2:a [i + 1] = a [i] + iシーケンス

08048b74 <phase_2>: 8048b74: 53 push %ebx 8048b75: 83 ec 38 sub $0x38,%esp 8048b78: 8d 44 24 18 lea 0x18(%esp),%eax //18(esp) is the read parameter 8048b7c: 89 44 24 04 mov %eax,0x4(%esp) // 8048b80: 8b 44 24 40 mov 0x40(%esp),%eax // 8048b84: 89 04 24 mov %eax,(%esp) //read_six_numbers(0x40(%esp),0x18(%esp)) 8048b87: e8 3f 07 00 00 call 80492cb <read_six_numbers> //Read in 6 data 8048b8c: 83 7c 24 18 00 cmpl $0x0,0x18(%esp) //Compare 0x18 (%esp) and 0 8048b91: 79 05 jns 8048b98 <phase_2+0x24> //Nonnegative non-negative number, jump to 8048b98, so the first parameter is non-negative 8048b93: e8 fe 05 00 00 call 8049196 <explode_bomb> // 8048b98: bb 01 00 00 00 mov $0x1,%ebx //ebx=1 initialize ebx=1

①最初にこのコンパイルを確認します。read_six_numbers(0x40(%esp)、0x18(%esp))は6つのデータを読み取り、その中でcmpl $ 0x0,0x18(%esp)命令はシーケンスの最初の数値と0のサイズを比較します。非負が非負の場合は、8048b98にジャンプします。そうしないと爆発するため、最初の数値は非負でなければなりません。その後、見下ろし続けます。



/* loop part 8048b9d: 89 d8 mov %ebx,%eax //eax=i 8048b9f: 03 44 9c 14 add 0x14(%esp,%ebx,4),%eax //eax=*(esp+1*4+14)+eax,eax=a[i]+i 8048ba3: 39 44 9c 18 cmp %eax,0x18(%esp,%ebx,4) //Compare eax and a[i+1] 8048ba7: 74 05 je 8048bae //If equal, jump to 8048bae 8048ba9: e8 e8 05 00 00 call 8049196 //Otherwise it will explode 8048bae: 83 c3 01 add $0x1,%ebx //ebx=i+1 8048bb1: 83 fb 06 cmp $0x6,%ebx //compare ebx and 6 8048bb4: 75 e7 jne 8048b9d //If ebx! =6 then jump to 8048B9D, loop ‬5 times, compare one by one */

②このコードは0x14(%esp、%ebx、4)を追加し、%eaxはeax = a [i] + iを計算し、cmp%eax、0x18(%esp、%ebx、4)はeaxとa [i +1]を比較します。等しい場合は、8048baにジャンプします。そうしないと爆発します。したがって、爆弾を避けたい場合は、最初のa [0]が非負、2番目のa [i + 1] = a [の2つの条件を満たす必要があります。 i] + i、このルールに従って、シーケンスを作成します:1 2 4 7 11 16 21、結果が正しいことを確認します。

Phase_3:ブランチの切り替え

08048bbb <phase_3>: 8048bbb: 83 ec 3c sub $0x3c,%esp 8048bbe: 8d 44 24 28 lea 0x28(%esp),%eax 8048bc2: 89 44 24 10 mov %eax,0x10(%esp) 8048bc6: 8d 44 24 2f lea 0x2f(%esp),%eax 8048bca: 89 44 24 0c mov %eax,0xc(%esp) 8048bce: 8d 44 24 24 lea 0x24(%esp),%eax 8048bd2: 89 44 24 08 mov %eax,0x8(%esp) 8048bd6: c7 44 24 04 ba a2 04 movl $0x804a2ba,0x4(%esp) //String parameter 8048bdd: 08 8048bde: 8b 44 24 40 mov 0x40(%esp),%eax 8048be2: 89 04 24 mov %eax,(%esp) 8048be5: e8 86 fc ff ff call 8048870 <root@xxxxx> //Enter '%d %c %d', the order is (0x24,0x2f,0x28) 8048bea: 83 f8 02 cmp $0x2,%eax //Return the result and compare 2 8048bed: 7f 05 jg 8048bf4 <phase_3+0x39> //Jump to 8048bf4 if greater than 2, so the number of input parameters must be greater than 2 8048bef: e8 a2 05 00 00 call 8049196 <explode_bomb> 8048bf4: 83 7c 24 24 07 cmpl $0x7,0x24(%esp) //The first number must be less than 0x7 8048bf9: 0f 87 fc 00 00 00 ja 8048cfb <phase_3+0x140> //Greater than 7 jump to 8048CFB‬, explosion 8048bff: 8b 44 24 24 mov 0x24(%esp),%eax //Store the first number in eax 8048c03: ff 24 85 e0 a2 04 08 jmp *0x804a2e0(,%eax,4) //switch jump, assuming the first number is 0, then it will jump to *0x804a2e0=8048c0a 8048c0a: b8 63 00 00 00 mov $0x63,%eax //eax=63 8048c0f: 81 7c 24 28 82 01 00 cmpl $0x182,0x28(%esp) //Compare the third parameter with 0x182=386

①この機能を見て、 [メール保護] 関数、これはいくつかのデータを入力している必要がありますが、これらのデータの形式は関数の前のパラメーターの準備に依存します。イミディエート番号0x804a2baを参照し、gdbデバッグを有効にし、関数phase_3にブレークポイントを設定して、命令を実行します。

(gdb) x/s 0x804a2ba The result of finding this string is'%d %c %d'

前のパラメータを組み合わせると、次のことがわかります。 [メール保護] 呼び出しシーケンスは(0x24、0x2f、0x28)であるため、関数は整数、文字列、および整数を入力します。

8048bde: 8b 44 24 40 mov 0x40(%esp),%eax 8048be2: 89 04 24 mov %eax,(%esp) 8048be5: e8 86 fc ff ff call 8048870 <root@xxxxx> //Enter '%d %c %d', the order is (0x24,0x2f,0x28) 8048bea: 83 f8 02 cmp $0x2,%eax //Return the result and compare 2 8048bed: 7f 05 jg 8048bf4 <phase_3+0x39> //Jump to 8048bf4 if greater than 2, so the number of input parameters must be greater than 2 8048bef: e8 a2 05 00 00 call 8049196 <explode_bomb> 8048bf4: 83 7c 24 24 07 cmpl $0x7,0x24(%esp) //The first number must be less than 0x7 8048bf9: 0f 87 fc 00 00 00 ja 8048cfb <phase_3+0x140> //Greater than 7 jump to 8048CFB‬, explosion 8048bff: 8b 44 24 24 mov 0x24(%esp),%eax //Store the first number in eax 8048c03: ff 24 85 e0 a2 04 08 jmp *0x804a2e0(,%eax,4) //switch jump, assuming the first number is 0, then it will jump to *0x804a2e0=8048c0a 8048c0a: b8 63 00 00 00 mov $0x63,%eax //eax=63 8048c0f: 81 7c 24 28 82 01 00 cmpl $0x182,0x28(%esp) //Compare the third parameter with 0x182=386 8048c16: 00 8048c17: 0f 84 e8 00 00 00 je 8048d05 <phase_3+0x14a> //=182, jump to 8048d05

②この段落では、cmpl $ 0x7,0x24(%esp)、ja 8048cfbが7より大きく、8048CFBにジャンプして展開されるため、最初の数値は0x7未満である必要がありますx804a2e0(、%eax、4)は一般的なスイッチジャンプです、最初の数値が0であるとすると、* 0x804a2e0 = 8048c0aにジャンプします。
8048c0aが見つかり、命令mov $ 0x63、%eax cmpl $ 0x182,0x28(%esp)が3番目のパラメーターを0x182 = 386と比較していることがわかりました。パラメータ3が386に等しい場合にのみ爆発を回避できるため、最初のパラメータが0の場合、パラメータ3は386である必要があります。

8048cf9: eb 0a jmp 8048d05 <phase_3+0x14a> 8048cfb: e8 96 04 00 00 call 8049196 <explode_bomb> //explosion 8048d00: b8 63 00 00 00 mov $0x63,%eax 8048d05: 3a 44 24 2f cmp 0x2f(%esp),%al //Compare the lower 8 bits of eax, 0110 0011=c 8048d09: 74 05 je 8048d10 <phase_3+0x155>//Equal to pass 8048d0b: e8 86 04 00 00 call 8049196 <explode_bomb>

③2番目のパラメータを決定したら、8048d0にジャンプします。このとき、eaxの下位8ビットが01100011から取り出され、0x2f(%esp)と比較され、2番目のパラメーターがここに格納されるため、2番目のパラメーターascii = 0110 0011 = cとなり、3つのパラメーターは次のようになります。 0 c 386として決定されます。検証後、答えは正解です。しかし、ここには7つのジャンプテーブルがあるため、答えは一意ではありません。

Phase_4:再帰呼び出しを処理する

08048d81 <phase_4>: 8048d81: 83 ec 2c sub $0x2c,%esp 8048d84: 8d 44 24 1c lea 0x1c(%esp),%eax //Parameter 2 0x1c(%esp) 8048d88: 89 44 24 0c mov %eax,0xc(%esp) 8048d8c: 8d 44 24 18 lea 0x18(%esp),%eax //Parameter 1 0x18(%esp) 8048d90: 89 44 24 08 mov %eax,0x8(%esp) //Stored in 0x8(%esp) 8048d94: c7 44 24 04 a3 a4 04 movl $0x804a4a3,0x4(%esp) //String parameter '%d %d' 8048d9b: 08 8048d9c: 8b 44 24 30 mov 0x30(%esp),%eax 8048da0: 89 04 24 mov %eax,(%esp) 8048da3: e8 c8 fa ff ff call 8048870 <root@xxxxx> 8048da8: 83 f8 02 cmp $0x2,%eax //The number of inputs is equal to 2 8048dab: 75 0d jne 8048dba <phase_4+0x39> 8048dad: 8b 44 24 18 mov 0x18(%esp),%eax //eax=parameter 1 8048db1: 85 c0 test %eax,%eax //Parameter 1 8048db3: 78 05 js 8048dba <phase_4+0x39> //Parameter 1 cannot be negative 8048db5: 83 f8 0e cmp $0xe,%eax //Compare parameter 1 and 0xe=14 8048db8: 7e 05 jle 8048dbf <phase_4+0x3e> //Parameter 1 must be less than or equal to 0xe 8048dba: e8 d7 03 00 00 call 8049196 <explode_bomb> //explosion 8048dbf: c7 44 24 08 0e 00 00 movl $0xe,0x8(%esp) //0x8(%esp)=0xe, the third parameter of func4 8048dc6: 00 8048dc7: c7 44 24 04 00 00 00 movl $0x0,0x4(%esp) //0x4(%esp)=0, the second parameter of func4 8048dce: 00 8048dcf: 8b 44 24 18 mov 0x18(%esp),%eax //eax=parameter 1, the first parameter of func4 8048dd3: 89 04 24 mov %eax,(%esp) 8048dd6: e8 39 ff ff ff call 8048d14 <func4> //Call func4(arg1,0,14), the return value must be 1 8048ddb: 83 f8 01 cmp $0x1,%eax //Compare the result with 1 8048dde: 75 07 jne 8048de7 <phase_4+0x66> //If the result is not equal to 1, then explode, the result of func4 must be 1 8048de0: 83 7c 24 1c 01 cmpl $0x1,0x1c(%esp) //Compare parameter 2 with 1 8048de5: 74 05 je 8048dec <phase_4+0x6b> //If parameter 2=1, jump, indicating that parameter 2 must be equal to 1

①ここで呼ばれているのがわかります [メール保護] 関数、この前に即時値0x804a4a3があります。 gdbがデバッグしているとき、x / s 0x804a4a3は文字列を調べ、結果は '%d%d'になります。これは、2つの整数パラメーターを渡す必要があることを意味します。mov0x18(%esp)、%eaxおよびtest%eax、% eaxは、最初のパラメーターが負の場合に判断し、爆発して、最初のパラメーターが負であってはならないことを示します。 cmp $ 0xe、%eaxおよびjleは、爆発を回避するためにパラメーター1が0xe以下であるかどうかを判断するため、最初のパラメーターも0xe未満の条件を満たす必要があります。次に、関数呼び出しを行い、2つのパラメーターをfunc4に渡します。呼び出しが終了した後、cmp $ 0x1、%eaxは結果を1 jne 8048de7と比較します。結果が1に等しくない場合、爆発、func4の結果は1でなければなりません。cmpl$ 0x1,0x1c(%esp)パラメーター2を比較します。 1の場合、パラメーター2 = 1の場合、ジャンプします。これは、パラメーター2が1に等しくなければならないことを示します。

08048d14 <func4>: 8048d14: 83 ec 1c sub $0x1c,%esp //Compare with 1 8048d17: 89 5c 24 14 mov %ebx,0x14(%esp) 8048d1b: 89 74 24 18 mov %esi,0x18(%esp) 8048d1f: 8b 54 24 20 mov 0x20(%esp),%edx //Parameter 1 8048d23: 8b 44 24 24 mov 0x24(%esp),%eax //Parameter 2 8048d27: 8b 5c 24 28 mov 0x28(%esp),%ebx //Parameter 3 8048d2b: 89 d9 mov %ebx,%ecx 8048d2d: 29 c1 sub %eax,%ecx //Parameter 3-parameter 2->ecx 8048d2f: 89 ce mov %ecx,%esi //ecx->esi 8048d31: c1 ee 1f shr $0x1f,%esi //esi>>31 bits, get the sign bit 8048d34: 01 f1 add %esi,%ecx //Add the sign bit to ecx 8048d36: d1 f9 sar %ecx //sar $1 %ecx shifts the ecx arithmetic by 1 digit to the right, and the above three points are integrated into (ecx>>31 + ecx3) >> 1 8048d38: 01 c1 add %eax,%ecx //ecx+=arg2 8048d3a: 39 d1 cmp %edx,%ecx //Compare arg2+(arg3>>31 + arg3) >> 1 and parameter 1 8048d3c: 7e 17 jle 8048d55 <func4+0x41> //Less than or equal to parameter 1, jump, otherwise call the function recursively 8048d3e: 83 e9 01 sub $0x1,%ecx //ecx-1 8048d41: 89 4c 24 08 mov %ecx,0x8(%esp) //arg3 8048d45: 89 44 24 04 mov %eax,0x4(%esp) //arg2 8048d49: 89 14 24 mov %edx,(%esp) //arg1 8048d4c: e8 c3 ff ff ff call 8048d14 <func4> //func4(edx,eax,ecx), ecx is changed, the third parameter 8048d51: 01 c0 add %eax,%eax //The result*2 8048d53: eb 20 jmp 8048d75 <func4+0x61> //End of function 8048d55: b8 00 00 00 00 mov $0x0,%eax //8048d3c jump to here, eax=0 8048d5a: 39 d1 cmp %edx,%ecx //Compare ecx-edx parameter 1 8048d5c: 7d 17 jge 8048d75 <func4+0x61> //If ecx>=edx, return 8048d5e: 89 5c 24 08 mov %ebx,0x8(%esp) //Otherwise, put parameter 3 back to arg3 8048d62: 83 c1 01 add $0x1,%ecx //Set ecx+1 as arg2 8048d65: 89 4c 24 04 mov %ecx,0x4(%esp) 8048d69: 89 14 24 mov %edx,(%esp) //edx parameter one as arg1 8048d6c: e8 a3 ff ff ff call 8048d14 <func4> //Call function recursively 8048d71: 8d 44 00 01 lea 0x1(%eax,%eax,1),%eax //Return 2*eax+1 8048d75: 8b 5c 24 14 mov 0x14(%esp),%ebx //return 8048d79: 8b 74 24 18 mov 0x18(%esp),%esi

②func4を見てください。最初に、この関数がパラメーター3をecxに格納し、次にecxをesiに入れ、次にshr $ 0x1f、%esiがesi >> 31を符号ビットに取り、%esi、%ecxを追加することを確認しました。符号ビットをecxに追加します。 sar%ecx sar $ 1%ecxは、ecx演算を1ビット右にシフトします。上記の3つのポイントの統合は(ecx >> 31 + ecx)>> 1です。最も追加される%eax、%ecxは、結果と2番目のパラメーターをecxに格納することです。次に、cmp%edx、%ecxは、arg2 +(arg3 >> 31 + arg3)>> 1とパラメーター1のサイズを比較します。以下の場合は、行8048d55にジャンプし、等しい場合は0を返します。再帰的コールfunc4、lea 0x1(%eax、%eax、1)、%eax未満の場合は、戻り値func4(x、tmp + 1、z)* 2 +1を直接計算します。 smallがsub $ 0x1の実行よりも大きい場合、%ecxはecx-1を実行してから、func4(x、z、tmp-1)* 2を呼び出します。
③上記の分岐条件と呼び出し規則により、Cコードを記述できます。

int func4(int x, int y, int z) { int tmp=(((z-y)+((z-y)>>31))>>1)+y if (tmp<=x) { if (tmp==x) return 0 else return func4(x,tmp+1,z)*2+1 } else return func4(x,z,tmp-1)*2 }

③最後に、phase_4関数に戻り、func4の結果を1にする必要があるため、結果が1のデータを生成するプログラムを作成しました。

int main() { int x for(int i=0i<14i++) { printf('(%d,0,14)=%d ',i,func4(i,0,14)) } system('pause') return 0 } (0,0,14)=0 (1,0,14)=0 (2,0,14)=0 (3,0,14)=0 (4,0,14)=0 (5,0,14)=0 (6,0,14)=0 (8,0,14)=1 (9,0,14)=1 (10,0,14)=1 (11,0,14)=1 (12,0,14)=3 (13,0,14)=3 (7,0,14)=0

上記の1の結果の最初のパラメーターは、フェーズ4を解決するためのパラメーター1として使用できます。 8 1の組み合わせを選択すると、答えが正しいことが確認されます。

Phase_5は、下位4ビットとシーケンスを構成します

08048df0 <phase_5>: 8048df0: 53 push %ebx 8048df1: 83 ec 18 sub $0x18,%esp 8048df4: 8b 5c 24 20 mov 0x20(%esp),%ebx 8048df8: 89 1c 24 mov %ebx,(%esp) 8048dfb: e8 6b 02 00 00 call 804906b <string_length> //Read the length of the string 8048e00: 83 f8 06 cmp $0x6,%eax //The length must be equal to 6, otherwise it will explode 8048e03: 74 05 je 8048e0a <phase_5+0x1a> //Equal to 6, jump to 8048e0a 8048e05: e8 8c 03 00 00 call 8049196 <explode_bomb> 8048e0a: ba 00 00 00 00 mov $0x0,%edx 8048e0f: b8 00 00 00 00 mov $0x0,%eax

①Phase_5の最初の部分はstring_length関数を呼び出して、戻り値len cmp $ 0x6を判断します。%eaxの長さは6に等しくなければなりません。そうでない場合、爆発するため、入力文字は6のみである必要があります。

8048e14: 0f be 0c 03 movsbl (%ebx,%eax,1),%ecx //Calculate (eax+ebx)->ecx, which is the eaxth character of a string of length 6 //The movzbl instruction is responsible for copying a byte and filling the rest of its destination operand with 0. This extension method is called 'zero extension' //The movsbl instruction is responsible for copying a byte and filling the rest of the destination operand with the highest bit of the source operand. This extension method is called 'sign extension' 8048e18: 83 e1 0f and $0xf,%ecx //Take the lower 4 bits of ecx 8048e1b: 03 14 8d 00 a3 04 08 add 0x804a300(,%ecx,4),%edx //Add 0x804a300(,%ecx,4) to edx 8048e22: 83 c0 01 add $0x1,%eax //eax count 8048e25: 83 f8 06 cmp $0x6,%eax //Compare 6 8048e28: 75 ea jne 8048e14 <phase_5+0x24> 8048e2a: 83 fa 2f cmp $0x2f,%edx //Compare the values ​​of 0x2f and edx, and require that it be equal to 0x2f 8048e2d: 74 05 je 8048e34 <phase_5+0x44> //Success if equal 8048e2f: e8 62 03 00 00 call 8049196 <explode_bomb> 8048e34: 83 c4 18 add $0x18,%esp 8048e37: 5b pop %ebx 8048e38: c3 ret

②Phase_5の2番目の部分は、最初にedxとeaxを初期化します。次のmovsbl(%ebx、%eax、1)によれば、%ecxのアドレッシングモードとジャンプ条件はループプロセスであると判断できます。 movzbl命令は、1バイトをコピーし、宛先オペランドの残りを0で埋めます。$ 0xf、%ecxはecxの下位4ビットを取得し、0x804a300(、%ecx、4)を追加し、%edxは0x804a300( 、%ecx、4)to edx、cmp $ 0x2f、%ループ実行の終了後、edxは0x2fとedxの値を比較し、最後に0x2fと等しいことを要求します。そうでない場合、爆発します。したがって、私たちのタスクは、6文字を入力し、この文字の下位4ビットを取得し、0x804a300(、%ecx、4)に従って0x804a300配列の値を見つけることです。累積の結果は、0x2fである必要があります。

③gdbデバッグを有効にし、次の図に示すように、x / 16wxを使用して0x804a300の値を表示します。次の表で6つのデータの選択を繰り返し、0x2fを満たす必要があります。私が構築した値のセットは10+ 10 + 10 + 10 + 6+です対応する配列の添え字は11 1 1 2 3 asciiコードテーブルでは、次のようにデータの下位4ビットを見つけるだけで済みます0001/010/0011の3つの値:
画像
画像
したがって、解の結果はaaaabcであり、これは正しいことが確認されています。

Phase_6:リンクリスト

08048e39 <phase_6>: //phase_6 8048e39: 56 push %esi 8048e3a: 53 push %ebx 8048e3b: 83 ec 44 sub $0x44,%esp 8048e3e: 8d 44 24 10 lea 0x10(%esp),%eax 8048e42: 89 44 24 04 mov %eax,0x4(%esp) 8048e46: 8b 44 24 50 mov 0x50(%esp),%eax 8048e4a: 89 04 24 mov %eax,(%esp) 8048e4d: e8 79 04 00 00 call 80492cb <read_six_numbers> //Read 6 data, input manually 8048e52: be 00 00 00 00 mov $0x0,%esi //Execute loop 8048e57: 8b 44 b4 10 mov 0x10(%esp,%esi,4),%eax 8048e5b: 83 e8 01 sub $0x1,%eax //x-1 8048e5e: 83 f8 05 cmp $0x5,%eax //x-1 compared with 5 8048e61: 76 05 jbe 8048e68 <phase_6+0x2f> //x-1 is less than or equal to 5, that is, x<=6 8048e63: e8 2e 03 00 00 call 8049196 <0> 8048e68: 83 c6 01 add $0x1,%esi 8048e6b: 83 fe 06 cmp $0x6,%esi //Judgment subscript esi 8048e6e: 74 1b je 8048e8b <phase_6+0x52> //esi=6, jump to 8048e8b, otherwise continue to execute the loop 8048e70: 89 f3 mov %esi,%ebx // 8048e72: 8b 44 9c 10 mov 0x10(%esp,%ebx,4),%eax //I found that esi was assigned to ebx, and ebx+1 was executed later, so I concluded that this is a 2-layer loop 8048e76: 39 44 b4 0c cmp %eax,0xc(%esp,%esi,4) //Compare a[eip] and a[ebx] 8048e7a: 75 05 jne 8048e81 <phase_6+0x48> //Satisfy a[eip] is not equal to a[ebx] 8048e7c: e8 15 03 00 00 call 8049196 <explode_bomb> // 8048e81: 83 c3 01 add $0x1,%ebx 8048e84: 83 fb 05 cmp $0x5,%ebx // 8048e87: 7e e9 jle 8048e72 <phase_6+0x39> 8048e89: eb cc jmp 8048e57 <phase_6+0x1e>//for(eip for(ebx)) //The two conditions are a[i]<=6,a[i]!=a[j]

①最初は、アセンブリコードが非常に長く、手がかりがないことを知りましたが、機能に気づきましたが、見下ろしてみると、前の部分で6つの入力データの基本的なテストが行​​われていることがわかりました。 2つのコードcmp $ 0x5、%eax // x-1を5と比較すると、jbe8048e68ではxが6以上である必要があります。そうでない場合、爆発が発生します。 cmp%eax、0xc(%esp、%esi、4)a [eip]とa [ebx]を比較すると、jne8048e81はa [eip]がa [ebx]と等しくないことを満たしているため、入力データは[私]<=6,a[i]!=a[j]

②入力範囲は、データが6以下でなければならず、互いに等しくないことです。 1、2、3、4、5、6のデータであると判断し、gdbでデバッグし、phase_6にブレークポイントを設定しました。

③当面の番号を見つけることは非常に重要です。 gdbでデバッグするとき、この即値の連続データを出力しましたが、実際にはそのような構造であることがわかりました。そして、より特徴的なのは、これらのデータが3つと3つのグループであり、2つ目は順次増加する添え字であり、3つ目は明らかにリンクリストである次の要素ノードのアドレスであるため、最初のAn要素はこのノードの情報値-彼の重み。
画像

ソート後、取得した構造データは次のとおりです。

0x0000035d 0x00000001 0x0804c148 0x000002eb 0x00000002 0x0804c154 0x000002bb 0x00000003 0x0804c160 0x000000eb 0x00000004 0x0804c16c 0x00000380 0x00000005 0x0804c178 0x0000009f 0x00000006 0x00000000

重み+添え字+アドレス

④振り返ってみると、途中にたくさんのアセンブリコードがあり、機能を爆発させていないようだったので、直接スキップしました。
8049196が呼び出されますが、重要な点は、phase_6が終了する前のアセンブリコードの一部で、リンクリスト全体がトラバースされたことです。あなたは何をした?最初に5つの要素をトラバースし、esiは添え字を格納し、次にmov0x8(%ebx)、%eaxは毎回eaxにebxの値を格納します。このベースアドレスオフセットの結果は、次のノードの重みです。そして、cmp%edx、(%ebx)jgeを満たすには、爆弾を爆発させないために、このシーケンスを減らす必要があります。

//Verify whether the data is in decreasing order 8048f07: be 05 00 00 00 mov $0x5,%esi //Loop through the entire linked list node 8048f0c: 8b 43 08 mov 0x8(%ebx),%eax //Place the value of ebx, which is the next address corresponding to the linked list, in eax 8048f0f: 8b 10 mov (%eax),%edx //Access (eax)->edx 8048f11: 39 13 cmp %edx,(%ebx) //Compare edx and ebx, edx records the previous value 8048f13: 7d 05 jge 8048f1a <phase_6+0xe1> //When (%ebx)>=%edx, swap to ebx 8048f15: e8 7c 02 00 00 call 8049196 <explode_bomb> //Otherwise it explodes 8048f1a: 8b 5b 08 mov 0x8(%ebx),%ebx //Put the new value into ebx 8048f1d: 83 ee 01 sub $0x1,%esi 8048f20: 75 ea jne 8048f0c <phase_6+0xd3> 8048f22: 83 c4 44 add $0x44,%esp 8048f25: 5b pop %ebx 8048f26: 5e pop %esi 8048f27: c3 ret

⑤したがって、この関数は各ノードの重みに従ってノードをソートする可能性が高いと結論付けます。したがって、これらのノードを重みの降順で並べ替えて、新しいシーケンスを取得します。

0x00000380 0x00000005 0x00000000 0x0000035d 0x00000001 0x0804c154 0x000002eb 0x00000002 0x0804c160 0x000002bb 0x00000003 0x0804c16c 0x000000eb 0x00000004 0x0804c178 0x0000009f 0x00000006 0x00000000

⑥512 3 4 6.検証の結果、爆弾はまだ爆発していることがわかりました。どうしてこれなの?コードのスキップされたセクションは省略できないようです。振り返ってみると、添え字ごとにsub(%eax)と%edxが7ずつ減算されていることがわかりました。最初はわからなかったのですが、これが落とし穴だと気づきました!私はすぐにシーケンスを7-xバージョンに変更しました:2 6 5 4 3 1検証後、これは正しいです!

画像

Secret_pause

何?秘密のレベルもありますが、最初は完全に拒否しました。しかし、私は弾丸を噛むことができますか? Secret_pauseを探す
関数では、func7が呼び出されていることがわかりますが、Secret_pauseのシークレットレベルに入るにはどうすればよいですか? phase_defused関数内で見つけましたが、いくつかの奇妙なイミディエートがあり、gdb x / sを使用してチェックしています。

804934e: c7 44 24 04 a9 a4 04 movl $0x804a4a9,0x4(%esp) //String parameter %d %d %s 8049355: 08 8049356: c7 04 24 d0 c4 04 08 movl $0x804c4d0,(%esp) //This is the location of the parameter, '8 1' 804935d: e8 0e f5 ff ff call 8048870 <root@xxxxx> 8049362: 83 f8 03 cmp $0x3,%eax //Input data is equal to 3 8049365: 75 35 jne 804939c <phase_defused+0x81> 8049367: c7 44 24 04 b2 a4 04 movl $0x804a4b2,0x4(%esp) //Store 0x804a4b2 into 0x4(%esp) the second parameter, DrEvil 804936e: 08 804936f: 8d 44 24 2c lea 0x2c(%esp),%eax 8049373: 89 04 24 mov %eax,(%esp) 8049376: e8 09 fd ff ff call 8049084 <strings_not_equal> 804937b: 85 c0 test %eax,%eax 804937d: 75 1d jne 804939c <phase_defused+0x81> //Determine whether the input is equal 804937f: c7 04 24 78 a3 04 08 movl $0x804a378,(%esp) //0x804a378 8049386: e8 75 f4 ff ff call 8048800 <root@xxxxx> 804938b: c7 04 24 a0 a3 04 08 movl $0x804a3a0,(%esp) //But finding it and solving it are quite different... 8049392: e8 69 f4 ff ff call 8048800 <root@xxxxx> 8049397: e8 dd fb ff ff call 8048f79 <secret_phase>

①DrEvilの2つの文字列といくつかのプロンプトメッセージを除いて、ブレークスルーは見つかりませんでしたが、プロンプトメッセージを出力する(つまり、シークレットレベルに入る)には、2つの条件を満たす必要があることがわかりました。 1つ目はcmmpl $ 0x6,0x804c3ccです。この即値の数は6に等しくなければなりません。2つ目は、関数を比較する前にパラメーター準備段階があります。 2番目のパラメーターは、上記で取得したアドレス$ 0x804a4b2の文字列DrEvilです。最初のパラメータは何ですか? ?
画像

②gdbデバッグを有効にし、ブレークポイントをb * 0x8049356(イミディエート番号0x804c4d0が配置されている行)に設定し、x / s 0x804c4d0を使用してアドレスのコンテンツを表示すると、彼が実際にコンテンツを入力したことがわかります '8 phase_4の1 '。私はこれを推測しますフェーズとの何らかの関係があるに違いありません。

画像

③関数のパラメータの1つがDrEvilであり、他は何であるかに注意してください。入力はありますか?彼はそれをespに入れ、その前に一時的にeaxを介して保存しました。パラメーターの値は、eaxを直接印刷したところ、空であることがわかりました。理由は何ですか?なぜ空なのか、8048870に注意してください [メール保護] 3つのパラメーターが必要であり、それらは%d%d%sの形式であるため、他の入力ポートがないため、入力ポイントはphase_4のみになります。したがって、phase_4で8 1 helloworldを決定的に入力します。次に、gdbがデバッグすると、最初のパラメーターp $ eaxが出力され、結果は実際にはhelloWorld!になります。 、等しいと判断される限り、secret_phaseに入ることができるので、ロジックは非常に明確です!

Pahse4 input%d %d %s------>s==DrEvil?----------> stringsEqual---->secretPhase

画像

④Secret_pauseが正常にアクティブ化された後、読み取りを続行すると、最初に1行が読み取られ、戻り値%eaxが関数として使用されていることがわかります。 [メール保護] のパラメータの1つ、他の2つのパラメータはそれぞれ0xaと0x0です。 lea -0x1(%eax)、%eaxおよびcmp $ 0x3e8、%eaxから、これらの2つの文は、入力10進数が1001以下でなければならないことを認識しています。次にfunc7を呼び出すと、関数の3つのパラメーターはp /です。 x * 0x804c088、これは0x24であり、intデータを読み取ります。

8048f91: 00 8048f92: 89 04 24 mov %eax,(%esp) //Parameter 1, readline read 8048f95: e8 46 f9 ff ff call 80488e0 <root@xxxxx> 8048f9a: 89 c3 mov %eax,%ebx 8048f9c: 8d 40 ff lea -0x1(%eax),%eax //eax-1 8048f9f: 3d e8 03 00 00 cmp $0x3e8,%eax //1000 8048fa4: 76 05 jbe 8048fab <secret_phase+0x32> //eax-1<=1000, which means eax<=1001 8048fa6: e8 eb 01 00 00 call 8049196 <explode_bomb> 8048fab: 89 5c 24 04 mov %ebx,0x4(%esp) 8048faf: c7 04 24 88 c0 04 08 movl $0x804c088,(%esp) //Parameter 1, p/x *0x804c088 is 0x24 8048fb6: e8 6d ff ff ff call 8048f28 <fun7> 8048fbb: 83 f8 07 cmp $0x7,%eax //The return value must be 0x7 8048fbe: 74 05 je 8048fc5 <secret_phase+0x4c> 8048fc0: e8 d1 01 00 00 call 8049196 <explode_bomb> 8048fc5: c7 04 24 94 a2 04 08 movl $0x804a294,(%esp) 8048fcc: e8 2f f8 ff ff call 8048800 <root@xxxxx> 8048fd1: e8 45 03 00 00 call 804931b <phase_defused> 8048fd6: 83 c4 18 add $0x18,%esp 8048fd9: 5b pop %ebx

①Func7機能

08048f28 <fun7>: 8048f28: 53 push %ebx 8048f29: 83 ec 18 sub $0x18,%esp 8048f2c: 8b 54 24 20 mov 0x20(%esp),%edx //arg1 8048f30: 8b 4c 24 24 mov 0x24(%esp),%ecx //arg2 8048f34: 85 d2 test %edx,%edx 8048f36: 74 37 je 8048f6f <fun7+0x47> //arg1=null, return 0xffffffff 8048f38: 8b 1a mov (%edx),%ebx //ebx=*(arg1) 8048f3a: 39 cb cmp %ecx,%ebx //Compare*(arg1) and arg2 8048f3c: 7e 13 jle 8048f51 <fun7+0x29> 8048f3e: 89 4c 24 04 mov %ecx,0x4(%esp) //*(arg1)>arg2 8048f42: 8b 42 04 mov 0x4(%edx),%eax 8048f45: 89 04 24 mov %eax,(%esp) 8048f48: e8 db ff ff ff call 8048f28 <fun7> 8048f4d: 01 c0 add %eax,%eax 8048f4f: eb 23 jmp 8048f74 <fun7+0x4c> 8048f51: b8 00 00 00 00 mov $0x0,%eax //*(arg1)<=arg2 8048f56: 39 cb cmp %ecx,%ebx 8048f58: 74 1a je 8048f74 <fun7+0x4c> //*(arg1) 8048f5a: 89 4c 24 04 mov %ecx,0x4(%esp) 8048f5e: 8b 42 08 mov 0x8(%edx),%eax 8048f61: 89 04 24 mov %eax,(%esp) 8048f64: e8 bf ff ff ff call 8048f28 <fun7> 8048f69: 8d 44 00 01 lea 0x1(%eax,%eax,1),%eax //2*func7(arg1+8,arg2)+1 8048f6d: eb 05 jmp 8048f74 <fun7+0x4c> 8048f6f: b8 ff ff ff ff mov $0xffffffff,%eax 8048f74: 83 c4 18 add $0x18,%esp //*(arg1)==arg2 8048f77: 5b pop %ebx 8048f78: c3 ret

この関数の関数は(パラメーターがa、bであると仮定して)、最初の判断です aがnullポインタであるかどうか、はいの場合は-1を返し、そうでない場合は判断します。 aとbのサイズ( a == bの場合、0を返します。2 + 1 if a> b次に、後方を4バイトオフセットし、この関数を再帰的に処理して、結果を取得します* 2ポインタは最初は0x804c088です。このアドレステーブルを出力して、アドレス値を示します。実際、これもリンクリスト各ノードの構造には、接続されたノードの重みと2つのアドレスが含まれます。検証後、これはバイナリツリー構造になります。

func7関数は次のように復元できます。

int func7(int* a,int b) { if(a==null) return 0xffffffff if(*a<=b) { if(*a==b) return 0 return 2*func7(a+8,b)+1 } return 2*func7(a+4,b) }

画像

func7( レベルを正常に通過するには、a、b)の結果が0x7である必要があります。結果が7のソリューションを作成する必要があります。関数の境界は値0を返す必要があります。次に、次のように作成できます。開始ノード24から右の子ノードに開始して32にアクセスし、次に右の子にアクセスします。 32のノード6b、次に右側の子ノード3e9にアクセスしてここに戻ると、結果は0 ----- 1 ------ 2になります。 1 + 1 = 3 ------ 3 * 2 + 1 = 7、結果は7なので、10進数で1001である3e9を選択でき、答えが正しいことが確認されます。

実験結果と分析:

①最終的な答えは次のとおりです。2、3、4、5、6の答えは一意ではありません

When I get angry, Mr . Bigglesworth gets upset. 1 2 4 7 11 16 21 0 c 386 8 1 aaaabc 2 6 5 4 3 1 1001

②プログラム検証後の正解

獲得と経験:

①この実験を通じて、gdbデバッグの使い方をより深く理解し、デバッグ能力を発揮しました。
②movzblおよびmovsbl命令を新たに理解する:movzblはバイトをコピーし、その宛先オペランドの残りを0で埋める役割を果たします。この展開方法はゼロ展開と呼ばれます。movsbl命令はバイトをコピーし、ソースオペランドの最上位ビットを持つデスティネーションオペランドの残り。この拡張方法は符号拡張と呼ばれます
③プロシージャコールとパラメータ準備についての理解を深める
④x/ outputとpoutputの違いをさらに理解し、どちらの表示方法がデータをメモリに保存するのに効果的かを学びました。