【深入理解計算機系統】CSAPP Bomb Lab實驗


零碎記事

  久違的,昨天打了一整天的游戲,玩的LOL,就只玩刀妹這個英雄,本人絕活。

  不得不說,刀妹這個英雄設計得太好玩了,可以說是將游戲中的博弈部分放大到了極致。這個容錯率極低的英雄,每一步都要操作謹慎,但高操作帶來的高收益,在我還沒出現失誤的時候,我的對手也在時刻警惕着我的動作。我Q上去,瞬間接近對方,他以為我要打了,趕緊回頭扔技能,我卻往回Q走跑了,又以為我不想打的時候,我又突然以兩個小兵作為跳板Q回來,接近拉E暈人幾乎一瞬間完成,還疊滿了被動,這時對方才知道大事不妙,然而已經跑不了了,兩段突進之下只能被我活活A死。這個英雄所帶來的的線上支配感,讓我深受喜歡。

  一個游戲為什么會好玩?很大一部分,是因為游戲的博弈在暗中會使人產生一種情感變化,知乎上有個游戲設計師就對游戲設計的情感變化這部分做過很精彩的講解:

  假設你正好在與一個陌生人下國際象棋,現在輪到你動子,而且看起來你要輸。由於想不出一步好棋,你會感覺有壓力,並且精神緊張。在分析過棋盤上的局勢之后,你更加緊張了。接下來卻峰回路轉,你發現了一招好棋——如果你向后方跳馬,不但可以保護自己的王,同時還可以威脅到對方!你緊綳的神經得到了極大的緩解,隨之帶來一絲成就感。你移動棋子,對方因為發現了你的意圖而面色沉重。看到對方的樣子,你會感覺自己有一種支配力,你的臉上露出了一絲得意。對方陷入了沉思,在你仍然感到志得意滿的時候,你發現了自己的一個薄弱環節。如果對方飛象過來,就能吃掉你自己的馬。這步棋比較隱蔽,對方會注意到嗎?你的滿足感逐漸被焦慮所取代。你努力讓自己保持神情自若,同時時間一點一點的流逝,這種焦慮在你的腦子里亂撞,你整個大腦都被這種焦慮的情感所淹沒,你的腎上腺素開始飆升,心跳加快。終於,對手只是移動了一個卒。你緊綳的神經再次得到了緩解,甚至比上次有過之而無不及。你感覺這一局已經穩操勝券了。

  從旁觀者的角度來看,這盤棋平淡無奇。兩個人面色凝重地坐在桌旁對弈,各自在棋盤上移動棋子,甚至連對於雙方都沒有意識到自己所經歷的這些情感。即便如此,他們從下棋的競技過程中感受到了過山車般的情感變化。並且之后,他們還將一次又一次經歷這種情感的變化。

可以說,玩家與游戲機制之間的互動是產生感情波動的來源,對於一個有吸引力的游戲來說,這些互動需要能夠激發人類熱血沸騰的情感。當這些時間真正激發出了自豪、歡樂、可怕或者恐怖等情感的時候,這個游戲才好玩。

  在優秀的游戲設計中,玩家不會注意到這種情感是由游戲機制本身產生的。比如你在《LOL》里使用龍龜打野,背后一群人在追着你打,但你一直開着反傷,最后追你的人成為了被你追的人。

  而劣質的游戲設計里,玩家在遭受挫折時,通常第一個想到的就是游戲機制的不公平,比如在《一刀999》游戲里,你開着和平模式在野外掛機,這時候一個氪金玩家突然出現把你擊殺,你最好的裝備掉在了地上被氪金玩家拾取,並分解。

  《LOL》里每個英雄的技能設計,都可以讓玩家在艾歐尼亞的戰斗中產生豐富的情感。當你用雪人在地上逐漸推起一個大雪球時;當你用瞎子站在敵方玩家看不到的地方,扔出一個Q將敵方玩家擊殺時;當你用瑞文的Q跨過一道牆時;當你殘血閃現,結果撞到牆時;當你用炸彈人的W成功逃生時;當你用寒冰千里之外命中控場時。這些設計產生的情感才是游戲存在的真正意義,這也是玩家願意花時間、精力和金錢在棋盤上移動棋子,或者把球投進籃筐里的真正原因。

 


 Bomb Lab

實驗說明

  這個實驗是依靠匯編代碼解開六道謎題也就是phase_1到phase_6,都是以函數形式給出,我們只需要看匯編代碼的函數部分,讓輸入數據進入函數后能繞過所有炸彈就行。

  個人感覺,其實只要耐心看下去,這個實驗誰都能解開所有謎題,唯一的難點就是剛開始對匯編和gdb一無所知的時候,幾乎都不知道這個實驗到底要自己干嘛。我建議,就算是打算自己完全獨立去解開這些炸彈謎題的,最好也是先看看phase_1和phase_2別人是怎么分析怎么解開的,熟悉下這個實驗到底是在干嘛后,自己再往后做,少做那兩道簡單的謎題,是完全不影響你的能力成長的。遵循這個思路,我在講解phase_1和phase_2的時候,會連同去講解gdb的一些基本操作和一些反匯編后看匯編代碼的經驗邏輯,方便大家先入個門,但往后面,我還是主要介紹我在解題過程中的思路,不再過於詳細地展開。


