CSAPP lab2 二進制拆彈 binary bombs phase_6


給出對應於7個階段的7篇博客

phase_1  https://www.cnblogs.com/wkfvawl/p/10632044.html
phase_2  https://www.cnblogs.com/wkfvawl/p/10636214.html
phase_3  https://www.cnblogs.com/wkfvawl/p/10651205.html
phase_4  https://www.cnblogs.com/wkfvawl/p/10672680.html
phase_5  https://www.cnblogs.com/wkfvawl/p/10703941.html
phase_6  https://www.cnblogs.com/wkfvawl/p/10742405.html
secret_phase  https://www.cnblogs.com/wkfvawl/p/10745307.html

 

phase_6

phase_6要求輸入6個1~6的數,這6個數不能重復。phase_6根據用戶的輸入,將某個鏈表按照用戶的輸入的值(進行某種計算后)進行排序,如果最終能排成降序,則解題成功。

phase_6主要考察學生對C語言指針、鏈表以及結構的機器級表示的掌握程度。

觀察框架源文件bomb.c:

 

 

從上可以看出:

1、首先調用了read_line()函數,用於輸入炸彈秘鑰,輸入放置在char* input中。

2、調用phase_6函數,輸入參數即為input,可以初步判斷,phase_6函數將輸入的input字符串作為參數。

因此下一步的主要任務是從asm.txt中查找在哪個地方調用了readline函數以及phase_6函數。

1.1 尋找並分析調用phase_6函數的代碼

打開asm.txt,尋找phase_6函數。

 

 

phase_1類似分析:

1、當前棧的位置存放的是read_line函數讀入的一串輸入;

2、phase_6的函數入口地址為0x8048e81

此時的函數棧為:

 

1.2 phase_6分析

asm.txt中繼續尋找phase_6,或者尋找0x8048e81,找到phase_6函數入口:

 584-591行:初始化函數棧幀,然后調用read_six_numbers函數。調用之后,從input中讀取了6個數num[0]  ~  num[5]read_siz_numbers函數分析參見前面),位於esp+0x10  ~esp+0x24,此時函數棧幀如下圖所示:

 

 

2592行:0  --> esi

3、593-597行:判斷(esp + esi*4 + 0x10)是否小於等於6以及大於等於1,如果不滿足,則引爆炸彈(625行);也即輸入的數(esi=0時,為num[0]esi=1時,為num[1]......)應該大於等於1,同時小於等於6。注意,比較時,先將該值減1594行),然后與5進行比較(595行),比較時用的jbe無符號整數比較),也即,如果該輸入值小於1,減1之后變成一個很大的無符號數(負數),肯定是大於5的。因此這幾行就實現了判斷num[esi] >=1 && num[esi] <=6。(這應該是編譯器做的優化

4598行:esi += 1

5、599-602行:esi6進行比較,如果等於,則意味着6個數已經比較完畢,跳轉到8048ef7。

6603行:如果602行沒有跳轉,也即6個數還沒有判斷完畢,則繼續執行,將esi賦值給ebx

7604-607行:判斷num[ebx]是否與num[esi-1]相等,如果相等,則引爆炸彈;

8608-610行:ebx+=1,然后判斷ebx是否小於等於5,如果是,則跳轉到8048ec1,即604行,也即跳轉到第7步。

9611行:跳轉到8048e9f,進行num[esi]的比較(注意num[esi]在第608行加1

10、綜合以上分析,可以判斷出以上代碼的作用是:

1)判斷每個輸入的數應小於等於6,大於等於1

2num[i]不等於它的后續的每個數;

3)也即輸入的6個數,應是1/2/3/4/5/6,但順序不一定。

使用類c語言描述:

for (i = 0; i < 6; i++)
{
    if ((num[i] < 1) || (num[i] > 6))
    {
        explode_bomb();
    }
    for (j = i + 1; j < 6; j++)
    {
        if (num[i] == num[j])
        {
            explode_bomb();
        }
    }
}

 

 

