csapp 炸彈實(shí)驗(yàn)

這是 2016 版的 bomb

下載得到 bomb.tar 文件,解壓后只有 bomb 二進(jìn)制文件,以及一個(gè) bomb.c 文件,bomb.c 沒(méi)有對(duì)應(yīng)的頭文件。所有思路只有是反匯編 bomb, 分析匯編代碼。

這里用到兩個(gè)非常強(qiáng)大的工具 objdump,gdb

  • objdump 用來(lái)反匯編的,-d 參數(shù)得到 x86 匯編,-M 參數(shù)還可以選擇不同的匯編形式,比如 -M 8086 得到 8086 匯編,詳細(xì)內(nèi)容可以 man objdump.
  • gdb 是強(qiáng)大的 GNU DEBUGGER 用法如下
    (gdb) b(breakpoint): 用法:b 函數(shù)名 :對(duì)此函數(shù)進(jìn)行中斷 ;b 文件名:行號(hào);
    (gdb) run:?jiǎn)?dòng)程序,運(yùn)行至程序的斷點(diǎn)或者結(jié)束;
    (gdb) l(list): 用法:l funcname,制定函數(shù)的源碼
    (gdb) s(step): 進(jìn)入函數(shù),逐語(yǔ)句運(yùn)行;
    (gdb) n(next): 不進(jìn)入函數(shù),逐過(guò)程運(yùn)行;
    (gdb) c(continue):繼續(xù)運(yùn)行,跳至下一個(gè)斷點(diǎn);
    (gdb) p(print):打印顯示變量值;
    (gdb) set variable=value, 為變量賦值;
    (gdb) kill:終止調(diào)試的程序;
    (gdb) h(help):列出 gdb 詳細(xì)命令幫助列表;
    (gdb) clear filename.c:30:清除 30 行處的斷點(diǎn);
    (gdb) info break:顯示斷點(diǎn)信息;
    (gdb) delete 斷點(diǎn)編號(hào):斷點(diǎn)編號(hào)是 info break 后顯示出來(lái)的;
    (gdb) bt(backtrace):回溯到段出錯(cuò)的位置;
    (gdb) frame 幀號(hào):幀號(hào)是 bt 命令產(chǎn)生的堆棧針;
    (gdb) q:退出;
    (gdb) x(examine):查看內(nèi)存中的值等 // 詳細(xì)內(nèi)容在 gdb 中輸入 help x 查看

下面開(kāi)始拆 :bomb: 之旅

general

觀察匯編代碼,可以看到有 main, phase1--6, 等,重點(diǎn)看這幾個(gè)函數(shù),從 main 開(kāi)始,結(jié)合 bomb.c, 可以明白程序的控制流,每個(gè)階段用 phase 函數(shù)判斷輸入是否正確,不正確就 boon, 結(jié)束程序

phase1

來(lái)到 phase1,