實驗前置准備

  解壓bomb.tar

  bomb的tar包解壓完后,我們會得到下面幾個文件

  

   其中bomb是我們的可執行程序,README我個人建議做實驗前先看看,bomb.c的話看不看都無所謂,看了也沒什么用的。

  我們的任務就是運行這個可執行程序,做六次輸入,每次都不能觸發炸彈。

  就拿第一次輸入作為例子,運行程序,會出現這么個東西。。。

  

   其實這個就是phase_1,你要輸入數據他才會繼續往下走,如果胡亂輸入東西按下回車,就會理所當然的觸發炸彈。。。

  

   如果我輸入了正確的數據。。。

  

  就會有提示phase_1已通過,並且要求你做下一個輸入,也就是phase_2的輸入,phase_2輸入不對,那么phase_2就會炸,后邊以此類推了,直到phase_6都是這樣。

  那么問題來了,我們怎么知道正確的輸入啊?

  這個就要反匯編,用匯編代碼去反推了,也就是這篇文章的重點。

  輸入objdump -d bomb > bomb.asm,可以得到bomb程序的反匯編代碼

  

   推薦傳到windows上拿Notepad++看看,直接vim看也是可以,但因為想方便后邊打注釋分析,遲早要拿到Notepad++上面的。

  打開匯編代碼看看

  

   嗯,沒問題。

  Linux上裝個gdb,不會裝的自己百度吧,一搜一大把。

  輸入gdb bomb,把程序裝載到調試器上方便調試

  

  簡單說明下要用到的gdb指令

  r    運行程序

  b <*0x某某某>    在某個地址設置斷點,具體哪里,可以看反匯編的代碼,可以根據那個直接復制粘貼設斷點的

  d 刪除所有斷點

  d <斷點號>    刪除指定斷點

  info b    查看所有斷點信息

  continue 從斷點處繼續執行

  display <$寄存器>    跟蹤寄存器,碰到斷點停下時會顯示出所有跟蹤的寄存器的當前值,非常好用的一個命令,注意的是gdb中表示寄存器的話前面用的不是百分符號%,而是美元符號$

  x/參數 <地址>    訪問地址的內存,其實就是間接訪問,也是很好用的指令,關於參數,s是輸出為字符串,d為輸出為十進制,x為輸出為十六進制,b、w、l、q控制輸出字節,默認是w,四字節,s字符串不受這個控制除外。

  info r    查看所有寄存器的值

  前置工作就這些了,接下來正式解題


phase_1

  看匯編代碼,直接拉到phase_1函數部分

  

 

  這就是個HelloWorld讓你入門的東西,看看我是怎么從這些代碼中分析得到正確的輸出的。

  首先要讓大家知道一個很關鍵的東西

  

   看到圈起來的那部分嗎?你的代碼要是運行了那行指令,那你就觸發炸彈涼涼了。所以你得繞過它,這個繞過是什么意思呢?別急,看看前面一行代碼。

  

   看到沒,上面一行有個跳轉指令,直接跳過這個炸彈的,當然,你得滿足這個跳轉條件才行,怎么滿足呢?結合第350行那個,我們可以知道只要%eax為0就滿足這個跳轉條件,怎么讓%eax為0呢?首先要知道%eax其實就是%rax的后32位,那%rax又是什么?前面有函數調用的話,一般%rax的值就是程序的返回值,實際上,我們確實看到它在前面調用了一個函數

  

   從函數的名字我們很容易猜出來這個函數是用來判斷字符串是否相等的,不等返回1,相等返回0,也就說,想讓%rax等於0,就等讓字符串相等,什么字符串相等?當然是我們輸入的串跟答案的串相等啊。可是我們又怎么才能知道答案的串是什么呢?先別急,至少現在我們明白了繞過炸彈的條件了。

  先看看上邊前面兩行

  

   上面一行是開棧操作,與之對應的是第353行的返回前棧指針回收,這個是每個函數首尾都會有的,表明了函數中開了多大的局部變量。看這個有什么用呢?我們輸入的可能就是輸入到函數里面的局部變量的,那這樣我們順着%rsp指針就能找到我們輸入的內容,嘛,這么說而已,其實這個phase里邊並不是這樣的,可以試給你們看看

  首先我在0x400ee9那設了個斷點,也就是callq  401338 <strings_not_equal>這一行,那么程序會在碰到這條匯編指令但還沒執行的時候停下來,這樣我就可以查看程序運行到那里時寄存器或者內存的一些信息。

  

   然后我開始跑我的程序,並隨便輸入了點東西,再往下,碰到斷點

  

   我們的目的是找出正確的字符串,%rsp開了8個字節的空間,這里很可能有放東西,比如我們的輸入之類的,先看看吧,輸入x/s $rsp,也就是以字符串形式查看棧頂的東西

   

  然而並不是,里面的東西我們啥也看不懂,什么用都沒有

  我們看看下一行

   

   要知道call的函數需要傳參的話,參數一般得實現准備好,然后這個%rdi往往就是放第一個參數的地方,其實這里很明顯%rsi就是作為參數傳進<strings_not_equal>的,我們可以大膽猜測這傳的是我們所輸入的字符串或者是正確的字符串,查它!。

  

  一查,果然有個字符串在里面,然而並不是我輸入的串,但是應該就是正確的字符串了,至於我輸入的串去哪了,我也不知道,但是到這為止至少我們可以確定輸入行為是在這個phase_1函數的外部發生的,現在先不管這些,先試試剛剛拿到的串對不對,對的話也不用進一步深究了。

  因為之前隨便輸入的串肯定不對,往下走肯定是callq  40143a <explode_bomb>被執行然后炸掉,就不continue了,直接r重新開始吧,這次我們輸入剛剛的的得到的疑似正確的字符串。

  

  嗯,看來還真是這個,phase_1就過了,反推就是這么一回事。


