---
# System prepended metadata

title: CSAPP Bomb
tags: [Computer System]

---

# CSAPP Bomb
###### tags: `Computer System`
## Phase 1
題目給定一個 `bomb.c` 檔案以及一個執行檔 `bomb.out`，我們先看到 `bomb.c`

```c
    initialize_bomb();

    printf("Welcome to my fiendish little bomb. You have 6 phases with\n");
    printf("which to blow yourself up. Have a nice day!\n");

    /* Hmm...  Six phases must be more secure than one phase! */
    input = read_line();             /* Get input                   */
    phase_1(input);                  /* Run the phase               */
    phase_defused();                 /* Drat!  They figured it out!
				      * Let me know how they did it. */
    printf("Phase 1 defused. How about the next one?\n");

    /* The second phase is harder.  No one will ever figure out
     * how to defuse this... */
    input = read_line();
    phase_2(input);
    phase_defused();
    printf("That's number 2.  Keep going!\n");
```
首先會到 `phase_1`，接著進入到 `phase_defused` 進行判斷，如果成功，則執行下方 `printf` 接著向下執行

使用 `gdb bomb` 對 `bomb.out` 進行分析，先對 `phase_1` 下一個中斷點，接著執行
```
(gdb)$ b phase_1
(gdb)$ r
```
以下結果
```
RAX: 0x603780 --> 0x636261 ('abc')
RBX: 0x402210 (<__libc_csu_init>:       mov    QWORD PTR [rsp-0x28],rbp)
RCX: 0x3
RDX: 0x1
RSI: 0x603780 --> 0x636261 ('abc')
RDI: 0x603780 --> 0x636261 ('abc')
RBP: 0x0
RSP: 0x7fffffffdbe8 --> 0x400e3f (<main+159>:   call   0x4015c4 <phase_defused>)
RIP: 0x400ee0 (<phase_1>:       sub    rsp,0x8)
R8 : 0x603780 --> 0x636261 ('abc')
R9 : 0x7c ('|')
R10: 0xfffffffffffffe34
R11: 0x7ffff7dfe4a0 (<__ctype_b_loc>:   endbr64)
R12: 0x400c90 (<_start>:        xor    ebp,ebp)
R13: 0x7fffffffdce0 --> 0x1
R14: 0x0
R15: 0x0
EFLAGS: 0x206 (carry PARITY adjust zero sign trap INTERRUPT direction overflow)
─────────────────────────────────────────────────────────────────────── Code ───────────────────────────────────────────────────────────────────────
   0x400edd:    nop
   0x400ede:    nop
   0x400edf:    nop
=> 0x400ee0 <phase_1>:  sub    rsp,0x8
   0x400ee4 <phase_1+4>:        mov    esi,0x402400
   0x400ee9 <phase_1+9>:        call   0x401338 <strings_not_equal>
   0x400eee <phase_1+14>:       test   eax,eax
   0x400ef0 <phase_1+16>:       je     0x400ef7 <phase_1+23>
────────────────────────────────────────────────────────────────────── Stack ───────────────────────────────────────────────────────────────────────
0000| 0x7fffffffdbe8 --> 0x400e3f (<main+159>:  call   0x4015c4 <phase_defused>)
0008| 0x7fffffffdbf0 --> 0x402210 (<__libc_csu_init>:   mov    QWORD PTR [rsp-0x28],rbp)
0016| 0x7fffffffdbf8 --> 0x7ffff7dee083 (<__libc_start_main+243>:       mov    edi,eax)
0024| 0x7fffffffdc00 --> 0x100000044
0032| 0x7fffffffdc08 --> 0x7fffffffdce8 --> 0x7fffffffdf1e ("/mnt/c/Users/Hank/Desktop/workspace/third_grade_2/CSAPP/CSAPP-Labs/labs/bomb/bomb")
0040| 0x7fffffffdc10 --> 0x1f7fb27a0
0048| 0x7fffffffdc18 --> 0x400da0 (<main>:      push   rbx)
0056| 0x7fffffffdc20 --> 0x402210 (<__libc_csu_init>:   mov    QWORD PTR [rsp-0x28],rbp)
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Legend: code, data, rodata, heap, value

Breakpoint 1, 0x0000000000400ee0 in phase_1 ()
gdb-peda$
```
我們將 `abc` 作為輸入，並且這時候程式停在 `phase_1` 中，我們可以看到接著會呼叫會呼叫 `strings_not_equal`，我們判斷 `phase_1` 會將使用者輸入的字串和某一個字串進入到 `strings_not_equal` 進行比較，如果比較錯誤，則我們不會進入到下面的 `printf`，我們可以嘗試對 `strings_not_equal` 下一個中斷點，並通過 `stepi` 進入到 `strings_not_equal` 中
```
(gdb)$ b strings_not_equal
(gdb)$ stepi
```
```
─────────────────────────────────────────────────────────────────────── Code ───────────────────────────────────────────────────────────────────────
   0x401330 <string_length+21>: repz ret
   0x401332 <string_length+23>: mov    eax,0x0
   0x401337 <string_length+28>: ret
=> 0x401338 <strings_not_equal>:        push   r12
   0x40133a <strings_not_equal+2>:      push   rbp
   0x40133b <strings_not_equal+3>:      push   rbx
   0x40133c <strings_not_equal+4>:      mov    rbx,rdi
   0x40133f <strings_not_equal+7>:      mov    rbp,rsi
```
進入到 `strings_not_equal` 中可以看到，我們會將 `rdi` 暫存器的值放入到 `rbx` 中，`rsi` 暫存器中的值放入到 `rbp` 中。