第一行準(zhǔn)備棧幀,第二行就是將地址存入 $esi, 這是一個(gè)字符串的地址,可以猜測(cè)下面 string_not_equal 就是比較這個(gè)字符串與輸入字符串是否相等的函數(shù).(最開(kāi)始我還去分析了這個(gè)函數(shù)的匯編代碼,確實(shí)是那樣,先比較長(zhǎng)度,然后逐一比較。所以找到這個(gè)地址0x402400存儲(chǔ)的字符串就行了,在 asm 文件中搜索,沒(méi)有,所以要在程序運(yùn)行時(shí)才可以到達(dá)這個(gè)虛擬地址,未來(lái) address space 的堆中。這時(shí)就要用到強(qiáng)大的 gdb 了,

切換到 bomb 文件夾,依次輸入

gdb
(gdb) file bomb
(gdb) x /s 0x402400 # x(examine) s 參數(shù)是 string 的意思

即得Border relations with Canada have never been better.

phase2

所以答案是 1 2 4 8 16 32

phase3

  0000000000400f43 <phase_3>:
    400f43: sub    $0x18,%rsp
    400f47: lea    0xc(%rsp),%rcx
    400f4c: lea    0x8(%rsp),%rdx
    400f51: mov    $0x4025cf,%esi                     # 又是一個(gè)字符串,可以用 gdb 查看,得到`"%d %d", 格式化字符串,說(shuō)明輸入兩個(gè)數(shù)字
    400f56: mov    $0x0,%eax
    400f5b: callq  400bf0 <__isoc99_sscanf@plt>       # 輸入
    400f60: cmp    $0x1,%eax                           # 判斷輸入成功
    400f63: jg     400f6a <phase_3+0x27>
    400f65: callq  40143a <explode_bomb>
    400f6a: cmpl   $0x7,0x8(%rsp)                       # 第一個(gè)參數(shù)是否小于等于 7, 大于則 boom
    400f6f: ja     400fad <phase_3+0x6a>
    400f71: mov    0x8(%rsp),%eax
    400f75: jmpq   *0x402470(,%rax,8)                 # 以下是 switch, 根據(jù) rax, 即第一個(gè)輸入的參數(shù)跳轉(zhuǎn)
    400f7c: mov    $0xcf,%eax                          # 由此容易得到答案,比如這里是 rax=0 時(shí),則 另一個(gè)參數(shù)為 0xcf  = 207
    400f81: jmp    400fbe <phase_3+0x7b>
    400f83: mov    $0x2c3,%eax
    400f88: jmp    400fbe <phase_3+0x7b>
    400f8a: mov    $0x100,%eax
    400f8f: jmp    400fbe <phase_3+0x7b>
    400f91: mov    $0x185,%eax
    400f96: jmp    400fbe <phase_3+0x7b>
    400f98: mov    $0xce,%eax
    400f9d: jmp    400fbe <phase_3+0x7b>
    400f9f: mov    $0x2aa,%eax
    400fa4: jmp    400fbe <phase_3+0x7b>
    400fa6: mov    $0x147,%eax
    400fab: jmp    400fbe <phase_3+0x7b>
    400fad: callq  40143a <explode_bomb>
    400fb2: mov    $0x0,%eax
    400fb7: jmp    400fbe <phase_3+0x7b>
    400fb9: mov    $0x137,%eax
    400fbe: cmp    0xc(%rsp),%eax
    400fc2: je     400fc9 <phase_3+0x86>
    400fc4: callq  40143a <explode_bomb>
    400fc9: add    $0x18,%rsp
    400fcd: retq

swith 跳轉(zhuǎn)表
%rax 跳轉(zhuǎn)地址 0xc(%rsp)
0 0x0000000000400f7c 0xcf 207
1 0x0000000000400fb9 0x137 311
2 0x0000000000400f83 0x2c3 707
3 0x0000000000400f8a 0x100 256
4 0x0000000000400f91 0x185 389
5 0x0000000000400f98 0xce 206
6 0x0000000000400f9f 0x2aa 682
7 0x0000000000400fa6 0x147 327
所以結(jié)果為0 207 ...

phase4

 000000000040100c <phase_4>:
   40100c:    sub    $0x18,%rsp
   401010:    lea    0xc(%rsp),%rcx
   401015:    lea    0x8(%rsp),%rdx
   40101a:    mov    $0x4025cf,%esi                 #同樣,gdb 中 x /s 知道輸入兩個(gè)數(shù)字
   40101f:    mov    $0x0,%eax
   401024:    callq  400bf0 <__isoc99_sscanf@plt>
   401029:    cmp    $0x2,%eax                      # 判斷是否輸入兩個(gè)數(shù)
   40102c:    jne    401035 <phase_4+0x29>
   40102e:    cmpl   $0xe,0x8(%rsp)                  # 判斷每個(gè)數(shù)是否≤14 , 大于則 boom
   401033:    jbe    40103a <phase_4+0x2e>           # 跳轉(zhuǎn)
   401035:    callq  40143a <explode_bomb>
   40103a:    mov    $0xe,%edx                       # 構(gòu)造 func4 的參數(shù) (phase4 調(diào)用的)
   40103f:    mov    $0x0,%esi                       # 構(gòu)造 func4 的參數(shù)
   401044:    mov    0x8(%rsp),%edi                   # 構(gòu)造 func4 的參數(shù)
   401048:    callq  400fce <func4>
   40104d:    test   %eax,%eax                       # 測(cè)試,func4 返回 0, 若不,則 boom
   40104f:    jne    401058 <phase_4+0x4c>
   401051:    cmpl   $0x0,0xc(%rsp)
   401056:    je     40105d <phase_4+0x51>
   401058:    callq  40143a <explode_bomb>
   40105d:    add    $0x18,%rsp
   401061:    retq

將 func4 轉(zhuǎn)換為 c 語(yǔ)言,并用 0--14 測(cè)試,這點(diǎn)很難,需要翻譯匯編語(yǔ)言,花很多時(shí)間,得熟悉匯編代碼才行

int func4(int a, int b, int c)
{
   int result;
   result = c;
   result = result - b;
   int tmp = result;
   tmp = (unsigned)tmp >> 31;
   result = result + tmp;
   result = result / 2;
   tmp = result + b;
   if(tmp > a)
   {
       c = tmp - 1;
       result = func4(a, b, c);
       return (2 * result);
   }
   result = 0;
   if(tmp < a)
   {
       b = tmp + 1;
       result = func4(a, b, c);
       return (1 + 2 * result);
   }
   return result;
}
// 測(cè)試從 0~14 范圍內(nèi)滿足條件的值

int main()
{
   for(int input = 0; input < 15; ++input)
   {
       int result = func4(input, 0, 14);
       if(result == 0)
       {
           printf("input = %d, func4 = %d\n", input, result);
       }
   }
   return 0;
}

得到可行解
因此 phase4 可能結(jié)果為:
0 0
1 0
3 0
7 0

phase5

嗯,加油,還有兩關(guān)了。(●ˇ?ˇ●)

 0000000000401062 <phase_5>:
    401062:  push   %rbx
    401063:  sub    $0x20,%rsp
    401067:  mov    %rdi,%rbx
    40106a:  mov    %fs:0x28,%rax
    401071:
    401073:  mov    %rax,0x18(%rsp)
    401078:  xor    %eax,%eax
    40107a:  callq  40131b <string_length>
    40107f:  cmp    $0x6,%eax                 # 說(shuō)明輸入是六個(gè)字符
    401082:  je     4010d2 <phase_5+0x70>
    401084:  callq  40143a <explode_bomb>
    401089:  jmp    4010d2 <phase_5+0x70>
    40108b:  movzbl (%rbx,%rax,1),%ecx       # 從棧幀中取出各個(gè)字符,記為 x
    40108f:  mov    %cl,(%rsp)
    401092:  mov    (%rsp),%rdx
    401096:  and    $0xf,%edx                # y=0xf & x, 即將一個(gè) byte 的高 4 位置 0
    401099:  movzbl 0x4024b0(%rdx),%edx       # 用 gdb 查看 x /s 0x4024b0  得到字符串"maduiersnfotvbyl", 所以這一行是以 y 作為偏移量,取字符數(shù)組的第幾個(gè)字符
    4010a0:  mov    %dl,0x10(%rsp,%rax,1)     # 將取得的存于棧幀中 // 后面用 string_not_equl 比較
    4010a4:  add    $0x1,%rax
    4010a8:  cmp    $0x6,%rax                 # 循環(huán) 6 次
    4010ac:  jne    40108b <phase_5+0x29>
    4010ae:  movb   $0x0,0x16(%rsp)
    4010b3:  mov    $0x40245e,%esi           # 這是要比較的字符串,同樣用 gdb 查看得到 "flyers"
    4010b8:  lea    0x10(%rsp),%rdi
    4010bd:  callq  401338 <strings_not_equal>
    4010c2:  test   %eax,%eax
    4010c4:  je     4010d9 <phase_5+0x77>
    4010c6:  callq  40143a <explode_bomb>
    4010cb:  nopl   0x0(%rax,%rax,1)
    4010d0:  jmp    4010d9 <phase_5+0x77>
    4010d2:  mov    $0x0,%eax
    4010d7:  jmp    40108b <phase_5+0x29>
    4010d9:  mov    0x18(%rsp),%rax
    4010de:  xor    %fs:0x28,%rax
    4010e5:
    4010e7:  je     4010ee <phase_5+0x8c>
    4010e9:  callq  400b30 <__stack_chk_fail@plt>
    4010ee:  add    $0x20,%rsp
    4010f2:  pop    %rbx
    4010f3:  retq

解釋在上面,反向得到需要的輸入的思路是:對(duì) flyers 的每個(gè)字符,得到在字符數(shù)組中的 index, 也就是輸入的字符的后 4 位 bit, 而鍵盤輸入一般是字母,所以很可能有兩種可能,字符 byte 的高四位為01000110, 而且可以發(fā)現(xiàn)剛好這是大寫字母 / 小寫字母開(kāi)始的前一個(gè) ascii, 所以用 python 算一下即得"ionefg"或是"IONEFG"

phase6

phase6 很難了,這真的要熟練匯編語(yǔ)言,我翻譯到前面,知道輸入的是六個(gè)不相同的數(shù)字,而且≤6 ,所以可以試全排列了, 后面的實(shí)在看不下去了,在網(wǎng)上找到這份解析
這是鏈接

(gdb) disas phase_6
Dump of assembler code for function phase_6:
  0x00000000004010f4 <+0>: push   %r14                                將被調(diào)用者保存寄存器壓入棧
  0x00000000004010f6 <+2>: push   %r13
  0x00000000004010f8 <+4>: push   %r12
  0x00000000004010fa <+6>: push   %rbp
  0x00000000004010fb <+7>: push   %rbx                                %rsp = 0x7fffffffe2c0
  0x00000000004010fc <+8>: sub    $0x50,%rsp                          分配棧空間 %rsp = 0x7fffffffe270
  0x0000000000401100 <+12>:    mov    %rsp,%r13

  0x0000000000401103 <+15>:    mov    %rsp,%rsi
  0x0000000000401106 <+18>:    callq  0x40145c <read_six_numbers>     讀入 6 個(gè)值,保存至從 %rsi 開(kāi)始的地址

  0x000000000040110b <+23>:    mov    %rsp,%r14
  0x000000000040110e <+26>:    mov    $0x0,%r12d                      %r12 置 0, 并且 %r13 %r14 %rbp 均和 %rsp 指向相同地址 0x7fffffffe270

  0x0000000000401114 <+32>:    mov    %r13,%rbp
  0x0000000000401117 <+35>:    mov    0x0(%r13),%eax                  將第 %r13 指向的輸入數(shù)復(fù)制到 %eax
  0x000000000040111b <+39>:    sub    $0x1,%eax                       將輸入數(shù)減 1
  0x000000000040111e <+42>:    cmp    $0x5,%eax                       判斷輸入數(shù)是否小于等于 6, 因?yàn)樯弦徊街袦p 1 操作
  0x0000000000401121 <+45>:    jbe    0x401128 <phase_6+52>           若大于 6, 則調(diào)用 explode_bomb
  0x0000000000401123 <+47>:    callq  0x40143a <explode_bomb>
