CSAPP--配套實驗(Bomblab)記錄
實驗材料
這是這學期上系統級程式設計課的實驗之一,是從CMU引入的,原始碼和資料可以 ofollow,noindex">CMU課程網站 獲得,直接選擇第二個實驗的Self-Study Handout下載即可。
做這個實驗需要反彙編和與除錯,建議使用gdb和objdump,如果還不會gdb 可以看看這個 簡易gdb使用指南 ,關於objdump 簡單看看這個就行了 畢竟做這個實驗我也只用了一個命令 objdump -d filename。
準備工作
下載的解壓包裡面就三個檔案,有用的也就是那個可執行檔案bomb,還有一個bomb.c可以讓你看清楚整個程式執行流程

bomp.c.png
這是main函式主要的部分,可以看到程式分為6個phase,每一個都需要你輸入一行字串,然後對應呼叫phase_n()函式進行判斷是否觸發炸彈
先用objdump -d bomb > bomb.asm 反彙編儲存到bomb.asm,然後用tmux開分屏,左邊是gdb除錯bomb

2.png
首先定位到main函式如下:
00000000000400da0 <main>: 400da0:53push%rbx 400da1:83 ff 01cmp$0x1,%edi 400da4:75 10jne400db6 <main+0x16> 400da6:48 8b 05 9b 29 20 00mov0x20299b(%rip),%rax# 603748 <stdin@@GLIBC_2.2.5> 400dad:48 89 05 b4 29 20 00mov%rax,0x2029b4(%rip)# 603768 <infile> 400db4:eb 63jmp400e19 <main+0x79> 400db6:48 89 f3mov%rsi,%rbx 400db9:83 ff 02cmp$0x2,%edi 400dbc:75 3ajne400df8 <main+0x58> 400dbe:48 8b 7e 08mov0x8(%rsi),%rdi 400dc2:be b4 22 40 00mov$0x4022b4,%esi 400dc7:e8 44 fe ff ffcallq400c10 <fopen@plt> 400dcc:48 89 05 95 29 20 00mov%rax,0x202995(%rip)# 603768 <infile> 400dd3:48 85 c0test%rax,%rax 400dd6:75 41jne400e19 <main+0x79> 400dd8:48 8b 4b 08mov0x8(%rbx),%rcx 400ddc:48 8b 13mov(%rbx),%rdx 400ddf:be b6 22 40 00mov$0x4022b6,%esi 400de4:bf 01 00 00 00mov$0x1,%edi 400de9:e8 12 fe ff ffcallq400c00 <__printf_chk@plt> 400dee:bf 08 00 00 00mov$0x8,%edi 400df3:e8 28 fe ff ffcallq400c20 <exit@plt> 400df8:48 8b 16mov(%rsi),%rdx 400dfb:be d3 22 40 00mov$0x4022d3,%esi 400e00:bf 01 00 00 00mov$0x1,%edi 400e05:b8 00 00 00 00mov$0x0,%eax 400e0a:e8 f1 fd ff ffcallq400c00 <__printf_chk@plt> 400e0f:bf 08 00 00 00mov$0x8,%edi 400e14:e8 07 fe ff ffcallq400c20 <exit@plt> 400e19:e8 84 05 00 00callq4013a2 <initialize_bomb> 400e1e:bf 38 23 40 00mov$0x402338,%edi 400e23:e8 e8 fc ff ffcallq400b10 <puts@plt> 400e28:bf 78 23 40 00mov$0x402378,%edi 400e2d:e8 de fc ff ffcallq400b10 <puts@plt> 400e32:e8 67 06 00 00callq40149e <read_line> 400e37:48 89 c7mov%rax,%rdi 400e3a:e8 a1 00 00 00callq400ee0 <phase_1> 400e3f:e8 80 07 00 00callq4015c4 <phase_defused> 400e44:bf a8 23 40 00mov$0x4023a8,%edi 400e49:e8 c2 fc ff ffcallq400b10 <puts@plt> 400e4e:e8 4b 06 00 00callq40149e <read_line> 400e53:48 89 c7mov%rax,%rdi 400e56:e8 a1 00 00 00callq400efc <phase_2> 400e5b:e8 64 07 00 00callq4015c4 <phase_defused> 400e60:bf ed 22 40 00mov$0x4022ed,%edi 400e65:e8 a6 fc ff ffcallq400b10 <puts@plt> 400e6a:e8 2f 06 00 00callq40149e <read_line> 400e6f:48 89 c7mov%rax,%rdi 400e72:e8 cc 00 00 00callq400f43 <phase_3> 400e77:e8 48 07 00 00callq4015c4 <phase_defused> 400e7c:bf 0b 23 40 00mov$0x40230b,%edi 400e81:e8 8a fc ff ffcallq400b10 <puts@plt> 400e86:e8 13 06 00 00callq40149e <read_line> 400e8b:48 89 c7mov%rax,%rdi 400e8e:e8 79 01 00 00callq40100c <phase_4> 400e93:e8 2c 07 00 00callq4015c4 <phase_defused> 400e98:bf d8 23 40 00mov$0x4023d8,%edi 400e9d:e8 6e fc ff ffcallq400b10 <puts@plt> 400ea2:e8 f7 05 00 00callq40149e <read_line> 400ea7:48 89 c7mov%rax,%rdi 400eaa:e8 b3 01 00 00callq401062 <phase_5> 400eaf:e8 10 07 00 00callq4015c4 <phase_defused> 400eb4:bf 1a 23 40 00mov$0x40231a,%edi 400eb9:e8 52 fc ff ffcallq400b10 <puts@plt> 400ebe:e8 db 05 00 00callq40149e <read_line> 400ec3:48 89 c7mov%rax,%rdi 400ec6:e8 29 02 00 00callq4010f4 <phase_6> 400ecb:e8 f4 06 00 00callq4015c4 <phase_defused> 400ed0:b8 00 00 00 00mov$0x0,%eax 400ed5:5bpop%rbx
和我們在bomb.c中看到的是一樣的,main函式內每次先呼叫read_line,然後將返回的地址傳遞給phase_n函式,如果輸入的不正確那麼就會執行爆炸函式。所以當然就順著main函式執行軌跡一個個來排雷~
Phase_1
先檢視phase_1反彙編程式碼:
0000000000400ee0 <phase_1>: 400ee0:48 83 ec 08sub$0x8,%rsp 400ee4:be 00 24 40 00mov$0x402400,%esi 400ee9:e8 4a 04 00 00callq401338 <strings_not_equal> 400eee:85 c0test%eax,%eax 400ef0:74 05je400ef7 <phase_1+0x17> 400ef2:e8 43 05 00 00callq40143a <explode_bomb> 400ef7:48 83 c4 08add$0x8,%rsp 400efb:c3retq
phase_1彙編程式碼非常簡潔,在這之前首先說明一下
read_line函式會將讀入字串地址存放在rdi 和rsi中,strings_not_equal函式會使用edi和esi中的值當做兩個字元址,並且判斷他們是否相等,相等返回0
再看phase_1函式首先將0x402400這個賦值給esi,然後呼叫strings_not_equal,剛才分析了,在每次呼叫phase_n之前都會先呼叫read_line讀入一行並且放在edi和esi。顯然這裡是呼叫字串比較函式比較我們輸入的字串和存放在0x402400地址的字串是否相等,緊接著呼叫test指令,如果eax為0也就是兩個字串相等就跳轉到函式結尾,否則呼叫explode_bomb函式,這個就是引爆炸彈的函式。到這裡答案也就出來了,我們需要輸入的就是存放在0x402400處的字串。接下來用gdb開始除錯
(gdb) bphase_1;打斷點 (gdb) run;執行到下一個斷點 (gdb) info r ;檢視暫存器值 (gdb) print (char*)(0x402400) ;檢視記憶體中字串