phase_2

  這是phase_2的函數的反匯編代碼,我們可以先看看,我是邊分析邊注釋的,所以有些自己加的東西,注意,當時到phase_2時我還不太懂匯編,注釋的東西都只是順着想法寫腦海中某一步的情況,你理解我的注釋不一定跟我實際注釋的意思一樣,而且突然知道怎么解后我就直接不注釋到下一關了,顯得很有跳躍性,不建議看這個注釋的。

  

 1 0000000000400efc <phase_2>:
 2   400efc:    55                       push   %rbp
 3   400efd:    53                       push   %rbx
 4   400efe:    48 83 ec 28              sub    $0x28,%rsp //開棧
 5   400f02:    48 89 e6                 mov    %rsp,%rsi //%rsi = %rsp
 6   400f05:    e8 52 05 00 00           callq  40145c <read_six_numbers>
 7   400f0a:    83 3c 24 01              cmpl   $0x1,(%rsp) //第一個數是1
 8   400f0e:    74 20                    je     400f30 <phase_2+0x34>
 9   400f10:    e8 25 05 00 00           callq  40143a <explode_bomb>
10   400f15:    eb 19                    jmp    400f30 <phase_2+0x34>
11   400f17:    8b 43 fc                 mov    -0x4(%rbx),%eax //%rax = %rbx - 4
12   400f1a:    01 c0                    add    %eax,%eax //%rax += %rax
13   400f1c:    39 03                    cmp    %eax,(%rbx) //第二個數在%rax里
14   400f1e:    74 05                    je     400f25 <phase_2+0x29>
15   400f20:    e8 15 05 00 00           callq  40143a <explode_bomb>
16   400f25:    48 83 c3 04              add    $0x4,%rbx %rbx += 4
17   400f29:    48 39 eb                 cmp    %rbp,%rbx
18   400f2c:    75 e9                    jne    400f17 <phase_2+0x1b>
19   400f2e:    eb 0c                    jmp    400f3c <phase_2+0x40>
20   400f30:    48 8d 5c 24 04           lea    0x4(%rsp),%rbx
21   400f35:    48 8d 6c 24 18           lea    0x18(%rsp),%rbp
22   400f3a:    eb db                    jmp    400f17 <phase_2+0x1b>
23   400f3c:    48 83 c4 28              add    $0x28,%rsp
24   400f40:    5b                       pop    %rbx
25   400f41:    5d                       pop    %rbp
26   400f42:    c3                       retq 

  看看第6行,有個callq  40145c <read_six_numbers>,可以推測phase_2是要我們輸入6個數,輸入的時候我們用空格隔開就行了

  看看第7行,發生了一個比較cmpl $0x1,(%rsp),結合下兩行的跳轉+bomb觸發,我們可以知道要不觸發第一個bomb,那么就得讓棧頂的東西,也就是(%rsp)的值等於1才行。首先我們看看棧頂專門存什么的,其實可以猜測我們輸入的數就是從棧頂開始放的,那么這樣一來(%rsp)就會是我們輸入的第一個數,驗證下這個想法先,在第八行設個斷點,我隨便輸入了六個數字,然后查看了下(%rsp)的值

  

  正如我所猜,輸入的數從棧頂開始放的,那這就好說了啊,滿足第8行的跳轉條件,我輸入的第一個數等於1就行。繼續往下分析。

  滿足條件后是一個jmp

  

  jmp后執行了兩個計算,分別是lea    0x4(%rsp),%rbx 和 0x18(%rsp),%rbp,再往后是到上面的jmp語句

 

   這個是無條件跳轉的,Nodepad++很方便,我們還發現了再往上有同樣跳轉目的的一個條件跳轉

   從有條件的跳轉入手,我們容易發現那里是個循環,這里我為了方便后續分析,就做了標記

   這種習慣是非常有利於我們去分析反匯編代碼的,分析循環,發現里面有個炸彈,我們得繞開它,我上邊因為當時還不是很懂匯編,所以有些注釋是有問題的,尤其是這循環里的注釋,你大可不管。反正分析知道,%rbx里的東西是我們輸入的數,而拿來比較的(%rbx),是他的正確答案,我們可以慢慢查看這個(%rbx),就是在炸彈前設斷點,獲得一個正確的數字就重新來重新輸入再看這樣,具體就不弄了,大家可以試試的,最后知道要輸入的內容就是1 2 4 8 16 32。

  

   OK,phase_2解掉


phase_3

  匯編代碼+注釋,從這個phase開始往后,這個注釋和思路是沒問題的,大家可以看看,有些地方可能有些跳躍性,可自行分析接上,不影響大體

  

 1 0000000000400f43 <phase_3>:
 2   400f43:    48 83 ec 18              sub    $0x18,%rsp //開棧
 3   400f47:    48 8d 4c 24 0c           lea    0xc(%rsp),%rcx //%rcx = (%rsp + 0xc)
 4   400f4c:    48 8d 54 24 08           lea    0x8(%rsp),%rdx //%rdx = (%rsp + 0x8)
 5   400f51:    be cf 25 40 00           mov    $0x4025cf,%esi //%rsi = 0x4025cf
 6   400f56:    b8 00 00 00 00           mov    $0x0,%eax //%rax = 0
 7   400f5b:    e8 90 fc ff ff           callq  400bf0 <__isoc99_sscanf@plt> //該函數返回值是輸入數字的個數
 8   400f60:    83 f8 01                 cmp    $0x1,%eax //進入函數后返回值要大於1,說明輸入要超過1個數字
 9   400f63:    7f 05                    jg     400f6a <phase_3+0x27>
10   400f65:    e8 d0 04 00 00           callq  40143a <explode_bomb>
11   400f6a:    83 7c 24 08 07           cmpl   $0x7,0x8(%rsp) //((%rsp + 0x8) > 7),此條件成立就引爆炸彈,所以輸入的第一個數要小於或等於7
12   400f6f:    77 3c                    ja     400fad <phase_3+0x6a> //跳到炸彈
13   400f71:    8b 44 24 08              mov    0x8(%rsp),%eax //%rax = 第一個數
14   400f75:    ff 24 c5 70 24 40 00     jmpq   *0x402470(,%rax,8) //jump到8*第一個數 + 0x402470
15   400f7c:    b8 cf 00 00 00           mov    $0xcf,%eax //%rax = 0xcf
16   400f81:    eb 3b                    jmp    400fbe <phase_3+0x7b>
17   400f83:    b8 c3 02 00 00           mov    $0x2c3,%eax //%rax = 0x2c3
18   400f88:    eb 34                    jmp    400fbe <phase_3+0x7b>
19   400f8a:    b8 00 01 00 00           mov    $0x100,%eax //%rax = 0x100
20   400f8f:    eb 2d                    jmp    400fbe <phase_3+0x7b>
21   400f91:    b8 85 01 00 00           mov    $0x185,%eax //%rax = 0x185
22   400f96:    eb 26                    jmp    400fbe <phase_3+0x7b>
23   400f98:    b8 ce 00 00 00           mov    $0xce,%eax //%rax = 0xce
24   400f9d:    eb 1f                    jmp    400fbe <phase_3+0x7b>
25   400f9f:    b8 aa 02 00 00           mov    $0x2aa,%eax //%rax = 0x2aa
26   400fa4:    eb 18                    jmp    400fbe <phase_3+0x7b>
27   400fa6:    b8 47 01 00 00           mov    $0x147,%eax //%rax = 0x147
28   400fab:    eb 11                    jmp    400fbe <phase_3+0x7b>
29   400fad:    e8 88 04 00 00           callq  40143a <explode_bomb>
30   400fb2:    b8 00 00 00 00           mov    $0x0,%eax //%rax = 0
31   400fb7:    eb 05                    jmp    400fbe <phase_3+0x7b>
32   400fb9:    b8 37 01 00 00           mov    $0x137,%eax //%rax = 0x137
33   400fbe:    3b 44 24 0c              cmp    0xc(%rsp),%eax //%rax不等於(%rsp + 12)就引爆炸彈,(%rsp + 12)是第二個數,試了下,第一個數等於3時,到這里rax = 256
34   400fc2:    74 05                    je     400fc9 <phase_3+0x86> //到達函數出口
35   400fc4:    e8 71 04 00 00           callq  40143a <explode_bomb>
36   400fc9:    48 83 c4 18              add    $0x18,%rsp
37   400fcd:    c3                       retq

answer:輸入超過1個數,第一個數要小於或等於7,最終測試得知,過關條件之一是 3 256


 

phase_4

 1 0000000000400fce <func4>:
 2   400fce:    48 83 ec 08              sub    $0x8,%rsp
 3   400fd2:    89 d0                    mov    %edx,%eax //%rax = 14
 4   400fd4:    29 f0                    sub    %esi,%eax //%rax = 14 - 0 = 14
 5   400fd6:    89 c1                    mov    %eax,%ecx //%rcx = 14
 6   400fd8:    c1 e9 1f                 shr    $0x1f,%ecx //%rcx = 0
 7   400fdb:    01 c8                    add    %ecx,%eax //%rax = 14
 8   400fdd:    d1 f8                    sar    %eax //%rax = 14 / 2 = 7
 9   400fdf:    8d 0c 30                 lea    (%rax,%rsi,1),%ecx //%rcx = 1 * 0 + 7 = 7
10   400fe2:    39 f9                    cmp    %edi,%ecx //第一個數小於等於7,就跳轉到%rax = 0
11   400fe4:    7e 0c                    jle    400ff2 <func4+0x24>
12   400fe6:    8d 51 ff                 lea    -0x1(%rcx),%edx
13   400fe9:    e8 e0 ff ff ff           callq  400fce <func4>
14   400fee:    01 c0                    add    %eax,%eax
15   400ff0:    eb 15                    jmp    401007 <func4+0x39>
16   400ff2:    b8 00 00 00 00           mov    $0x0,%eax //%rax = 0
17   400ff7:    39 f9                    cmp    %edi,%ecx //第一個數大於等於7,跳轉到函數尾巴,由此可知,第一個數等於7
18   400ff9:    7d 0c                    jge    401007 <func4+0x39>
19   400ffb:    8d 71 01                 lea    0x1(%rcx),%esi
20   400ffe:    e8 cb ff ff ff           callq  400fce <func4>
21   401003:    8d 44 00 01              lea    0x1(%rax,%rax,1),%eax
22   401007:    48 83 c4 08              add    $0x8,%rsp
23   40100b:    c3                       retq   
24 
25 000000000040100c <phase_4>:
26   40100c:    48 83 ec 18              sub    $0x18,%rsp
27   401010:    48 8d 4c 24 0c           lea    0xc(%rsp),%rcx 
28   401015:    48 8d 54 24 08           lea    0x8(%rsp),%rdx
29   40101a:    be cf 25 40 00           mov    $0x4025cf,%esi
30   40101f:    b8 00 00 00 00           mov    $0x0,%eax
31   401024:    e8 c7 fb ff ff           callq  400bf0 <__isoc99_sscanf@plt>
32   401029:    83 f8 02                 cmp    $0x2,%eax //從這個下一行可推斷要輸入兩個數
33   40102c:    75 07                    jne    401035 <phase_4+0x29>
34   40102e:    83 7c 24 08 0e           cmpl   $0xe,0x8(%rsp) //第一個數要小於等於14
35   401033:    76 05                    jbe    40103a <phase_4+0x2e>
36   401035:    e8 00 04 00 00           callq  40143a <explode_bomb>
37   40103a:    ba 0e 00 00 00           mov    $0xe,%edx //%rdx = 14
38   40103f:    be 00 00 00 00           mov    $0x0,%esi //%rsi = 0
39   401044:    8b 7c 24 08              mov    0x8(%rsp),%edi //%rdi = 第一個數
40   401048:    e8 81 ff ff ff           callq  400fce <func4> //進入一個函數
41   40104d:    85 c0                    test   %eax,%eax
42   40104f:    75 07                    jne    401058 <phase_4+0x4c> //函數返回值不等於0就引爆炸彈
43   401051:    83 7c 24 0c 00           cmpl   $0x0,0xc(%rsp) //第二個數等於0才行
44   401056:    74 05                    je     40105d <phase_4+0x51>
45   401058:    e8 dd 03 00 00           callq  40143a <explode_bomb>
46   40105d:    48 83 c4 18              add    $0x18,%rsp
47   401061:    c3                       retq   

answer:直接看匯編,甚至不用gdb都能推出來,答案是7 0


 