我們也可以試著反組譯整個 `strings_not_equal` 了解其行為
```
disas strings_not_equal
```
```
=> 0x0000000000401338 <+0>:     push   r12
   0x000000000040133a <+2>:     push   rbp
   0x000000000040133b <+3>:     push   rbx
   0x000000000040133c <+4>:     mov    rbx,rdi
   0x000000000040133f <+7>:     mov    rbp,rsi
   0x0000000000401342 <+10>:    call   0x40131b <string_length>
   0x0000000000401347 <+15>:    mov    r12d,eax
   0x000000000040134a <+18>:    mov    rdi,rbp
   0x000000000040134d <+21>:    call   0x40131b <string_length>
   0x0000000000401352 <+26>:    mov    edx,0x1
   0x0000000000401357 <+31>:    cmp    r12d,eax
   0x000000000040135a <+34>:    jne    0x40139b <strings_not_equal+99>
   0x000000000040135c <+36>:    movzx  eax,BYTE PTR [rbx]
   0x000000000040135f <+39>:    test   al,al
   0x0000000000401361 <+41>:    je     0x401388 <strings_not_equal+80>
   0x0000000000401363 <+43>:    cmp    al,BYTE PTR [rbp+0x0]
   0x0000000000401366 <+46>:    je     0x401372 <strings_not_equal+58>
   0x0000000000401368 <+48>:    jmp    0x40138f <strings_not_equal+87>
   0x000000000040136a <+50>:    cmp    al,BYTE PTR [rbp+0x0]
   0x000000000040136d <+53>:    nop    DWORD PTR [rax]
   0x0000000000401370 <+56>:    jne    0x401396 <strings_not_equal+94>
   0x0000000000401372 <+58>:    add    rbx,0x1
   0x0000000000401376 <+62>:    add    rbp,0x1
   0x000000000040137a <+66>:    movzx  eax,BYTE PTR [rbx]
   0x000000000040137d <+69>:    test   al,al
   0x000000000040137f <+71>:    jne    0x40136a <strings_not_equal+50>
   0x0000000000401381 <+73>:    mov    edx,0x0
   0x0000000000401386 <+78>:    jmp    0x40139b <strings_not_equal+99>
   0x0000000000401388 <+80>:    mov    edx,0x0
   0x000000000040138d <+85>:    jmp    0x40139b <strings_not_equal+99>
   0x000000000040138f <+87>:    mov    edx,0x1
   0x0000000000401394 <+92>:    jmp    0x40139b <strings_not_equal+99>
   0x0000000000401396 <+94>:    mov    edx,0x1
   0x000000000040139b <+99>:    mov    eax,edx
   0x000000000040139d <+101>:   pop    rbx
   0x000000000040139e <+102>:   pop    rbp
   0x000000000040139f <+103>:   pop    r12
   0x00000000004013a1 <+105>:   ret
```
我們可以看到最一開始，我們會存取 `rdi` 暫存器和 `rsi` 暫存器，這兩個暫存器會用來儲存指標，記憶體地址等等，而 `strings_not_equal` 判斷會接收兩個 `char *`，因此判斷兩個參數應該會在 `rdi` 以及 `rsi` 中。

我們試著印出 `rdi` 的內容
```
(gdb)$ info register rdi
rdi            0x603780            0x603780
```
我們可以看到 `rdi` 暫存器中儲存的是一個記憶體地址，判斷這個是一個指標，指向一段字串，我們試著印出該記憶體地址所指向的字串
```
(gdb)$ print (char *) 0x603780
$1 = 0x603780 <input_strings> "abc"
```
可以看到這個字串就是我們剛剛輸入的字串，而判斷 `rsi` 暫存器中儲存記憶體地址所指向的字串應該就是我們要比較的字串了
```
(gdb)$ info register rsi
rsi            0x402400            0x402400
(gdb)$ print (char *) 0x402400
$2 = 0x402400 "Border relations with Canada have never been better."
```
只要我們輸入的字串為 `"Border relations with Canada have never been better."`，便可以通過 `phase_1`

以下為 `phase_1` 反組議產生的組合語言經由 IDA 產生出的 C 語言結果
```c
__int64 __fastcall phase_1(__int64 a1)
{
  __int64 result; // rax

  result = strings_not_equal(a1, "Border relations with Canada have never been better.");
  if ( (_DWORD)result )
    explode_bomb();
  return result;
}
```