=========================================================================================================================================================
  0x0000000000401128 <+52>:    add    $0x1,%r12d                      將 %r12 加 1
  0x000000000040112c <+56>:    cmp    $0x6,%r12d                      判斷 %r12 是否等于 6
  0x0000000000401130 <+60>:    je     0x401153 <phase_6+95>           若等于 6, 跳轉(zhuǎn),否則繼續(xù)執(zhí)行
  0x0000000000401132 <+62>:    mov    %r12d,%ebx                      將 %r12 復(fù)制到 %ebx

  0x0000000000401135 <+65>:    movslq %ebx,%rax                       將 %ebx 符號(hào)位擴(kuò)展復(fù)制到 %rax
  0x0000000000401138 <+68>:    mov    (%rsp,%rax,4),%eax              將第 %ebx 輸入數(shù)復(fù)制到 %eax
  0x000000000040113b <+71>:    cmp    %eax,0x0(%rbp)                  比較 %r13 指向的輸入數(shù)和 第 %ebx 輸入數(shù) 是否相等
  0x000000000040113e <+74>:    jne    0x401145 <phase_6+81>           如果相等,則調(diào)用 explode_bomb
  0x0000000000401140 <+76>:    callq  0x40143a <explode_bomb>
  0x0000000000401145 <+81>:    add    $0x1,%ebx                       將 %ebx 加 1
  0x0000000000401148 <+84>:    cmp    $0x5,%ebx                       判斷 %ebx 是否小于等于 5
  0x000000000040114b <+87>:    jle    0x401135 <phase_6+65>           若小于等于,跳轉(zhuǎn),否則繼續(xù)執(zhí)行;該循環(huán)判斷 %r13 指向的數(shù)據(jù)和其后輸入數(shù)不相等

  0x000000000040114d <+89>:    add    $0x4,%r13                       將 %r13 指向下一個(gè)輸入數(shù),該循環(huán)判斷所有的輸入數(shù)全部不相等
  0x0000000000401151 <+93>:    jmp    0x401114 <phase_6+32>
=========================================================================================================================================================
  0x0000000000401153 <+95>:    lea    0x18(%rsp),%rsi                 將 %rsi 指向棧中跳過(guò)讀入數(shù)據(jù)位置作為結(jié)束標(biāo)記,并且 %r14 仍和 %rsp 指向同一個(gè)位置
  0x0000000000401158 <+100>:   mov    %r14,%rax                       將 %r14 復(fù)制到 %rax
  0x000000000040115b <+103>:   mov    $0x7,%ecx
  0x0000000000401160 <+108>:   mov    %ecx,%edx                       將立即數(shù) 0x7 復(fù)制到 %edx
  0x0000000000401162 <+110>:   sub    (%rax),%edx                     立即數(shù) 7 減去 %r14 指向的數(shù)據(jù)
  0x0000000000401164 <+112>:   mov    %edx,(%rax)                     將 7 減的結(jié)果存回 %r14 執(zhí)行的內(nèi)存單元
  0x0000000000401166 <+114>:   add    $0x4,%rax                       %rax 指向下一個(gè)輸入數(shù)
  0x000000000040116a <+118>:   cmp    %rsi,%rax                       比較是否達(dá)到輸入數(shù)組的末尾,
  0x000000000040116d <+121>:   jne    0x401160 <phase_6+108>          該循環(huán)使用立即數(shù) 7 減去每個(gè)輸入數(shù)據(jù)
==========================================================================================================================================================
  0x000000000040116f <+123>:   mov    $0x0,%esi                       將 %rsi 置 0
  0x0000000000401174 <+128>:   jmp    0x401197 <phase_6+163>

  0x0000000000401176 <+130>:   mov    0x8(%rdx),%rdx                  將 0x8(%rdx) 指向內(nèi)存單元的內(nèi)容復(fù)制到 %rdx, 指向鏈表下一個(gè)元素
  0x000000000040117a <+134>:   add    $0x1,%eax                       將 %eax 加 1
  0x000000000040117d <+137>:   cmp    %ecx,%eax                       比較 %ecx 和 %eax 是否相等
  0x000000000040117f <+139>:   jne    0x401176 <phase_6+130>          不相等,繼續(xù)遍歷鏈表,最終 %rdx 指向鏈表的第 %ecx 個(gè)節(jié)點(diǎn)
  0x0000000000401181 <+141>:   jmp    0x401188 <phase_6+148>
  0x0000000000401183 <+143>:   mov    $0x6032d0,%edx                  重置鏈表首地址
  0x0000000000401188 <+148>:   mov    %rdx,0x20(%rsp,%rsi,2)
  0x000000000040118d <+153>:   add    $0x4,%rsi
  0x0000000000401191 <+157>:   cmp    $0x18,%rsi
  0x0000000000401195 <+161>:   je     0x4011ab <phase_6+183>

  0x0000000000401197 <+163>:   mov    (%rsp,%rsi,1),%ecx              將 (%rsp + %rsi) 指向的數(shù)據(jù)復(fù)制到 %ecx
  0x000000000040119a <+166>:   cmp    $0x1,%ecx                       比較 %ecx 是否小于等于 1
  0x000000000040119d <+169>:   jle    0x401183 <phase_6+143>          若小于等于,跳轉(zhuǎn),否則繼續(xù)執(zhí)行,等于 1, %edx 直接指向鏈表首地址
  0x000000000040119f <+171>:   mov    $0x1,%eax                       將 %eax 置 1
  0x00000000004011a4 <+176>:   mov    $0x6032d0,%edx                  將 %rdx 指向內(nèi)存單元 0x6032d0
  0x00000000004011a9 <+181>:   jmp    0x401176 <phase_6+130>          跳轉(zhuǎn);該循環(huán)根據(jù)輸入數(shù)將鏈表中對(duì)應(yīng)的第輸入數(shù)個(gè)節(jié)點(diǎn)的地址復(fù)制到 0x20(%rsp) 開(kāi)始的棧中
==========================================================================================================================================================
  0x00000000004011ab <+183>:   mov    0x20(%rsp),%rbx                 將 0x20(%rsp) 的鏈表節(jié)點(diǎn)地址復(fù)制到 %rbx
  0x00000000004011b0 <+188>:   lea    0x28(%rsp),%rax                 將 %rax 指向棧中下一個(gè)鏈表節(jié)點(diǎn)的地址
  0x00000000004011b5 <+193>:   lea    0x50(%rsp),%rsi                 將 %rsi 指向保存的鏈表節(jié)點(diǎn)地址的末尾
  0x00000000004011ba <+198>:   mov    %rbx,%rcx

  0x00000000004011bd <+201>:   mov    (%rax),%rdx
  0x00000000004011c0 <+204>:   mov    %rdx,0x8(%rcx)                  將棧中指向的后一個(gè)節(jié)點(diǎn)的地址復(fù)制到前一個(gè)節(jié)點(diǎn)的地址位置
  0x00000000004011c4 <+208>:   add    $0x8,%rax                       移動(dòng)到下一個(gè)節(jié)點(diǎn)
  0x00000000004011c8 <+212>:   cmp    %rsi,%rax                       判斷 6 個(gè)節(jié)點(diǎn)是否遍歷完畢
  0x00000000004011cb <+215>:   je     0x4011d2 <phase_6+222>
  0x00000000004011cd <+217>:   mov    %rdx,%rcx
  0x00000000004011d0 <+220>:   jmp    0x4011bd <phase_6+201>
  0x00000000004011d2 <+222>:   movq   $0x0,0x8(%rdx)                  該循環(huán)按照 7 減去輸入數(shù)據(jù)的索引重新調(diào)整鏈表
==========================================================================================================================================================
  0x00000000004011da <+230>:   mov    $0x5,%ebp
  0x00000000004011df <+235>:   mov    0x8(%rbx),%rax                  將 %rax 指向 %rbx 下一個(gè)鏈表節(jié)點(diǎn)
  0x00000000004011e3 <+239>:   mov    (%rax),%eax
  0x00000000004011e5 <+241>:   cmp    %eax,(%rbx)                     比較鏈表節(jié)點(diǎn)中第一個(gè)字段值的大小,如果前一個(gè)節(jié)點(diǎn)值大于后一個(gè)節(jié)點(diǎn)值,跳轉(zhuǎn)
  0x00000000004011e7 <+243>:   jge    0x4011ee <phase_6+250>
  0x00000000004011e9 <+245>:   callq  0x40143a <explode_bomb>
  0x00000000004011ee <+250>:   mov    0x8(%rbx),%rbx                  將 %rbx 向后移動(dòng),指向棧中下一個(gè)鏈表節(jié)點(diǎn)的地址
  0x00000000004011f2 <+254>:   sub    $0x1,%ebp                       判斷循環(huán)是否結(jié)束,該循環(huán)判斷棧中重新調(diào)整后的鏈表節(jié)點(diǎn)是否按照降序排列
  0x00000000004011f5 <+257>:   jne    0x4011df <phase_6+235>
  0x00000000004011f7 <+259>:   add    $0x50,%rsp
  0x00000000004011fb <+263>:   pop    %rbx
  0x00000000004011fc <+264>:   pop    %rbp
  0x00000000004011fd <+265>:   pop    %r12
  0x00000000004011ff <+267>:   pop    %r13
  0x0000000000401201 <+269>:   pop    %r14
  0x0000000000401203 <+271>:   retq
End of assembler dump.

(gdb) disas read_six_numbers
%rsi 存儲(chǔ)調(diào)用者 phase_2 棧幀的局部變量開(kāi)始地址
%rdx = %rsi + 0
%rcx = %rsi + 4
%r8 =  %rsi + 8
%r9 =  %rsi + 12
(%rsp)  = %rsi + 16
8(%rsp) = %rsi + 20
Dump of assembler code for function read_six_numbers:
  0x000000000040145c <+0>: sub    $0x18,%rsp
  0x0000000000401460 <+4>: mov    %rsi,%rdx
  0x0000000000401463 <+7>: lea    0x4(%rsi),%rcx
  0x0000000000401467 <+11>:    lea    0x14(%rsi),%rax
  0x000000000040146b <+15>:    mov    %rax,0x8(%rsp)
  0x0000000000401470 <+20>:    lea    0x10(%rsi),%rax
  0x0000000000401474 <+24>:    mov    %rax,(%rsp)
  0x0000000000401478 <+28>:    lea    0xc(%rsi),%r9
  0x000000000040147c <+32>:    lea    0x8(%rsi),%r8
  0x0000000000401480 <+36>:    mov    $0x4025c3,%esi
  0x0000000000401485 <+41>:    mov    $0x0,%eax
  0x000000000040148a <+46>:    callq  0x400bf0 <__isoc99_sscanf@plt>
  0x000000000040148f <+51>:    cmp    $0x5,%eax
  0x0000000000401492 <+54>:    jg     0x401499 <read_six_numbers+61>
  0x0000000000401494 <+56>:    callq  0x40143a <explode_bomb>
  0x0000000000401499 <+61>:    add    $0x18,%rsp
  0x000000000040149d <+65>:    retq

%rbp %rbx %r12~%15 被調(diào)用者保存寄存器
%r10 %r11 調(diào)用者保存寄存器
%rdi %rsi %rdx %rcx %r8 %r9 依次保存輸入數(shù) 1~6

假設(shè)輸入數(shù)據(jù)為 4 3 2 1 6 5

猜測(cè) 0x6032d8 為鏈表首地址,鏈表中每個(gè)節(jié)點(diǎn)占用 12 個(gè) Byte, 前 8 字節(jié)保存兩個(gè) 4 字 Byte 的整型數(shù),剩余的 4Byte 存放下個(gè)節(jié)點(diǎn)地址