14622行(0x8048fa2<phase_6+0x90>- 625行:ebx保存到esimov %ebx, %esi),將esp + ebx*4 + 0x10的內容(當ebx=0時,為num[0],當ebx=1時,為num[1]......)與1相比較,如果 esp + ebx*4 + 0x10 <= 1624行),則跳轉到 8048ee6 <phase_6+0x65>(625行),否則繼續執行626行。(根據前面分析,ecx為輸入的num的值,僅且僅當ecx=1時,執行這個625行跳轉語句,跳轉到8048ee6 <phase_6+0x65>

15617行(0x8048f91<phase_6+0x7f>):當ecx=1時,會執行該條語句,將0x804c174送入到edx。查看0x804c174地址的內容(objdump --start-address=0x804c174  -s bomb):

 

 

16618行:將edx內容送入到esp + esi*4 + 0x28

17619-621行:ebx += 1,然后與6相比較,如果等於6,則跳轉到0x8048f0e 。

18、如果不等於6,則繼續執行622行,對於本文,即跳轉到第14步,前面分析了ecx=1的情況,如果ecx不等於1,則應繼續執行626-628

19626-628行:eax賦值為1edx賦值為0x804c174,跳轉到8048eda <phase_6+0x59>。(第612行)

20612-615行:這是一個循環。判斷ecxnum[ebx])是否等於eax,如果不是,則將edx + 8的內容送入到edx,然后繼續判斷, edx +8的內容應該是指向的是一個地址。如果相等,則跳轉到 8048eda <phase_6+0x59>(從第612行繼續執行)

21、根據前面的分析,13~20步的代碼,是根據處理后的num值(參見第10步分析),將相關信息壓棧(從esp+28開始壓棧):(注意:IA32是小端方式)

1)當num[i] == 1時,將0x804c174(node1)壓入到esp + 0x28 + i * 4

2)當num[i] == 2時,將0x804c180(node2)壓入到esp + 0x28 + i * 4

3)當num[i] == 3時,將0x804c18c(node3)壓入到esp + 0x28 + i * 4

4)當num[i] == 4時,將0x804c198(node4)壓入到esp + 0x28 + i * 4

5)當num[i] == 5時,將0x804c1a4(node5)壓入到esp + 0x28 + i * 4

6)當num[i] == 6時,將0x804c1b0(node6)壓入到esp + 0x28 + i * 4

7)觀察壓入棧的內容,每個內容地址實際上是指向12(例如:0x804c180-0x804c174字節的一段數據,該數據的末尾又是指向一個地址,因此,可以判斷0x804c174開始的地方指向的是一個鏈表(但這些鏈表的存空間是連續分配的),每個節點包括12個字節,其中最后一個是指向下一個的指針,猜測每個節點的定義:

struct node
{
    int d1;//尚不清楚含義,以4個字節的int暫替
    int d2;//尚不清楚含義,以4個字節的int暫替
    struct node* next;
}

6個節點,分別為:

node1 = {0x6d, 0x01, 0x804c180}; (&node1 = 0x804c174)

node2 = {0x69, 0x02, 0x804c18c }; (&node2 = 0x804c180)

node3 = {0x3b2, 0x03, 0x804c198}; (&node3 = 0x804c18c)

node4 = {0x299, 0x04, 0x804c1a4}; (&node4 = 0x804c198)

node5 = {0xc7, 0x05, 0x804c1b0}; (&node5 = 0x804c1a4)

node6 = {0x285b, 0x06, 0}; (&node6 = 0x804c1b0)

鏈接關系為:

node1   -->   node2   -->   node3   -->   node4   -->   node5   -->   node6   -->   0

 

8)假設當前6num的值為6/5/4/3/2/1,則經過6次循環后,函數棧幀如下圖所示。

 

 

注:后面分析,均假設6num的值為6/5/4/3/2/1

22、以上操作結束,則跳轉到 8048f0e <phase_6+0x8d>(第629行,參見第17步分析)。

236298048fb9 <phase_6+0xa7>- 676行:

1629行:0x28%esp)的內容(num[0]這個值指向的節點的地址) --> ebx

2630行:esp+0x2c  --> eaxesp+0x2c這個地址的內容為num[1]這個值對應的節點的地址

3631行:esp + 0x40 --> esiesp + 0x40,根據后面的分析,這個值是作為“哨兵”,防止訪問越界

4632行:ebx --> ecx(此時ebx以及ecx都是num[0]這個值指向的節點的地址)

5633行:eax所指向的地址的內容(num[1]這個值對應的節點的地址)--> edx

6634行:將edx的內容賦值給8%ecx)的地址,注意,此時ecxnum[0]指向的節點的地址,8%ecx)正好是num[0]這個值所對應的next,即node6.next = &node5