3.png
通過上面除錯視窗可以看到($edi)處存放的正是我輸入的hello ,而地址0x402400處的"Border relations with Canada have never been better."正是答案。接著重新開啟除錯視窗輸入這個字串,通過phase_1。
可以把之前解出來的答案寫到一個檔案裡,每個答案一行,然後開始除錯時設定下命令列引數
set args xixi(這裡是你的答案檔名)即可後續直接輸入已經解出的答案
Phase_2
還是先看看彙編程式碼,這個函式要長不少,而且中間多了很多條件跳轉指令,很不利於理解程式碼作用,我一般喜歡在分支處標明
0000000000400efc <phase_2>: 400efc:55push%rbp 400efd:53push%rbx 400efe:48 83 ec 28sub$0x28,%rsp 400f02:48 89 e6mov%rsp,%rsi 400f05:e8 52 05 00 00callq40145c <read_six_numbers> ;讀入六個數,第一個存在($rsp)處 400f0a:83 3c 24 01cmpl$0x1,(%rsp) ;第一個數和1比較 400f0e:74 20je400f30 <phase_2+0x34>;等於1跳轉 400f10:e8 25 05 00 00callq40143a <explode_bomb>;否則爆炸 400f15:eb 19jmp400f30 <phase_2+0x34> 400f17:8b 43 fcmov-0x4(%rbx),%eax;取出rbx-4處的值賦給eax 400f1a:01 c0add%eax,%eax; eax = eax *2 400f1c:39 03cmp%eax,(%rbx) ;比較eax*2和rbx處的值,注意:eax是ebx-4處的值,即將rbx和前一個數的兩倍比較 400f1e:74 05je400f25 <phase_2+0x29> ;如果相等就跳轉,而跳轉處的程式碼是將rbx+4 400f20:e8 15 05 00 00callq40143a <explode_bomb>;否則爆炸 400f25:48 83 c3 04add$0x4,%rbx; 將rbx+4 400f29:48 39 ebcmp%rbp,%rbx ;將加4後的值和rbp比較,注意rbp是rsp+24,而rsp是第一個數,一個數四個位元組。那麼rbp就應該是 後那個數後面那個地址,即rbp是個迴圈哨兵 400f2c:75 e9jne400f17 <phase_2+0x1b>;不等就繼續跳轉去迴圈 400f2e:eb 0cjmp400f3c <phase_2+0x40>; 相等就結束跳轉到函式結尾 400f30:48 8d 5c 24 04lea0x4(%rsp),%rbx;將rsp+4存到rbx 400f35:48 8d 6c 24 18lea0x18(%rsp),%rbp;將rsp +24 存到rbp 400f3a:eb dbjmp400f17 <phase_2+0x1b>;跳轉 400f3c:48 83 c4 28add$0x28,%rsp 400f40:5bpop%rbx 400f41:5dpop%rbp 400f42:c3retq
可以很明顯的看到呼叫了read_six_numbers,這個函式作用名字已經告訴我們了,只是有一點需要去看看它的程式碼才知道,它會把第一個數存在地址($rsp),以後依次遞增。這段程式碼註釋已經很清楚了,主體就是一個迴圈,而每一輪迴圈要做的就是判斷當前數和前一個數的兩倍是否相等,一旦不相等就爆炸。加上要求第一個數必須為1,那麼輸入的六個數就應該是1 2 4 8 16 32,用gdb除錯驗證

4.png
Secret_phase
這個不看反彙編程式碼根本不知道有這個雷存在,現在我們就來看看這個祕密炸彈
老規矩還是看反彙編