GDB 查看使用 7 減去對(duì)應(yīng)的輸入后的數(shù)據(jù)
(gdb) p /x rsp1 = 0x7fffffffe270
(gdb) x/6dw 0x7fffffffe270
0x7fffffffe270: 3 4 5 6
0x7fffffffe280: 1 2

重新調(diào)整鏈表前的鏈表的結(jié)構(gòu)
(gdb) x/24xw 0x006032d0
0x6032d0 <node1>: 0x0000014c 0x00000001 0x006032e0 0x00000000
0x6032e0 <node2>: 0x000000a8 0x00000002 0x006032f0 0x00000000
0x6032f0 <node3>: 0x0000039c 0x00000003 0x00603300 0x00000000
0x603300 <node4>: 0x000002b3 0x00000004 0x00603310 0x00000000
0x603310 <node5>: 0x000001dd 0x00000005 0x00603320 0x00000000
0x603320 <node6>: 0x000001bb 0x00000006 0x00000000 0x00000000

保存在棧中鏈表節(jié)點(diǎn)信息
(gdb) x/6xg 0x7fffffffe290
0x7fffffffe290: 0x00000000006032f0 0x0000000000603300
0x7fffffffe2a0: 0x0000000000603310 0x0000000000603320
0x7fffffffe2b0: 0x00000000006032d0 0x00000000006032e0

按照 7 減去對(duì)應(yīng)的輸入后重新調(diào)整鏈表后的鏈表結(jié)構(gòu),索引順序?yàn)?3 4 5 6 1 2
(gdb) x/24xw 0x006032d0
0x6032d0 <node1>: 0x0000014c 0x00000001 0x006032e0 0x00000000
0x6032e0 <node2>: 0x000000a8 0x00000002 0x00000000 0x00000000
0x6032f0 <node3>: 0x0000039c 0x00000003 0x00603300 0x00000000
0x603300 <node4>: 0x000002b3 0x00000004 0x00603310 0x00000000
0x603310 <node5>: 0x000001dd 0x00000005 0x00603320 0x00000000
0x603320 <node6>: 0x000001bb 0x00000006 0x006032d0 0x00000000

破解思路:
將鏈表中每個(gè)節(jié)點(diǎn)按照前 4 字節(jié)降序排序
3 4 5 6 1 2
因?yàn)樵谇懊媸褂?7 減去對(duì)應(yīng)的值,所以破解密碼
4 3 2 1 6 5

final

啊,終于拆除??了,
╰(°▽°)╯
等等,還漏了什么`? 在 asm 中,可以看到還有 secret_phase 這個(gè)函數(shù),可是這個(gè)函數(shù)的調(diào)用是有技巧的,追蹤發(fā)現(xiàn)是在 phase_defused 中調(diào)用的,同樣,查看字符串,發(fā)現(xiàn)比較了"DrEvil", 以及一個(gè)格式串"%d %d %s", 可能是 phase3,phase4 的數(shù)字加上 DrEvil 輸入. 可是最后我試了很多次都沒(méi)有試出來(lái)。后來(lái)發(fā)現(xiàn)是最后才出現(xiàn),那是第七關(guān),我以為是在輸入 DrEvil 就出現(xiàn)。而且試了是在第四關(guān)后

方法二,gdb 中設(shè)置斷點(diǎn)'b phase_defused', 然后jump secret_phase

最后得到答案是 22

summary

通過(guò)這個(gè) lab, 學(xué)到了 gdb,objdump 等工具的使用,對(duì)匯編語(yǔ)言更熟悉,對(duì)函數(shù)調(diào)用中棧幀的變化,動(dòng)態(tài)變量的理解更加深刻
不得不佩服國(guó)外教學(xué)的質(zhì)量,以及這個(gè) lab 的有趣與實(shí)用

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容