phase_5

 1 0000000000401062 <phase_5>:
 2   401062:    53                       push   %rbx //%rbx是保存的字符串
 3   401063:    48 83 ec 20              sub    $0x20,%rsp
 4   401067:    48 89 fb                 mov    %rdi,%rbx
 5   40106a:    64 48 8b 04 25 28 00     mov    %fs:0x28,%rax
 6   401071:    00 00 
 7   401073:    48 89 44 24 18           mov    %rax,0x18(%rsp)
 8   401078:    31 c0                    xor    %eax,%eax //%rax清0
 9   40107a:    e8 9c 02 00 00           callq  40131b <string_length> //計算輸入的字符串長度
10   40107f:    83 f8 06                 cmp    $0x6,%eax //字符串長度等於6才合法
11   401082:    74 4e                    je     4010d2 <phase_5+0x70>
12   401084:    e8 b1 03 00 00           callq  40143a <explode_bomb>
13   401089:    eb 47                    jmp    4010d2 <phase_5+0x70>
14   
15   Loop:for(%rax=0; %rax!=6 ;%rax++)
16   40108b:    0f b6 0c 03              movzbl (%rbx,%rax,1),%ecx //%rcx = %rax + %rbx。其實這里是%rcx從第一個字符遍歷到第六個字符
17   40108f:    88 0c 24                 mov    %cl,(%rsp) //(%rsp) = %rcx的后8位
18   401092:    48 8b 14 24              mov    (%rsp),%rdx //%rdx = %rcx的后8位
19   401096:    83 e2 0f                 and    $0xf,%edx //%rdx = %rcx的后4位
20   401099:    0f b6 92 b0 24 40 00     movzbl 0x4024b0(%rdx),%edx //%rdx = (%rdx + 0x4024b0) //0x4024b0的前16位是"maduiersnfotvbyl"
21   4010a0:    88 54 04 10              mov    %dl,0x10(%rsp,%rax,1) //(%rsp + %rax + 16) = %rdx的最后四位。這里推出我們輸入字符的ASCII碼會的后四位會對應上上邊字符串的字符
22   4010a4:    48 83 c0 01              add    $0x1,%rax //%rax += 1
23   4010a8:    48 83 f8 06              cmp    $0x6,%rax //%rax != 6就繼續回到上邊的循環
24   4010ac:    75 dd                    jne    40108b <phase_5+0x29>
25   Loop End
26   4010ae:    c6 44 24 16 00           movb   $0x0,0x16(%rsp) //(%rsp + 0x16) = 0。實際上就是給字符串加結束符
27   4010b3:    be 5e 24 40 00           mov    $0x40245e,%esi //%rsi = 0x40245e。是字符串"flyers",可推出我們輸入的字符串的每個字符的ASCII碼的后四位應分別為9、F、E、567 ==> 符合條件的字符串之一是"9ON567"
28   4010b8:    48 8d 7c 24 10           lea    0x10(%rsp),%rdi //%rdi = (%rsp + 0x10)
29   4010bd:    e8 76 02 00 00           callq  401338 <strings_not_equal> //這里有個字符串檢測,設斷點看看前面的值
30   4010c2:    85 c0                    test   %eax,%eax
31   4010c4:    74 13                    je     4010d9 <phase_5+0x77>
32   4010c6:    e8 6f 03 00 00           callq  40143a <explode_bomb>
33   4010cb:    0f 1f 44 00 00           nopl   0x0(%rax,%rax,1)
34   4010d0:    eb 07                    jmp    4010d9 <phase_5+0x77>
35   4010d2:    b8 00 00 00 00           mov    $0x0,%eax //%rax = 0
36   4010d7:    eb b2                    jmp    40108b <phase_5+0x29>
37   4010d9:    48 8b 44 24 18           mov    0x18(%rsp),%rax
38   4010de:    64 48 33 04 25 28 00     xor    %fs:0x28,%rax
39   4010e5:    00 00 
40   4010e7:    74 05                    je     4010ee <phase_5+0x8c>
41   4010e9:    e8 42 fa ff ff           callq  400b30 <__stack_chk_fail@plt>
42   4010ee:    48 83 c4 20              add    $0x20,%rsp
43   4010f2:    5b                       pop    %rbx
44   4010f3:    c3                       retq   

answer:在貼出代碼的第20行和第26行結合起來看,注釋已經充分說明了答案,輸入的字符串符合這個規律“輸入的字符串的每個字符的ASCII碼的后四位應分別為9、F、E、5、6、7”就行,比如符合條件之一的字符串是"9ON567"


phase_6

  這是最難的一關,也是我注釋得最詳細的一關