7635行:eax += 4,即eax所指向的地址的內容變成了num[2]所指向的節點的地址;

8636行:將eaxesi(哨兵)相比較,如果等於,則說明循環結束,跳轉到 8048f2c <phase_6+0xab>,如果不是,繼續執行。

9638行:edx --> ecx:根據前面分析,edxnum[1]值指向的節點的地址。

10639行:跳轉到8048f1c <phase_6+0x9b>,即第633行,可以轉到上面第5)步繼續執行,注意,此時edxnum[1]值指向的節點地址,eax的內容為num[2]值指向的節點的地址,即node5.next = &node4

11)如此循環,最后的結果是:

node6.next = &node5node5.next = &node4node4.next = &node3node3.next = &node2node2.next = &node1

12)如果以上都做完,跳轉到8048fd7 <phase_6+0xc5>677行)繼續。

13640行:將0賦值給8%edx)指向的地址,此時edxnode1的地址,即將node1.next=0

14)顯然,以上步驟,根據num的值重新構成了一個鏈表,此時的鏈接關系變成了:

node6   -->   node5   -->   node4   -->   node3   -->   node2   -->   node1   -->   0。(注:以上分析均是基於6num的值為6/5/4/3/2/1

24641 - 行:

1641行:5 --> esi

2642行:將ebx+8這個地址的內容送給eax,注意, ebxnode6的地址,ebx+8node6->next這個值的地址,這個地址的內容即為node5的地址。也即eax的內容為node5的地址。

3643行:將eax指向的地址的內容賦值為eax,也即eax的內容為node5.d1

4644行:將node5.d1ebx指向的地址的內容相比較;(顯然,此處是整數的比較,因此,也可以判斷struct node中第一個元素應該是int),此時ebx的內容為node6的地址,node6的地址的內容為node6.d1,即node5.d1node6.d1相比較。

5645-646行:如果node6.d1 >= node5.d1,則跳轉到8048f46 <phase_6+0xc5>,否則引爆炸彈

6647行:ebx的內容變為其指向的節點的next,即ebx=node6-next,指向了node5

7648行:esi-= 1

8649行:如果esi不為0,則跳轉到642行,按以上的2)繼續分析,應注意,此處ebx的值為node5的地址了。

9)顯然,此時會判斷node5.d1是否大於等於node4.d1,如果是,則繼續,如果不是,則引爆炸彈

10)后續會依次判斷node4.d1是否大於等於node3.d1node3.d1是否大於等於node2.d1......,綜合起來,就是判斷按照num值排序之后的節點是否降序排列,如果是,則解題成功,如果不是,則引爆炸彈。

1.3 phase_6結果分析

根據以上分析,phase_6的功能:

1phase_6定義了一個包含6個節點的鏈表,每個節點中包含兩個整型(d1d2),以及指向下一個節點的指針;6個節點依次的鏈接順序為node1->node2->node3->node4->node5->node6

2)要求用戶輸入6個數,這6個數應為1~6,而且不能重;為便於以后說明,假設這6個數為6/5/4/3/2/1。

3)按照num[i]的值重新排列鏈表,此時鏈表變為:

node6->node5->node4->node3->node2->node1

4)判斷以上鏈表是否降序排列(按分量d1),如果是,則拆彈成功,否則,引爆炸彈。

也即phase_6會給出一個鏈表,鏈表中的節點的d1分量含有一個整數值,需要用戶輸入一個序列號,按照這個順序重新排列鏈表中的節點,如果鏈表是按照降序排列,則輸入的這個序列號是正確的。

對於前面的炸彈,其初始化的節點值為:

node1 = {0x6d, 0x01, 0x804c180}; (&node1 = 0x804c174)

node2 = {0x69, 0x02, 0x804c18c }; (&node2 = 0x804c180)

node3 = {0x3b2, 0x03, 0x804c198}; (&node3 = 0x804c18c)

node4 = {0x299, 0x04, 0x804c1a4}; (&node4 = 0x804c198)

node5 = {0xc7, 0x05, 0x804c1b0}; (&node5 = 0x804c1a4)

node6 = {0x285b, 0x06, 0}; (&node6 = 0x804c1b0)

 

顯然,使得這個鏈表按降序排列的序列是:3 4 6 5 1 2,因此,輸入的序列號應為:3 4 6 5 1 2,此即為本關答案。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM