Lab 采用的是 coursera 上華盛頓大學的The Hardware/Software Interface. 與CSAPP書上的不同,這里的lab采用的是64位機器。
Lab2 要求拆除6個炸彈加一個secret bomb。
phase_1
0000000000400e70 <phase_1>: 400e70: 48 83 ec 08 sub $0x8,%rsp 400e74: be f8 1a 40 00 mov $0x401af8,%esi 400e79: e8 bf 03 00 00 callq 40123d <strings_not_equal> 400e7e: 85 c0 test %eax,%eax 400e80: 74 05 je 400e87 <phase_1+0x17> 400e82: e8 b6 07 00 00 callq 40163d <explode_bomb> 400e87: 48 83 c4 08 add $0x8,%rsp 400e8b: c3 retq
000000000040123d <strings_not_equal>: 40123d: 48 89 5c 24 e8 mov %rbx,-0x18(%rsp) 401242: 48 89 6c 24 f0 mov %rbp,-0x10(%rsp) 401247: 4c 89 64 24 f8 mov %r12,-0x8(%rsp) 40124c: 48 83 ec 18 sub $0x18,%rsp 401250: 48 89 fb mov %rdi,%rbx 401253: 48 89 f5 mov %rsi,%rbp 401256: e8 c6 ff ff ff callq 401221 <string_length> 40125b: 41 89 c4 mov %eax,%r12d 40125e: 48 89 ef mov %rbp,%rdi 401261: e8 bb ff ff ff callq 401221 <string_length> 401266: ba 01 00 00 00 mov $0x1,%edx 40126b: 41 39 c4 cmp %eax,%r12d 40126e: 75 36 jne 4012a6 <strings_not_equal+0x69>
0000000000401221 <string_length>: 401221: b8 00 00 00 00 mov $0x0,%eax 401226: 80 3f 00 cmpb $0x0,(%rdi) 401229: 74 10 je 40123b <string_length+0x1a> 40122b: 48 89 fa mov %rdi,%rdx 40122e: 48 83 c2 01 add $0x1,%rdx 401232: 89 d0 mov %edx,%eax 401234: 29 f8 sub %edi,%eax 401236: 80 3a 00 cmpb $0x0,(%rdx) 401239: 75 f3 jne 40122e <string_length+0xd> 40123b: f3 c3 repz retq
string_length 中的代碼比較好懂,是以%rdi中的內容為地址,然后與0比較(0即是字符串的終結符號)。所以字符串起始地址應該藏在%rdi中!再看 strings_not_equal,分別有兩次 string_length 調用,再結合函數名 strings_not_equal,可以想見是我們輸入的字符串與藏在程序中的字符串進行比較!所以只要在 401256,401261兩處設置斷點,打印出以%rdi中內容為起始地址的字符串。其中一個是我們輸入的字符串,而另一個就是我們尋找的字符串。
phase_2
0000000000400e8c <phase_2>: 400e8c: 48 89 5c 24 e0 mov %rbx,-0x20(%rsp) 400e91: 48 89 6c 24 e8 mov %rbp,-0x18(%rsp) 400e96: 4c 89 64 24 f0 mov %r12,-0x10(%rsp) 400e9b: 4c 89 6c 24 f8 mov %r13,-0x8(%rsp) 400ea0: 48 83 ec 48 sub $0x48,%rsp 400ea4: 48 89 e6 mov %rsp,%rsi 400ea7: e8 97 08 00 00 callq 401743 <read_six_numbers> 400eac: 48 89 e5 mov %rsp,%rbp 400eaf: 4c 8d 6c 24 0c lea 0xc(%rsp),%r13 400eb4: 41 bc 00 00 00 00 mov $0x0,%r12d 400eba: 48 89 eb mov %rbp,%rbx 400ebd: 8b 45 0c mov 0xc(%rbp),%eax 400ec0: 39 45 00 cmp %eax,0x0(%rbp) 400ec3: 74 05 je 400eca <phase_2+0x3e> 400ec5: e8 73 07 00 00 callq 40163d <explode_bomb> 400eca: 44 03 23 add (%rbx),%r12d 400ecd: 48 83 c5 04 add $0x4,%rbp 400ed1: 4c 39 ed cmp %r13,%rbp 400ed4: 75 e4 jne 400eba <phase_2+0x2e> 400ed6: 45 85 e4 test %r12d,%r12d 400ed9: 75 05 jne 400ee0 <phase_2+0x54> 400edb: e8 5d 07 00 00 callq 40163d <explode_bomb> 400ee0: 48 8b 5c 24 28 mov 0x28(%rsp),%rbx 400ee5: 48 8b 6c 24 30 mov 0x30(%rsp),%rbp 400eea: 4c 8b 64 24 38 mov 0x38(%rsp),%r12 400eef: 4c 8b 6c 24 40 mov 0x40(%rsp),%r13 400ef4: 48 83 c4 48 add $0x48,%rsp 400ef8: c3 retq
phase_2 會調用 read_six_numbers,所以我們要輸入的應該是6個數字。
這段匯編代碼比較好懂,是讓我們比較 0x0(%rbp) 與 0xc(%rbp) ,0x4(%rbp) 與 0x10(%rbp),0x8(%rbp) 與 0x14(%rbp)的內容,一旦有一個不符就引爆炸彈,同時還會檢測 0x0(%rbp) + 0x4(%rbp) + 0x8(%rbp) 的和是否為0,為0則引爆炸彈。
那么上面這些與我們輸入的數字又有什么關系呢?只要在gdb中打印出來看看就行了。
啊哈,表示的就是我們輸入進去的數字嘛。所以我們只要保證后三位與前三位相同同時前三位之和不為0就ok了。 比如 1 2 3 1 2 3
phase_3
0000000000400ef9 <phase_3>: 400ef9: 48 83 ec 18 sub $0x18,%rsp 400efd: 48 8d 4c 24 08 lea 0x8(%rsp),%rcx 400f02: 48 8d 54 24 0c lea 0xc(%rsp),%rdx 400f07: be be 1e 40 00 mov $0x401ebe,%esi 400f0c: b8 00 00 00 00 mov $0x0,%eax 400f11: e8 9a fb ff ff callq 400ab0 <__isoc99_sscanf@plt> 400f16: 83 f8 01 cmp $0x1,%eax 400f19: 7f 05 jg 400f20 <phase_3+0x27> 400f1b: e8 1d 07 00 00 callq 40163d <explode_bomb> 400f20: 83 7c 24 0c 07 cmpl $0x7,0xc(%rsp) 400f25: 77 3c ja 400f63 <phase_3+0x6a> 400f27: 8b 44 24 0c mov 0xc(%rsp),%eax 400f2b: ff 24 c5 60 1b 40 00 jmpq *0x401b60(,%rax,8) 400f32: b8 17 02 00 00 mov $0x217,%eax 400f37: eb 3b jmp 400f74 <phase_3+0x7b> 400f39: b8 d6 00 00 00 mov $0xd6,%eax 400f3e: eb 34 jmp 400f74 <phase_3+0x7b> 400f40: b8 53 01 00 00 mov $0x153,%eax 400f45: eb 2d jmp 400f74 <phase_3+0x7b> 400f47: b8 77 00 00 00 mov $0x77,%eax 400f4c: eb 26 jmp 400f74 <phase_3+0x7b> 400f4e: b8 60 01 00 00 mov $0x160,%eax 400f53: eb 1f jmp 400f74 <phase_3+0x7b> 400f55: b8 97 03 00 00 mov $0x397,%eax 400f5a: eb 18 jmp 400f74 <phase_3+0x7b> 400f5c: b8 9c 01 00 00 mov $0x19c,%eax 400f61: eb 11 jmp 400f74 <phase_3+0x7b> 400f63: e8 d5 06 00 00 callq 40163d <explode_bomb> 400f68: b8 00 00 00 00 mov $0x0,%eax 400f6d: eb 05 jmp 400f74 <phase_3+0x7b> 400f6f: b8 9e 03 00 00 mov $0x39e,%eax 400f74: 3b 44 24 08 cmp 0x8(%rsp),%eax 400f78: 74 05 je 400f7f <phase_3+0x86> 400f7a: e8 be 06 00 00 callq 40163d <explode_bomb> 400f7f: 48 83 c4 18 add $0x18,%rsp 400f83: c3 retq
400ab0 <__isoc99_sscanf@plt> 指明了 sscanf 是標准庫函數,經查文檔,得到該函數的 signature 為 int sscanf(const char *buffer,const char *format,[argument ]...);
與gets函數一樣都要有buffer,結合前面將 0x8(%rsp),0xc(%rsp) 的地址壓入寄存器,以及后面會取出0x8(%rsp),0xc(%rsp)進行其他操作,所以猜測應該是輸入的內容放入棧中這兩個位置(作為buffer)。而且該函數的返回值(返回值為參數數目)要求大於1才不會爆炸,所以結合buffer的大小為2輸入的應該是兩個字符。%esi 對應的是參數 const char *forma,經打印為
更加確定輸入的是兩個字符,而且是兩個數字。
后面部分就很簡單,要求輸入的第一個數字小於等於7,且跳轉到 *(0x401b60 + 8 * args[1] ) 處。
這是一個switch結構,分別對eax賦值,然后與我們輸入的第二個數字比較,相等才過關。
所以相應的這里的答案也有8組。其中一組為 0 535 。
phase_4
0000000000400fc1 <phase_4>: 400fc1: 48 83 ec 18 sub $0x18,%rsp 400fc5: 48 8d 54 24 0c lea 0xc(%rsp),%rdx 400fca: be c1 1e 40 00 mov $0x401ec1,%esi 400fcf: b8 00 00 00 00 mov $0x0,%eax 400fd4: e8 d7 fa ff ff callq 400ab0 <__isoc99_sscanf@plt> 400fd9: 83 f8 01 cmp $0x1,%eax 400fdc: 75 07 jne 400fe5 <phase_4+0x24> 400fde: 83 7c 24 0c 00 cmpl $0x0,0xc(%rsp) 400fe3: 7f 05 jg 400fea <phase_4+0x29> 400fe5: e8 53 06 00 00 callq 40163d <explode_bomb> 400fea: 8b 7c 24 0c mov 0xc(%rsp),%edi 400fee: e8 91 ff ff ff callq 400f84 <func4> 400ff3: 83 f8 37 cmp $0x37,%eax 400ff6: 74 05 je 400ffd <phase_4+0x3c> 400ff8: e8 40 06 00 00 callq 40163d <explode_bomb> 400ffd: 48 83 c4 18 add $0x18,%rsp 401001: c3 retq
0000000000400f84 <func4>: 400f84: 48 89 5c 24 f0 mov %rbx,-0x10(%rsp) 400f89: 48 89 6c 24 f8 mov %rbp,-0x8(%rsp) 400f8e: 48 83 ec 18 sub $0x18,%rsp 400f92: 89 fb mov %edi,%ebx 400f94: b8 01 00 00 00 mov $0x1,%eax 400f99: 83 ff 01 cmp $0x1,%edi 400f9c: 7e 14 jle 400fb2 <func4+0x2e> 400f9e: 8d 7b ff lea -0x1(%rbx),%edi 400fa1: e8 de ff ff ff callq 400f84 <func4> 400fa6: 89 c5 mov %eax,%ebp 400fa8: 8d 7b fe lea -0x2(%rbx),%edi 400fab: e8 d4 ff ff ff callq 400f84 <func4> 400fb0: 01 e8 add %ebp,%eax 400fb2: 48 8b 5c 24 08 mov 0x8(%rsp),%rbx 400fb7: 48 8b 6c 24 10 mov 0x10(%rsp),%rbp 400fbc: 48 83 c4 18 add $0x18,%rsp 400fc0: c3
要求是輸入一個數(只能有一個數),要求這個數比0大,而且將這個數作為 func4 的參數。然后要求func4函數的返回值為55(0x37).而func4這個函數是個遞歸函數,所以這題的關鍵是func4函數。我們只要寫出該函數的 C 形式,然后不同的數去試就ok了。
int func4(int n) { if (n <= 1) return 1; return func4(n-1) + func4(n-2); } int main(void) { int i; for (i = 2; 1; i++) { if (func4(i) == 55) break; } printf("%d\n", i); }
輸出為 9,所以答案即為9.
phase_5
0000000000401002 <phase_5>: 401002: 48 83 ec 18 sub $0x18,%rsp 401006: 48 8d 4c 24 08 lea 0x8(%rsp),%rcx 40100b: 48 8d 54 24 0c lea 0xc(%rsp),%rdx 401010: be be 1e 40 00 mov $0x401ebe,%esi 401015: b8 00 00 00 00 mov $0x0,%eax 40101a: e8 91 fa ff ff callq 400ab0 <__isoc99_sscanf@plt> 40101f: 83 f8 01 cmp $0x1,%eax 401022: 7f 05 jg 401029 <phase_5+0x27> 401024: e8 14 06 00 00 callq 40163d <explode_bomb> 401029: 8b 44 24 0c mov 0xc(%rsp),%eax 40102d: 83 e0 0f and $0xf,%eax 401030: 89 44 24 0c mov %eax,0xc(%rsp) 401034: 83 f8 0f cmp $0xf,%eax 401037: 74 2c je 401065 <phase_5+0x63> 401039: b9 00 00 00 00 mov $0x0,%ecx 40103e: ba 00 00 00 00 mov $0x0,%edx 401043: 83 c2 01 add $0x1,%edx 401046: 48 98 cltq 401048: 8b 04 85 a0 1b 40 00 mov 0x401ba0(,%rax,4),%eax 40104f: 01 c1 add %eax,%ecx 401051: 83 f8 0f cmp $0xf,%eax 401054: 75 ed jne 401043 <phase_5+0x41> 401056: 89 44 24 0c mov %eax,0xc(%rsp) 40105a: 83 fa 0c cmp $0xc,%edx 40105d: 75 06 jne 401065 <phase_5+0x63> 40105f: 3b 4c 24 08 cmp 0x8(%rsp),%ecx 401063: 74 05 je 40106a <phase_5+0x68> 401065: e8 d3 05 00 00 callq 40163d <explode_bomb> 40106a: 48 83 c4 18 add $0x18,%rsp 40106e: c3 retq
這題也是要求你輸入兩個數字, 然后第一個數字只保留后四位,其他位置0(and $0xf,%eax)。且第一個輸入的數字作為int數組的索引從數組中取數,然后將得到的數字作為索引繼續從數組取數(每經過一次取數:edx+1,ecx不斷將取出的數加到自身)。。。直到取出15。此時要求edx為12,ecx等於第二個輸入的數。
在 0x401048 處設置斷點,不斷嘗試各種數字,看看這個數組中的內容到底是什么。
可以發現輸入7時,可以滿足取出15時要edx為12。此時ecx為93.
所以答案為 7 93
phase_6
00000000004010d9 <phase_6>: 4010d9: 48 83 ec 08 sub $0x8,%rsp 4010dd: ba 0a 00 00 00 mov $0xa,%edx 4010e2: be 00 00 00 00 mov $0x0,%esi 4010e7: e8 94 fa ff ff callq 400b80 <strtol@plt> 4010ec: 89 05 8e 16 20 00 mov %eax,0x20168e(%rip) # 602780 <node0> 4010f2: bf 80 27 60 00 mov $0x602780,%edi 4010f7: e8 73 ff ff ff callq 40106f <fun6> 4010fc: 48 8b 40 08 mov 0x8(%rax),%rax 401100: 48 8b 40 08 mov 0x8(%rax),%rax 401104: 48 8b 40 08 mov 0x8(%rax),%rax 401108: 8b 15 72 16 20 00 mov 0x201672(%rip),%edx # 602780 <node0> 40110e: 39 10 cmp %edx,(%rax) 401110: 74 05 je 401117 <phase_6+0x3e> 401112: e8 26 05 00 00 callq 40163d <explode_bomb> 401117: 48 83 c4 08 add $0x8,%rsp 40111b: c3 retq
查手冊得,strtol是將字符串中的數字轉化為特定進制,這里的參數edx為0xa,因此轉化為10進制。我們隨便輸入一個數字 100,在 4010ec 處設置斷點,發現eax中的數字就是100.
然后發現fun6的參數edi(0x602780)固定,那么輸出也應該是固定的,所以沒必要去看fun6。
引不引爆的關鍵在40110e處,我們在此處設置斷電,將edx,(rax)的內容都打印一下,
發現edx的數據是我們輸入的值100,而(rax)為600,如圖,因此我們只要開始輸入600應該就通過了(在 4010ec 中我們將100存入0x20168e(%rip)中,在 401108 中我們從 0x201672(%rip) 取出了100)。
因此答案為600.
secret_phase