00000000004010f4 <phase_6>:
  4010f4:    41 56                    push   %r14
  4010f6:    41 55                    push   %r13
  4010f8:    41 54                    push   %r12
  4010fa:    55                       push   %rbp
  4010fb:    53                       push   %rbx
  4010fc:    48 83 ec 50              sub    $0x50,%rsp
  401100:    49 89 e5                 mov    %rsp,%r13
  401103:    48 89 e6                 mov    %rsp,%rsi
  401106:    e8 51 03 00 00           callq  40145c <read_six_numbers>
  40110b:    49 89 e6                 mov    %rsp,%r14 //機子上測試了下,輸入的是6個4字節整數,開始地址是%rsp
  40110e:    41 bc 00 00 00 00        mov    $0x0,%r12d //%r12d = 0
  Loop 1:
  401114:    4c 89 ed                 mov    %r13,%rbp //這里跟%r13同值
  401117:    41 8b 45 00              mov    0x0(%r13),%eax //把輸入的整數拿進%rax,不出意外應該是遍歷操作
  40111b:    83 e8 01                 sub    $0x1,%eax //%rax -= 1
  40111e:    83 f8 05                 cmp    $0x5,%eax //%rax <= 5 ==>輸入的數要小於等於6,因為無符號數,上面又-1,所以還要大於等於1
  401121:    76 05                    jbe    401128 <phase_6+0x34>
  401123:    e8 12 03 00 00           callq  40143a <explode_bomb>
  401128:    41 83 c4 01              add    $0x1,%r12d
  40112c:    41 83 fc 06              cmp    $0x6,%r12d //結合上下文,其實已經可以推出%r12d是程序計數變量了
  401130:    74 21                    je     401153 <phase_6+0x5f>
  401132:    44 89 e3                 mov    %r12d,%ebx
  Loop 2:
  401135:    48 63 c3                 movslq %ebx,%rax
  401138:    8b 04 84                 mov    (%rsp,%rax,4),%eax //程序計數變量作為數組索引,是遍歷操作
  40113b:    39 45 00                 cmp    %eax,0x0(%rbp) //實際上是在把當前遍歷到的數與后面的每個數作比較
  40113e:    75 05                    jne    401145 <phase_6+0x51> //可推出不能輸入兩個相同的數
  401140:    e8 f5 02 00 00           callq  40143a <explode_bomb>
  401145:    83 c3 01                 add    $0x1,%ebx
  401148:    83 fb 05                 cmp    $0x5,%ebx
  40114b:    7e e8                    jle    401135 <phase_6+0x41>
  Loop 2 End
  40114d:    49 83 c5 04              add    $0x4,%r13
  401151:    eb c1                    jmp    401114 <phase_6+0x20>
  Loop 1 End
  分析循環收獲:一、輸入的數要小於等於6並且大於等於1 二、不能輸入兩個相同的數
  401153:    48 8d 74 24 18           lea    0x18(%rsp),%rsi
  401158:    4c 89 f0                 mov    %r14,%rax //是輸入的第一個數字
  40115b:    b9 07 00 00 00           mov    $0x7,%ecx //%rcx = 7
  Loop:
  401160:    89 ca                    mov    %ecx,%edx //看循環后面%rcx的值就沒動過,可認為這里恆有%rdx=7
  401162:    2b 10                    sub    (%rax),%edx //所以是7減去每個數
  401164:    89 10                    mov    %edx,(%rax) //因為是內存引用,這里改變了數組實際的值,這個循環過后,數組里的值變成7-輸入的數
  401166:    48 83 c0 04              add    $0x4,%rax //實際上還是在遍歷那六個數
  40116a:    48 39 f0                 cmp    %rsi,%rax
  40116d:    75 f1                    jne    401160 <phase_6+0x6c>
  Loop End
  分析循環收獲:數組的值被變成7-每個輸入的數
  40116f:    be 00 00 00 00           mov    $0x0,%esi
  401174:    eb 21                    jmp    401197 <phase_6+0xa3>
  Loop 1:Loop 2:
  401176:    48 8b 52 08              mov    0x8(%rdx),%rdx //這里應該是個鏈表
  40117a:    83 c0 01                 add    $0x1,%eax
  40117d:    39 c8                    cmp    %ecx,%eax //%rax初始值是1,這里可看成是循環條件為%rax<7-input[i] ==>%rdx的值為node[7-input[i]]的地址(鏈式順序)
  40117f:    75 f5                    jne    401176 <phase_6+0x82>
  Loop 2 End
  401181:    eb 05                    jmp    401188 <phase_6+0x94>
  Loop 3:
  401183:    ba d0 32 60 00           mov    $0x6032d0,%edx
  401188:    48 89 54 74 20           mov    %rdx,0x20(%rsp,%rsi,2) //棧中存入%rdx的值,也就是node的地址,整個循環的目的就在於這里
  40118d:    48 83 c6 04              add    $0x4,%rsi
  401191:    48 83 fe 18              cmp    $0x18,%rsi //%rsi是計數變量,看看下行跳出大循環的匯編代碼,估計又是遍歷了。。。
  401195:    74 14                    je     4011ab <phase_6+0xb7>
  401197:    8b 0c 34                 mov    (%rsp,%rsi,1),%ecx //7減過后的輸入數字
  40119a:    83 f9 01                 cmp    $0x1,%ecx
  40119d:    7e e4                    jle    401183 <phase_6+0x8f> //小於等於1就回Loop3,意思是某個輸入數字等於6就回Loop3
  Loop 3 End
  40119f:    b8 01 00 00 00           mov    $0x1,%eax //%rax要做計數變量了
  4011a4:    ba d0 32 60 00           mov    $0x6032d0,%edx //得看看這個地址裝的什么,看到是node1~6,是鏈表結點,通過查看數據,其鏈式data為{332, 168, 924, 691, 477, 443},對應node{1, 2, 3, 4, 5, 6}
  4011a9:    eb cb                    jmp    401176 <phase_6+0x82>
  Loop 1 End
  分析循環收獲:%rsp+0x20存了6個結點,分別為node[7-input[i]]
  4011ab:    48 8b 5c 24 20           mov    0x20(%rsp),%rbx //%rbx = node[7-input[1]]的地址
  4011b0:    48 8d 44 24 28           lea    0x28(%rsp),%rax //%rax = node棧[2]
  4011b5:    48 8d 74 24 50           lea    0x50(%rsp),%rsi //%rsi = node棧[6]
  4011ba:    48 89 d9                 mov    %rbx,%rcx //%rcx = node[7-input[1]]的地址
  Loop:
  4011bd:    48 8b 10                 mov    (%rax),%rdx //%rdx = node[7-input[i]]
  4011c0:    48 89 51 08              mov    %rdx,0x8(%rcx)
  4011c4:    48 83 c0 08              add    $0x8,%rax
  4011c8:    48 39 f0                 cmp    %rsi,%rax //%rax == node[7-input[6]]是循環退出條件,結合上邊有地址偏移8取next,下邊又有next賦值之類的,容易猜到這個是在重構鏈表
  4011cb:    74 05                    je     4011d2 <phase_6+0xde>
  4011cd:    48 89 d1                 mov    %rdx,%rcx
  4011d0:    eb eb                    jmp    4011bd <phase_6+0xc9>
  Loop End
  循環分析收獲:棧中的node重構成了鏈
  4011d2:    48 c7 42 08 00 00 00     movq   $0x0,0x8(%rdx) //node[7-input[6]].next = NULL
  4011d9:    00 
  4011da:    bd 05 00 00 00           mov    $0x5,%ebp
  Loop:
  4011df:    48 8b 43 08              mov    0x8(%rbx),%rax
  4011e3:    8b 00                    mov    (%rax),%eax
  4011e5:    39 03                    cmp    %eax,(%rbx)
  4011e7:    7d 05                    jge    4011ee <phase_6+0xfa> //結合上邊三條語句和這個循環,其實意思是:node[7-input[i]]->data >= node[7-input[i+1]]->data
  4011e9:    e8 4c 02 00 00           callq  40143a <explode_bomb>
  4011ee:    48 8b 5b 08              mov    0x8(%rbx),%rbx
  4011f2:    83 ed 01                 sub    $0x1,%ebp //這里看出%ebp是計數變量,循環實際上是for(%rbp=5; %rbp!=0 ;%rbp--)
  4011f5:    75 e8                    jne    4011df <phase_6+0xeb>
  Loop End
  循環分析收獲:node[7-input[i]]->data >= node[7-input[i+1]]->data
  至此謎題已經解開
  有6個node,通過查看數據,其鏈式data為{332, 168, 924, 691, 477, 443},對應node{1, 2, 3, 4, 5, 6}
  7減去我們的輸入所獲得的node的data要符合其降序
  不考慮7減去時,輸入為3, 4, 5, 6, 1, 2
  考慮7減去時,輸入為4, 3, 2, 1, 6, 5。其實這個就是正確的輸入
  4011f7:    48 83 c4 50              add    $0x50,%rsp
  4011fb:    5b                       pop    %rbx
  4011fc:    5d                       pop    %rbp
  4011fd:    41 5c                    pop    %r12
  4011ff:    41 5d                    pop    %r13
  401201:    41 5e                    pop    %r14
  401203:    c3                       retq   

answer:其實就是我最后注釋的內容

有6個node,通過查看數據,其鏈式data為{332, 168, 924, 691, 477, 443},對應node{1, 2, 3, 4, 5, 6}

7減去我們的輸入所獲得的node的data要符合其降序

不考慮7減去時,輸入為3, 4, 5, 6, 1, 2

考慮7減去時,輸入為4, 3, 2, 1, 6, 5。其實這個就是正確的輸入

 


免責聲明!

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



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