深入理解計算機系統(CSAPP)課程實驗bomb程式炸彈實驗日誌(phase_6)
找到phase_6的程式碼,比前面幾關都要長很多:
08048c89 <phase_6>: 8048c89: 55 push %ebp 8048c8a: 89 e5 mov %esp,%ebp 8048c8c: 57 push %edi 8048c8d: 56 push %esi 8048c8e: 53 push %ebx 8048c8f: 83 ec 5c sub $0x5c,%esp 8048c92: 8d 45 d0 lea -0x30(%ebp),%eax 8048c95: 89 44 24 04 mov %eax,0x4(%esp) 8048c99: 8b 45 08 mov 0x8(%ebp),%eax 8048c9c: 89 04 24 mov %eax,(%esp) 8048c9f: e8 67 04 00 00 call 804910b <read_six_numbers> 8048ca4: be 00 00 00 00 mov $0x0,%esi 8048ca9: 8d 7d d0 lea -0x30(%ebp),%edi 8048cac: 8b 04 b7 mov (%edi,%esi,4),%eax 8048caf: 83 e8 01 sub $0x1,%eax 8048cb2: 83 f8 05 cmp $0x5,%eax 8048cb5: 76 05 jbe 8048cbc <phase_6+0x33> 8048cb7: e8 15 04 00 00 call 80490d1 <explode_bomb> 8048cbc: 83 c6 01 add $0x1,%esi 8048cbf: 83 fe 06 cmp $0x6,%esi 8048cc2: 74 22 je 8048ce6 <phase_6+0x5d> 8048cc4: 8d 1c b7 lea (%edi,%esi,4),%ebx 8048cc7: 89 75 b4 mov %esi,-0x4c(%ebp) 8048cca: 8b 44 b7 fc mov -0x4(%edi,%esi,4),%eax 8048cce: 3b 03 cmp (%ebx),%eax 8048cd0: 75 05 jne 8048cd7 <phase_6+0x4e> 8048cd2: e8 fa 03 00 00 call 80490d1 <explode_bomb> 8048cd7: 83 45 b4 01 addl $0x1,-0x4c(%ebp) 8048cdb: 83 c3 04 add $0x4,%ebx 8048cde: 83 7d b4 05 cmpl $0x5,-0x4c(%ebp) 8048ce2: 7e e6 jle 8048cca <phase_6+0x41> 8048ce4: eb c6 jmp 8048cac <phase_6+0x23> 8048ce6: bb 00 00 00 00 mov $0x0,%ebx 8048ceb: 8d 7d d0 lea -0x30(%ebp),%edi 8048cee: eb 16 jmp 8048d06 <phase_6+0x7d> 8048cf0: 8b 52 08 mov 0x8(%edx),%edx 8048cf3: 83 c0 01 add $0x1,%eax 8048cf6: 39 c8 cmp %ecx,%eax 8048cf8: 75 f6 jne 8048cf0 <phase_6+0x67> 8048cfa: 89 54 b5 b8 mov %edx,-0x48(%ebp,%esi,4) 8048cfe: 83 c3 01 add $0x1,%ebx 8048d01: 83 fb 06 cmp $0x6,%ebx 8048d04: 74 16 je 8048d1c <phase_6+0x93> 8048d06: 89 de mov %ebx,%esi 8048d08: 8b 0c 9f mov (%edi,%ebx,4),%ecx 8048d0b: ba c4 c0 04 08 mov $0x804c0c4,%edx 8048d10: b8 01 00 00 00 mov $0x1,%eax 8048d15: 83 f9 01 cmp $0x1,%ecx 8048d18: 7f d6 jg 8048cf0 <phase_6+0x67> 8048d1a: eb de jmp 8048cfa <phase_6+0x71> 8048d1c: 8b 5d b8 mov -0x48(%ebp),%ebx 8048d1f: 8b 45 bc mov -0x44(%ebp),%eax 8048d22: 89 43 08 mov %eax,0x8(%ebx) 8048d25: 8b 55 c0 mov -0x40(%ebp),%edx 8048d28: 89 50 08 mov %edx,0x8(%eax) 8048d2b: 8b 45 c4 mov -0x3c(%ebp),%eax 8048d2e: 89 42 08 mov %eax,0x8(%edx) 8048d31: 8b 55 c8 mov -0x38(%ebp),%edx 8048d34: 89 50 08 mov %edx,0x8(%eax) 8048d37: 8b 45 cc mov -0x34(%ebp),%eax 8048d3a: 89 42 08 mov %eax,0x8(%edx) 8048d3d: c7 40 08 00 00 00 00 movl $0x0,0x8(%eax) 8048d44: be 00 00 00 00 mov $0x0,%esi 8048d49: 8b 43 08 mov 0x8(%ebx),%eax 8048d4c: 8b 13 mov (%ebx),%edx 8048d4e: 3b 10 cmp (%eax),%edx 8048d50: 7d 05 jge 8048d57 <phase_6+0xce> 8048d52: e8 7a 03 00 00 call 80490d1 <explode_bomb> 8048d57: 8b 5b 08 mov 0x8(%ebx),%ebx 8048d5a: 83 c6 01 add $0x1,%esi 8048d5d: 83 fe 05 cmp $0x5,%esi 8048d60: 75 e7 jne 8048d49 <phase_6+0xc0> 8048d62: 83 c4 5c add $0x5c,%esp 8048d65: 5b pop %ebx 8048d66: 5e pop %esi 8048d67: 5f pop %edi 8048d68: 5d pop %ebp 8048d69: c3 ret
依然是從新空間的開闢開始做。0x8048c9f位置讀入6個引數,0x8048ca4~0x8048cac執行的是與phase_5相似的陣列型別的操作,偏移量以4的倍數增長。
8048caf到8048cb7的程式碼說明,第一個數的值要小於等於6。否則將引爆炸彈。
從0x8048cbc開始,%esi的值開始從1往6遞增,直到等於6時才跳轉到0x8048ce6,否則將執行8048cc4。這裡應該是雙層迴圈的外層。
8048cc4將當前元素的地址傳入暫存器ebx儲存,並將當前的偏移量(陣列的下標)儲存到-0x4c(%ebp)位置暫存起來,這裡應該是內層迴圈的起點。再把陣列的下一個元素,也就是-0x4(%edi,%esi,4)位置的元素放入暫存器eax並將兩者比較,如果不相等則跳轉到0x8048cd7位置,否則將引爆炸彈。即第一個數不能和第二個數相等。
再看0x8048cd7位置,將前面暫存在-0x4c(%ebp)位置的值取出並加1,然後%ebx加4,指向陣列的下一個元素。
0x8048cde位置,將5和-0x4c(%ebp)位置的數,也就是內層迴圈的控制條件進行比較,當小於等於5時,說明內層迴圈還未結束,跳轉至0x8048cca繼續比較當前元素和下一個元素,需要滿足的條件同樣是兩個元素不相等,直到內層迴圈結束,0x8048ce4跳轉至0x8048cac處,重新開始外層迴圈。
至此可以分析出第一部分程式碼的意思是要求輸入的6個數據彼此都不能相等。所以這6個數據應該是大於0而小於等於6的。
當0x8048cc2處的跳轉條件滿足時,將結束整個迴圈,跳轉至0x8048ce6處。
0x8048ce6開始執行一個新的操作,將暫存器ebx的值清零,同樣將地址-0x30(%ebp)傳給%edi,然後直接跳轉至0x8048d06處。
0x8048d06開始先是將%ebx(也就是0)賦給%esi,使%esi獲得初始值0,再將(%edi,%ebx,4)位置的數取出,存入%ecx中,將立即數0x804c0c4存入暫存器%edx中。
0x8048d10開始將%eax的值變為1,將%ecx中的值(也就是記憶體中(%edi,%ebx,4)位置的數)和1做比較,如果大於1,則跳轉至0x8048cf0,否則跳轉至0x8048cfa。
轉向8048cf0位置,從8048cf0到8048cf8是一個迴圈,每迴圈一次,就更新一次%edx中的值,更新為記憶體地址為0x8(%edx)中的值,從這樣的操作可以發現,地址的下一個元素還是地址,由此可以聯想到的結構就是連結串列。直到%eax中的值和%ecx中的值相等為止。如果相等,就來到0x8048cfa位置。
0x8048cfa處開始是一個mov指令,將前面那個迴圈產生的最終的%edx中的值存入-0x48(%ebp,%esi,4)位置,若為第一輪操作,就是-0x48(%ebp),若為第二輪,就是-0x44(%ebp),依此次序每次向上遞增4個單元。(因為%esi,%ebx的值是變化的)
隨後%ebx加1,再和6做比較,不相等則繼續從0x8048d06重新開始,若相等,也就是6輪迴圈結束,則跳轉至0x8048d1c位置。
可以發現,這個6輪的迴圈總是會執行0x8048cfa開始的這一小部分程式碼,也就是說會有6組不同的%edx被傳入記憶體-0x48(%ebp)開始向後的6個空間。
接下來對0x8048ce6開始的這部分程式碼進行除錯,可以清楚地看到,暫存器%ecx中的值經歷了6個不同的值。而這6個值正是我們在除錯的時候輸入的。
並且,按不同的順序輸入6個數,每次存入對應記憶體位置的地址也不相同。
六輪迴圈結束後,跳轉至8048d1c位置。
與前面的程式碼實現的功能做一個類比可以發現,這裡一連串的mov指令應該是將連結串列中的元素做一個新的賦值對映。
同時,0x8048d44開始的比較+迴圈結構實現的功能是判斷連結串列元素是否是一個遞增的序列。
再次進入gdb除錯,檢視地址0x804c0c4(%edx)中的內容。
然後每次將%edx偏移0x8,檢視後會發現,首先edx中是內容,然後edx+0x8內的內容是一個地址,然後再檢視這個地址,地址內的內容是一個內容,然後在將這個地址偏移0x8得到的地址內的內容又是一個地址,可以看出來,這是一個典型的連結串列結構。連結串列就是不同的node節點,每個node節點在記憶體中存在不同的位置,不連續的位置中。每個node由內容和next指標構成,每個next指標內的值是一個指向下一個node節點的地址,最後一個node的next指向null。
這樣結合前面的分析就可以知道,我們輸入的6個數實際上就是不同的node節點數,根據我輸入的節點號,把節點內的內容重新進行排序,排成遞增序列。
將節點值按遞增順序排列,則節點號的順序應該為:
5,6,1,4,3,2
測試一下這個答案:
提示炸彈已解除。