![](https://i.imgur.com/2JKJK7i.png)

## Phase 2
接著我們同樣對 `phase_2` 的地方下斷點，然後我們試著反組議 `phase_2`，得到以下
```
   0x0000000000400efc <+0>:     push   rbp
   0x0000000000400efd <+1>:     push   rbx
   0x0000000000400efe <+2>:     sub    rsp,0x28
   0x0000000000400f02 <+6>:     mov    rsi,rsp
   0x0000000000400f05 <+9>:     call   0x40145c <read_six_numbers>
   0x0000000000400f0a <+14>:    cmp    DWORD PTR [rsp],0x1
   0x0000000000400f0e <+18>:    je     0x400f30 <phase_2+52>
   0x0000000000400f10 <+20>:    call   0x40143a <explode_bomb>
   0x0000000000400f15 <+25>:    jmp    0x400f30 <phase_2+52>
   0x0000000000400f17 <+27>:    mov    eax,DWORD PTR [rbx-0x4]
   0x0000000000400f1a <+30>:    add    eax,eax
   0x0000000000400f1c <+32>:    cmp    DWORD PTR [rbx],eax
   0x0000000000400f1e <+34>:    je     0x400f25 <phase_2+41>
   0x0000000000400f20 <+36>:    call   0x40143a <explode_bomb>
   0x0000000000400f25 <+41>:    add    rbx,0x4
   0x0000000000400f29 <+45>:    cmp    rbx,rbp
   0x0000000000400f2c <+48>:    jne    0x400f17 <phase_2+27>
   0x0000000000400f2e <+50>:    jmp    0x400f3c <phase_2+64>
   0x0000000000400f30 <+52>:    lea    rbx,[rsp+0x4]
   0x0000000000400f35 <+57>:    lea    rbp,[rsp+0x18]
   0x0000000000400f3a <+62>:    jmp    0x400f17 <phase_2+27>
   0x0000000000400f3c <+64>:    add    rsp,0x28
   0x0000000000400f40 <+68>:    pop    rbx
   0x0000000000400f41 <+69>:    pop    rbp
   0x0000000000400f42 <+70>:    ret
```
在最一開始的地方，會將 `rbp` 和 `rbx` 暫存器的數值押入到 stack 中，並且在 stack 上面分配 `0x28` 個 bytes 空間

接著我們會將 `rsp`，也就是 stack pointer 儲存到 `rsi` 暫存器中，然後呼叫 `read_six_numbers` 的函式，依照名稱判斷，作用為讀取 6 個數字，這一些數字會被存放在 stack 中。

看到 `cmp    DWORD PTR [rsp],0x1`，作用為將 rsp 暫存器所指向的 DWORD，也就是 32-bit 的值和 `0x1` 進行比較，比較的結果會儲存在 FLAGS 暫存器中，提供後續條件分之指令使用。

接著開始判斷上面的比較結果，如果為 `0x1`，則跳轉到 `<phase_2 + 52>` 的地方，也就是 `lea    rbx,[rsp+0x4]`，否則呼叫 `explode_bomb`。

如果數字為 `0x1`，會執行 `jmp    0x400f17 <phase_2+27>`，也就是執行以下程式碼
```
   0x0000000000400f17 <+27>:    mov    eax,DWORD PTR [rbx-0x4]
   0x0000000000400f1a <+30>:    add    eax,eax
   0x0000000000400f1c <+32>:    cmp    DWORD PTR [rbx],eax
   0x0000000000400f1e <+34>:    je     0x400f25 <phase_2+41>
```
這一段程式碼會將 `rbx` 指向位置的 4 bytes，也就是一個 DWORD 儲存到 `eax` 暫存器中，接著會將 `eax` 和 `eax` 相加，並放回 `eax`，接著 `cmp    DWORD PTR [rbx],eax` ，將 `eax` 和 `rbx` 記憶體指向的 DWORD 進行比較，意義上就是陣列中下一個數字和前一個數值進行比較，如果相等，也就是下一個數值為前一個數值的兩倍，則跳轉到 `<phase_2 + 41>`，也就是 `add    rbx,0x4`。

從上面可以分析出，整個 `phase_2` 的行為為讀數 6 個數字到 stack 區域中，並且使用迴圈進行比較，會比較下一個值是不是前一個值的兩倍，而我們又知道，輸入的第一個數字必須為 `0x1`，因此只要輸入 `1 2 4 8 16 32` 即可通過，以下為使用 IDA 對 `phase_2` 反組議出的組合語言所產生的 C 語言結果
```c
__int64 __fastcall phase_2(__int64 a1)
{
  __int64 result; // rax
  char *v2; // rbx
  int v3; // [rsp+0h] [rbp-38h] BYREF
  char v4; // [rsp+4h] [rbp-34h] BYREF
  char v5; // [rsp+18h] [rbp-20h] BYREF

  read_six_numbers(a1, &v3);
  if ( v3 != 1 )
    explode_bomb();
  v2 = &v4;
  do
  {
    result = (unsigned int)(2 * *((_DWORD *)v2 - 1));
    if ( *(_DWORD *)v2 != (_DWORD)result )
      explode_bomb();
    v2 += 4;
  }
  while ( v2 != &v5 );
  return result;
}
```
這裡需要注意讀取數字為 little endian 還是 big endian，我們可以實際輸入進行驗證，試著輸入 `1 2 3 4 5 6`，觀察 `rsp` 中的值
```
(gdb)$ x/8xw 0x7fffffffdc20
0x7fffffffdc20: 0x00000001      0x00000002      0x00000003      0x00000004
0x7fffffffdc30: 0x00000005      0x00000006      0x00401431      0x00000000
```
可以看到為 big endian，也就是第一個數字為 1。
![](https://i.imgur.com/mnLNkPv.png)
## Phase 3
我們對 `phase_3` 下斷點，並且反組譯
```asm
   0x0000000000400f43 <+0>:     sub    rsp,0x18
   0x0000000000400f47 <+4>:     lea    rcx,[rsp+0xc]
   0x0000000000400f4c <+9>:     lea    rdx,[rsp+0x8]
   0x0000000000400f51 <+14>:    mov    esi,0x4025cf
   0x0000000000400f56 <+19>:    mov    eax,0x0
   0x0000000000400f5b <+24>:    call   0x400bf0 <__isoc99_sscanf@plt>
   0x0000000000400f60 <+29>:    cmp    eax,0x1
   0x0000000000400f63 <+32>:    jg     0x400f6a <phase_3+39>
   0x0000000000400f65 <+34>:    call   0x40143a <explode_bomb>
   0x0000000000400f6a <+39>:    cmp    DWORD PTR [rsp+0x8],0x7
   0x0000000000400f6f <+44>:    ja     0x400fad <phase_3+106>
   0x0000000000400f71 <+46>:    mov    eax,DWORD PTR [rsp+0x8]
   0x0000000000400f75 <+50>:    jmp    QWORD PTR [rax*8+0x402470]
   0x0000000000400f7c <+57>:    mov    eax,0xcf
   0x0000000000400f81 <+62>:    jmp    0x400fbe <phase_3+123>
   0x0000000000400f83 <+64>:    mov    eax,0x2c3
   0x0000000000400f88 <+69>:    jmp    0x400fbe <phase_3+123>
   0x0000000000400f8a <+71>:    mov    eax,0x100
   0x0000000000400f8f <+76>:    jmp    0x400fbe <phase_3+123>
   0x0000000000400f91 <+78>:    mov    eax,0x185
   0x0000000000400f96 <+83>:    jmp    0x400fbe <phase_3+123>
   0x0000000000400f98 <+85>:    mov    eax,0xce
   0x0000000000400f9d <+90>:    jmp    0x400fbe <phase_3+123>
   0x0000000000400f9f <+92>:    mov    eax,0x2aa
   0x0000000000400fa4 <+97>:    jmp    0x400fbe <phase_3+123>
   0x0000000000400fa6 <+99>:    mov    eax,0x147
   0x0000000000400fab <+104>:   jmp    0x400fbe <phase_3+123>
   0x0000000000400fad <+106>:   call   0x40143a <explode_bomb>
   0x0000000000400fb2 <+111>:   mov    eax,0x0
   0x0000000000400fb7 <+116>:   jmp    0x400fbe <phase_3+123>
   0x0000000000400fb9 <+118>:   mov    eax,0x137
   0x0000000000400fbe <+123>:   cmp    eax,DWORD PTR [rsp+0xc]
   0x0000000000400fc2 <+127>:   je     0x400fc9 <phase_3+134>
   0x0000000000400fc4 <+129>:   call   0x40143a <explode_bomb>
   0x0000000000400fc9 <+134>:   add    rsp,0x18
   0x0000000000400fcd <+138>:   ret
```
首先，我們會在 stack 上面分配 `0x18`，也就是 24 bytes 的記憶體空間，接著，會將 `rsp + 0xc` 的值，也就是一個記憶體地址存入 `rcx`，將 `rsp + 0x8` 的值存入 `rdx`。

呼叫 `sscanf()` 將讀取進來的數字儲存到 `rsp + 0x8` 的位置，並將 `sscanf()` 的回傳值儲存在 `eax` 中，如果回傳值大於 1，則跳轉到 `phase_3+39`，否則呼叫 `explode_bomb`。

回傳值的意義為成功讀入的數字，我們嘗試輸入 `1 2 3 4 5`，並且查看 `eax` 的值，也就是成功讀入的數字
```
(gdb)$ info r eax
eax            0x2                 0x2
```
可以看到成功讀入的數字只有兩個。

接著將剛剛使用 `sscanf()` 讀取到的第一個數字和 `0x7` 進行比較，如果第一個數字比 `0x7` 還要大，則跳轉到 `explode_bomb`。

我們可以試著查看 `rsp + 0x8` 中的值
```
(gdb)$ info registers rsp
rsp            0x7fffffffdbd0      0x7fffffffdbd0
(gdb)$ print *(0x7fffffffdbd0+0x8)
$4 = 0x1
```
接著往下繼續分析，看到 `jmp    QWORD PTR [rax*8+0x402470]`，`rax` 中儲存的是我們第一個讀入的數字，而 `rax` <= 7，我們可以試著計算它的記憶體地址範圍
```
(gdb)$ x/8xg 0x402470
0x402470:       0x0000000000400f7c      0x0000000000400fb9
0x402480:       0x0000000000400f83      0x0000000000400f8a
0x402490:       0x0000000000400f91      0x0000000000400f98
0x4024a0:       0x0000000000400f9f      0x0000000000400fa6
```
概念上可以看做以下
- 如果第一個數字為 0，則跳轉到 `0x0000000000400f7c`
- 如果第一個數字為 1，則跳轉到 `0x0000000000400fb9`
- 如果第一個數字為 2，則跳轉到 `0x0000000000400f83`
- 如果第一個數字為 3，則跳轉到 `0x0000000000400f8a`
- 如果第一個數字為 4，則跳轉到 `0x0000000000400f91`
- 如果第一個數字為 5，則跳轉到 `0x0000000000400f98`
- 如果第一個數字為 6，則跳轉到 `0x0000000000400f9f`
- 如果第一個數字為 7，則跳轉到 `0x0000000000400fa6`
```asm
   0x0000000000400f7c <+57>:    mov    eax,0xcf
   0x0000000000400f81 <+62>:    jmp    0x400fbe <phase_3+123>
   0x0000000000400f83 <+64>:    mov    eax,0x2c3
   0x0000000000400f88 <+69>:    jmp    0x400fbe <phase_3+123>
   0x0000000000400f8a <+71>:    mov    eax,0x100
   0x0000000000400f8f <+76>:    jmp    0x400fbe <phase_3+123>
   0x0000000000400f91 <+78>:    mov    eax,0x185
   0x0000000000400f96 <+83>:    jmp    0x400fbe <phase_3+123>
   0x0000000000400f98 <+85>:    mov    eax,0xce
   0x0000000000400f9d <+90>:    jmp    0x400fbe <phase_3+123>
   0x0000000000400f9f <+92>:    mov    eax,0x2aa
   0x0000000000400fa4 <+97>:    jmp    0x400fbe <phase_3+123>
   0x0000000000400fa6 <+99>:    mov    eax,0x147
   0x0000000000400fab <+104>:   jmp    0x400fbe <phase_3+123>
   0x0000000000400fad <+106>:   call   0x40143a <explode_bomb>
   0x0000000000400fb2 <+111>:   mov    eax,0x0
   0x0000000000400fb7 <+116>:   jmp    0x400fbe <phase_3+123>
   0x0000000000400fb9 <+118>:   mov    eax,0x137
   0x0000000000400fbe <+123>:   cmp    eax,DWORD PTR [rsp+0xc]
   0x0000000000400fc2 <+127>:   je     0x400fc9 <phase_3+134>
   0x0000000000400fc4 <+129>:   call   0x40143a <explode_bomb>
   0x0000000000400fc9 <+134>:   add    rsp,0x18
   0x0000000000400fcd <+138>:   ret
```
根據跳轉的行為，對應到的 C 語言語法為 `switch`，可以看到每一個 case，都會跳轉到 `phase_3 + 123` 的位置，也就是 `cmp    eax,DWORD PTR [rsp+0xc]`，將 `rsp + 0xc` 記憶體地址所指向的值放入到 `eax`，也就是我們剛剛輸入的第二個數值
```
(gdb)$ info registers rsp
rsp            0x7fffffffdbd0      0x7fffffffdbd0
(gdb)$ print *(0x7fffffffdbd0 + 0xc)
$10 = 0x2
```
接下去，如果第二個數值和 `eax` 的數值相等，我們會跳轉到 `phase_3 + 134` 的位置，否則執行 `explode_bomb`，因此，我們要讓 `eax` 的值和第二個數值相等，而 `eax` 的值會在上面每一個 switch case 中變更。

如果我們輸入的第一個數字為 0，接下來我們會進入到上面 switch cace 的結構，首先我們會進入到 `mov    eax,0xcf`，接著進入到 `jmp    0x400fbe <phase_3+123>` 便跳轉，這時候 `eax` 的值為 10 進位的 207，我們這時候知道，只要我們輸入第二個數值為 207，我們就可以成功通過第三關了。

![](https://i.imgur.com/Zx80Rzn.png)

以下為 `phase_3` 反組譯後由 IDA 產生的 C 語言結果
```c
__int64 __fastcall phase_3(__int64 a1)
{
  __int64 result; // rax
  int v2; // [rsp+8h] [rbp-10h] BYREF
  int v3; // [rsp+Ch] [rbp-Ch] BYREF

  if ( (int)__isoc99_sscanf(a1, "%d %d", &v2, &v3) <= 1 )
    explode_bomb();
  switch ( v2 )
  {
    case 0:
      result = 207LL;
      break;
    case 1:
      result = 311LL;
      break;
    case 2:
      result = 707LL;
      break;
    case 3:
      result = 256LL;
      break;
    case 4:
      result = 389LL;
      break;
    case 5:
      result = 206LL;
      break;
    case 6:
      result = 682LL;
      break;
    case 7:
      result = 327LL;
      break;
    default:
      explode_bomb();
      return result;
  }
  if ( (_DWORD)result != v3 )
    explode_bomb();
  return result;
}
```
我們可以發現到其實這一題有很多答案的組合，諸如 `0 207`, `1 311` 都可以成立。
![](https://i.imgur.com/Bxu8X84.png)

## Phase 4
以下為 `phase_4` 反組譯的結果
```
   0x000000000040100c <+0>:     sub    rsp,0x18
   0x0000000000401010 <+4>:     lea    rcx,[rsp+0xc]
   0x0000000000401015 <+9>:     lea    rdx,[rsp+0x8]
   0x000000000040101a <+14>:    mov    esi,0x4025cf
   0x000000000040101f <+19>:    mov    eax,0x0
   0x0000000000401024 <+24>:    call   0x400bf0 <__isoc99_sscanf@plt>
   0x0000000000401029 <+29>:    cmp    eax,0x2
   0x000000000040102c <+32>:    jne    0x401035 <phase_4+41>
   0x000000000040102e <+34>:    cmp    DWORD PTR [rsp+0x8],0xe
   0x0000000000401033 <+39>:    jbe    0x40103a <phase_4+46>
   0x0000000000401035 <+41>:    call   0x40143a <explode_bomb>
   0x000000000040103a <+46>:    mov    edx,0xe
   0x000000000040103f <+51>:    mov    esi,0x0
   0x0000000000401044 <+56>:    mov    edi,DWORD PTR [rsp+0x8]
   0x0000000000401048 <+60>:    call   0x400fce <func4>
   0x000000000040104d <+65>:    test   eax,eax
   0x000000000040104f <+67>:    jne    0x401058 <phase_4+76>
   0x0000000000401051 <+69>:    cmp    DWORD PTR [rsp+0xc],0x0
   0x0000000000401056 <+74>:    je     0x40105d <phase_4+81>
   0x0000000000401058 <+76>:    call   0x40143a <explode_bomb>
   0x000000000040105d <+81>:    add    rsp,0x18
   0x0000000000401061 <+85>:    ret
```
首先會移動 `rsp` 暫存器，在 stack 中開出 24 bytes 的空間，接著將 `esi` 以及 `eax` 暫存器進行賦值，接著呼叫 `sscanf()`，將讀入的數字存入到 stack 中，回傳值由 `eax` 進行接收，由 `cmp    eax,0x2` 可以判斷，`sscanf()` 會成功接收到 2 個格式化數字，如果沒有成功，則會呼叫 `explode_bomb`。

下面會將輸入的第一個數值和 `0xe` 進行比較，接著來到 `jbe`，如果小於等於 `0xe`，就會跳轉到 `phase_4+46`。

下面呼叫 `func4`，傳入的參數為 `edi` 和 `esi`，`edi` 為我們輸入的第一個數字 (由 `mov    edi,DWORD PTR [rsp+0x8]` 得知)，我們試著反組譯 `func4`
```
   0x0000000000400fce <+0>:     sub    rsp,0x8
   0x0000000000400fd2 <+4>:     mov    eax,edx
   0x0000000000400fd4 <+6>:     sub    eax,esi
   0x0000000000400fd6 <+8>:     mov    ecx,eax
   0x0000000000400fd8 <+10>:    shr    ecx,0x1f
   0x0000000000400fdb <+13>:    add    eax,ecx
   0x0000000000400fdd <+15>:    sar    eax,1
   0x0000000000400fdf <+17>:    lea    ecx,[rax+rsi*1]
   0x0000000000400fe2 <+20>:    cmp    ecx,edi
   0x0000000000400fe4 <+22>:    jle    0x400ff2 <func4+36>
   0x0000000000400fe6 <+24>:    lea    edx,[rcx-0x1]
   0x0000000000400fe9 <+27>:    call   0x400fce <func4>
   0x0000000000400fee <+32>:    add    eax,eax
   0x0000000000400ff0 <+34>:    jmp    0x401007 <func4+57>
   0x0000000000400ff2 <+36>:    mov    eax,0x0
   0x0000000000400ff7 <+41>:    cmp    ecx,edi
   0x0000000000400ff9 <+43>:    jge    0x401007 <func4+57>
   0x0000000000400ffb <+45>:    lea    esi,[rcx+0x1]
   0x0000000000400ffe <+48>:    call   0x400fce <func4>
   0x0000000000401003 <+53>:    lea    eax,[rax+rax*1+0x1]
   0x0000000000401007 <+57>:    add    rsp,0x8
   0x000000000040100b <+61>:    ret
```
先看到以下
```
   0x0000000000400fce <+0>:     sub    rsp,0x8
   0x0000000000400fd2 <+4>:     mov    eax,edx
   0x0000000000400fd4 <+6>:     sub    eax,esi
   0x0000000000400fd6 <+8>:     mov    ecx,eax
   0x0000000000400fd8 <+10>:    shr    ecx,0x1f
   0x0000000000400fdb <+13>:    add    eax,ecx
   0x0000000000400fdd <+15>:    sar    eax,1
   0x0000000000400fdf <+17>:    lea    ecx,[rax+rsi*1]
   0x0000000000400fe2 <+20>:    cmp    ecx,edi
   0x0000000000400fe4 <+22>:    jle    0x400ff2 <func4+36>
```
使用 gdb 逐行執行，會發現到，執行到 `cmp ecx edi` 這一行時，`ecx` 的值為 `0x7`，`edi` 為我們輸入的第一個參數，因此到這裡我們知道，我們輸入 `7 0` 即可通過 `phase_4`，接著往下看，我們會到看再次回到 `phase_4` 一開始的地方，這是遞迴呼叫，接著往下繼續執行，我們會發現到這一題答案沒有唯一的解，使用 gdb 可以觀察到

```
─────────────────────────────────────────────────────────────────────── Code ───────────────────────────────────────────────────────────────────────
   0x400fdb <func4+13>: add    eax,ecx
   0x400fdd <func4+15>: sar    eax,1
   0x400fdf <func4+17>: lea    ecx,[rax+rsi*1]
=> 0x400fe2 <func4+20>: cmp    ecx,edi
   0x400fe4 <func4+22>: jle    0x400ff2 <func4+36>
   0x400fe6 <func4+24>: lea    edx,[rcx-0x1]
   0x400fe9 <func4+27>: call   0x400fce <func4>
   0x400fee <func4+32>: add    eax,eax
────────────────────────────────────────────────────────────────────── Stack ───────────────────────────────────────────────────────────────────────
0000| 0x7fffffffdaf0 --> 0x0
0008| 0x7fffffffdaf8 --> 0x400fee (<func4+32>:  add    eax,eax)
0016| 0x7fffffffdb00 --> 0x7fffffffdc20 --> 0x1
0024| 0x7fffffffdb08 --> 0x40104d (<phase_4+65>:        test   eax,eax)
0032| 0x7fffffffdb10 --> 0x402210 (<__libc_csu_init>:   mov    QWORD PTR [rsp-0x28],rbp)
0040| 0x7fffffffdb18 --> 0x200000001
0048| 0x7fffffffdb20 --> 0x0
0056| 0x7fffffffdb28 --> 0x400e93 (<main+243>:  call   0x4015c4 <phase_defused>)
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Legend: code, data, rodata, heap, value
0x0000000000400fe2 in func4 ()
gdb-peda$ info register ecx
ecx            0x3                 0x3
gdb-peda$
```

```
─────────────────────────────────────────────────────────────────────── Code ───────────────────────────────────────────────────────────────────────
   0x400fdd <func4+15>: sar    eax,1
   0x400fdf <func4+17>: lea    ecx,[rax+rsi*1]
   0x400fe2 <func4+20>: cmp    ecx,edi
=> 0x400fe4 <func4+22>: jle    0x400ff2 <func4+36>
 │ 0x400fe6 <func4+24>: lea    edx,[rcx-0x1]
 │ 0x400fe9 <func4+27>: call   0x400fce <func4>
 │ 0x400fee <func4+32>: add    eax,eax
 │ 0x400ff0 <func4+34>: jmp    0x401007 <func4+57>
 └─>   0x400ff2 <func4+36>:     mov    eax,0x0
       0x400ff7 <func4+41>:     cmp    ecx,edi
       0x400ff9 <func4+43>:     jge    0x401007 <func4+57>
       0x400ffb <func4+45>:     lea    esi,[rcx+0x1]
                                                                  JUMP is taken
────────────────────────────────────────────────────────────────────── Stack ───────────────────────────────────────────────────────────────────────
0000| 0x7fffffffdae0 --> 0xe
0008| 0x7fffffffdae8 --> 0x400fee (<func4+32>:  add    eax,eax)
0016| 0x7fffffffdaf0 --> 0x0
0024| 0x7fffffffdaf8 --> 0x400fee (<func4+32>:  add    eax,eax)
0032| 0x7fffffffdb00 --> 0x7fffffffdc20 --> 0x1
0040| 0x7fffffffdb08 --> 0x40104d (<phase_4+65>:        test   eax,eax)
0048| 0x7fffffffdb10 --> 0x402210 (<__libc_csu_init>:   mov    QWORD PTR [rsp-0x28],rbp)
0056| 0x7fffffffdb18 --> 0x200000001
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Legend: code, data, rodata, heap, value
0x0000000000400fe4 in func4 ()
gdb-peda$ info register ecx
ecx            0x1                 0x1
gdb-peda$
```
推測出本題有三個答案，分別為 `7 0`, `3 0`, `1 0`。

## Phase 5
首先將 `phase_5` 進行反組譯
```asm
   0x0000000000401062 <+0>:     push   rbx
   0x0000000000401063 <+1>:     sub    rsp,0x20
   0x0000000000401067 <+5>:     mov    rbx,rdi
   0x000000000040106a <+8>:     mov    rax,QWORD PTR fs:0x28
   0x0000000000401073 <+17>:    mov    QWORD PTR [rsp+0x18],rax
   0x0000000000401078 <+22>:    xor    eax,eax
   0x000000000040107a <+24>:    call   0x40131b <string_length>
   0x000000000040107f <+29>:    cmp    eax,0x6
   0x0000000000401082 <+32>:    je     0x4010d2 <phase_5+112>
   0x0000000000401084 <+34>:    call   0x40143a <explode_bomb>
   0x0000000000401089 <+39>:    jmp    0x4010d2 <phase_5+112>
   0x000000000040108b <+41>:    movzx  ecx,BYTE PTR [rbx+rax*1]
   0x000000000040108f <+45>:    mov    BYTE PTR [rsp],cl
   0x0000000000401092 <+48>:    mov    rdx,QWORD PTR [rsp]
   0x0000000000401096 <+52>:    and    edx,0xf
   0x0000000000401099 <+55>:    movzx  edx,BYTE PTR [rdx+0x4024b0]
   0x00000000004010a0 <+62>:    mov    BYTE PTR [rsp+rax*1+0x10],dl
   0x00000000004010a4 <+66>:    add    rax,0x1
   0x00000000004010a8 <+70>:    cmp    rax,0x6
   0x00000000004010ac <+74>:    jne    0x40108b <phase_5+41>
   0x00000000004010ae <+76>:    mov    BYTE PTR [rsp+0x16],0x0
   0x00000000004010b3 <+81>:    mov    esi,0x40245e
   0x00000000004010b8 <+86>:    lea    rdi,[rsp+0x10]
   0x00000000004010bd <+91>:    call   0x401338 <strings_not_equal>
   0x00000000004010c2 <+96>:    test   eax,eax
   0x00000000004010c4 <+98>:    je     0x4010d9 <phase_5+119>
   0x00000000004010c6 <+100>:   call   0x40143a <explode_bomb>
   0x00000000004010cb <+105>:   nop    DWORD PTR [rax+rax*1+0x0]
   0x00000000004010d0 <+110>:   jmp    0x4010d9 <phase_5+119>
   0x00000000004010d2 <+112>:   mov    eax,0x0
   0x00000000004010d7 <+117>:   jmp    0x40108b <phase_5+41>
   0x00000000004010d9 <+119>:   mov    rax,QWORD PTR [rsp+0x18]
   0x00000000004010de <+124>:   xor    rax,QWORD PTR fs:0x28
   0x00000000004010e7 <+133>:   je     0x4010ee <phase_5+140>
   0x00000000004010e9 <+135>:   call   0x400b30 <__stack_chk_fail@plt>
   0x00000000004010ee <+140>:   add    rsp,0x20
   0x00000000004010f2 <+144>:   pop    rbx
   0x00000000004010f3 <+145>:   ret
```
可以看到前面呼叫 `string_length` 這一個函式，接著回傳值會和 `0x6` 進行比較，也就是預期接收的字串長度為 6，我們輸入 `abcdef`

接著往下看
```asm
   0x000000000040108b <+41>:    movzx  ecx,BYTE PTR [rbx+rax*1]
   0x000000000040108f <+45>:    mov    BYTE PTR [rsp],cl
   0x0000000000401092 <+48>:    mov    rdx,QWORD PTR [rsp]
   0x0000000000401096 <+52>:    and    edx,0xf
   0x0000000000401099 <+55>:    movzx  edx,BYTE PTR [rdx+0x4024b0]
   0x00000000004010a0 <+62>:    mov    BYTE PTR [rsp+rax*1+0x10],dl
   0x00000000004010a4 <+66>:    add    rax,0x1
   0x00000000004010a8 <+70>:    cmp    rax,0x6
   0x00000000004010ac <+74>:    jne    0x40108b <phase_5+41>
``` 
這是一個迴圈的結構，`rax` 從 0 一路加到 5，接著從我們輸入的 `input[]`，從 `input[0]` 一路存取到 `input[5]`，先將值存入 `ecx` 中，接著我們通過 and `0xf` 的操作取出低位的部分。

接著將 `0x4024b0 + rdx ` 當作記憶體地址，讀取該記憶體地址內 1 bytes，拓展成 4 bytes 後儲存到 `edx` (從這裡可以判斷出，我們輸入的字串會被轉換成 index，用於存取 `0x4024b0` 所指向的內容)，接著取出 `dl` 的內容儲存到 `rsp+rax*1+0x10`，整個操作完成後，接著往下看
```asm
   0x00000000004010ae <+76>:    mov    BYTE PTR [rsp+0x16],0x0
   0x00000000004010b3 <+81>:    mov    esi,0x40245e
   0x00000000004010b8 <+86>:    lea    rdi,[rsp+0x10]
   0x00000000004010bd <+91>:    call   0x401338 <strings_not_equal>
   0x00000000004010c2 <+96>:    test   eax,eax
   0x00000000004010c4 <+98>:    je     0x4010d9 <phase_5+119>
```
`string_not_equal` 判斷為比較兩個字串的函式，參數位於 `rdi` 和 `rsi` 中，我們嘗試印出 `rsi` , `rdi` 地址指向的內容
```
gdb-peda$ print (char *)$esi
$44 = 0x40245e "flyers"
gdb-peda$ print (char *)$rdi
$45 = 0x7fffffffdb40 "aduier"
gdb-peda$
```
前面我們將 `rdi` 加上 `0x4024b0` 這個地址，我們查看其內容
```
gdb-peda$ print (char *)0x4024b0
$47 = 0x4024b0 <array> "maduiersnfotvbylSo you think you can stop the bomb with ctrl-c, do you?"
```
我們發現到，`aduier` 為 `0x4024b0` 指向的字串中某一部份的連續子字串，因此我們判斷，我們輸入的 `input[]` 會經過上面的轉換，變成 index，而這個 index 用於存取 `0x4024b0` 指向的字串，而我們又知道我們比較的目標字串為 `flyers`，因此，只要我們構造出一段字串，轉換成 index 能夠指向到 f, l, y, e, r, s，本題便結束了

```
       maduiersnfotvbylSo you think you can stop the bomb with ctrl-c, do you?
ASCII:  abcdefghijklmnopqrstuvwxyz...
```
對應到的輸入為 `ionefg`

## Phase 6
```asm
   0x00000000004010f4 <+0>:     push   r14
   0x00000000004010f6 <+2>:     push   r13
   0x00000000004010f8 <+4>:     push   r12
   0x00000000004010fa <+6>:     push   rbp
   0x00000000004010fb <+7>:     push   rbx
   0x00000000004010fc <+8>:     sub    rsp,0x50
   0x0000000000401100 <+12>:    mov    r13,rsp
   0x0000000000401103 <+15>:    mov    rsi,rsp
   0x0000000000401106 <+18>:    call   0x40145c <read_six_numbers>
   0x000000000040110b <+23>:    mov    r14,rsp
   0x000000000040110e <+26>:    mov    r12d,0x0
   0x0000000000401114 <+32>:    mov    rbp,r13
   0x0000000000401117 <+35>:    mov    eax,DWORD PTR [r13+0x0]
   0x000000000040111b <+39>:    sub    eax,0x1
   0x000000000040111e <+42>:    cmp    eax,0x5
   0x0000000000401121 <+45>:    jbe    0x401128 <phase_6+52>
   0x0000000000401123 <+47>:    call   0x40143a <explode_bomb>
   0x0000000000401128 <+52>:    add    r12d,0x1
   0x000000000040112c <+56>:    cmp    r12d,0x6
   0x0000000000401130 <+60>:    je     0x401153 <phase_6+95>
   0x0000000000401132 <+62>:    mov    ebx,r12d
   0x0000000000401135 <+65>:    movsxd rax,ebx
   0x0000000000401138 <+68>:    mov    eax,DWORD PTR [rsp+rax*4]
   0x000000000040113b <+71>:    cmp    DWORD PTR [rbp+0x0],eax
   0x000000000040113e <+74>:    jne    0x401145 <phase_6+81>
   0x0000000000401140 <+76>:    call   0x40143a <explode_bomb>
   0x0000000000401145 <+81>:    add    ebx,0x1
   0x0000000000401148 <+84>:    cmp    ebx,0x5
   0x000000000040114b <+87>:    jle    0x401135 <phase_6+65>
   0x000000000040114d <+89>:    add    r13,0x4
   0x0000000000401151 <+93>:    jmp    0x401114 <phase_6+32>
   0x0000000000401153 <+95>:    lea    rsi,[rsp+0x18]
   0x0000000000401158 <+100>:   mov    rax,r14
   0x000000000040115b <+103>:   mov    ecx,0x7
   0x0000000000401160 <+108>:   mov    edx,ecx
   0x0000000000401162 <+110>:   sub    edx,DWORD PTR [rax]
   0x0000000000401164 <+112>:   mov    DWORD PTR [rax],edx
   0x0000000000401166 <+114>:   add    rax,0x4
   0x000000000040116a <+118>:   cmp    rax,rsi
   0x000000000040116d <+121>:   jne    0x401160 <phase_6+108>
   0x000000000040116f <+123>:   mov    esi,0x0
   0x0000000000401174 <+128>:   jmp    0x401197 <phase_6+163>
   0x0000000000401176 <+130>:   mov    rdx,QWORD PTR [rdx+0x8]
   0x000000000040117a <+134>:   add    eax,0x1
   0x000000000040117d <+137>:   cmp    eax,ecx
   0x000000000040117f <+139>:   jne    0x401176 <phase_6+130>
   0x0000000000401181 <+141>:   jmp    0x401188 <phase_6+148>
   0x0000000000401183 <+143>:   mov    edx,0x6032d0
   0x0000000000401188 <+148>:   mov    QWORD PTR [rsp+rsi*2+0x20],rdx
   0x000000000040118d <+153>:   add    rsi,0x4
   0x0000000000401191 <+157>:   cmp    rsi,0x18
   0x0000000000401195 <+161>:   je     0x4011ab <phase_6+183>
   0x0000000000401197 <+163>:   mov    ecx,DWORD PTR [rsp+rsi*1]
   0x000000000040119a <+166>:   cmp    ecx,0x1
   0x000000000040119d <+169>:   jle    0x401183 <phase_6+143>
   0x000000000040119f <+171>:   mov    eax,0x1
   0x00000000004011a4 <+176>:   mov    edx,0x6032d0
   0x00000000004011a9 <+181>:   jmp    0x401176 <phase_6+130>
   0x00000000004011ab <+183>:   mov    rbx,QWORD PTR [rsp+0x20]
   0x00000000004011b0 <+188>:   lea    rax,[rsp+0x28]
   0x00000000004011b5 <+193>:   lea    rsi,[rsp+0x50]
   0x00000000004011ba <+198>:   mov    rcx,rbx
   0x00000000004011bd <+201>:   mov    rdx,QWORD PTR [rax]
   0x00000000004011c0 <+204>:   mov    QWORD PTR [rcx+0x8],rdx
   0x00000000004011c4 <+208>:   add    rax,0x8
   0x00000000004011c8 <+212>:   cmp    rax,rsi
   0x00000000004011cb <+215>:   je     0x4011d2 <phase_6+222>
   0x00000000004011cd <+217>:   mov    rcx,rdx
   0x00000000004011d0 <+220>:   jmp    0x4011bd <phase_6+201>
   0x00000000004011d2 <+222>:   mov    QWORD PTR [rdx+0x8],0x0
   0x00000000004011da <+230>:   mov    ebp,0x5
   0x00000000004011df <+235>:   mov    rax,QWORD PTR [rbx+0x8]
   0x00000000004011e3 <+239>:   mov    eax,DWORD PTR [rax]
   0x00000000004011e5 <+241>:   cmp    DWORD PTR [rbx],eax
   0x00000000004011e7 <+243>:   jge    0x4011ee <phase_6+250>
   0x00000000004011e9 <+245>:   call   0x40143a <explode_bomb>
   0x00000000004011ee <+250>:   mov    rbx,QWORD PTR [rbx+0x8]
   0x00000000004011f2 <+254>:   sub    ebp,0x1
   0x00000000004011f5 <+257>:   jne    0x4011df <phase_6+235>
   0x00000000004011f7 <+259>:   add    rsp,0x50
   0x00000000004011fb <+263>:   pop    rbx
   0x00000000004011fc <+264>:   pop    rbp
   0x00000000004011fd <+265>:   pop    r12
   0x00000000004011ff <+267>:   pop    r13
   0x0000000000401201 <+269>:   pop    r14
   0x0000000000401203 <+271>:   ret
```
首先可以看到會讀入 6 個數字，接著看到第一個部分
```asm
   0x000000000040110b <+23>:    mov    r14,rsp
   0x000000000040110e <+26>:    mov    r12d,0x0
   0x0000000000401114 <+32>:    mov    rbp,r13
   0x0000000000401117 <+35>:    mov    eax,DWORD PTR [r13+0x0]
   0x000000000040111b <+39>:    sub    eax,0x1
   0x000000000040111e <+42>:    cmp    eax,0x5
   0x0000000000401121 <+45>:    jbe    0x401128 <phase_6+52>
   0x0000000000401123 <+47>:    call   0x40143a <explode_bomb>
   0x0000000000401128 <+52>:    add    r12d,0x1
   0x000000000040112c <+56>:    cmp    r12d,0x6
   0x0000000000401130 <+60>:    je     0x401153 <phase_6+95>
   0x0000000000401132 <+62>:    mov    ebx,r12d
   0x0000000000401135 <+65>:    movsxd rax,ebx
   0x0000000000401138 <+68>:    mov    eax,DWORD PTR [rsp+rax*4]
   0x000000000040113b <+71>:    cmp    DWORD PTR [rbp+0x0],eax
   0x000000000040113e <+74>:    jne    0x401145 <phase_6+81>
   0x0000000000401140 <+76>:    call   0x40143a <explode_bomb>
   0x0000000000401145 <+81>:    add    ebx,0x1
   0x0000000000401148 <+84>:    cmp    ebx,0x5
   0x000000000040114b <+87>:    jle    0x401135 <phase_6+65>
   0x000000000040114d <+89>:    add    r13,0x4
   0x0000000000401151 <+93>:    jmp    0x401114 <phase_6+32>
```
首先會將 `rsp` 指向的元素，也就是輸入的第一個元素儲存到 `r14` 暫存器中。

`r13` 的值存到 `rbp` 中，`r13` 中的值為 `rsp`。

`r13` 的值存到 `eax` 中，並將 `eax` 的值 -1，和 5 進行比較，如果發現大於，則 `explode_bomb`，也就是說，輸入的第一個元素需要小於 6。

接著 `r12d` +1，並和 6 進行比較，接著 `r12d` 的值儲存到 `rbx` 中