# 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;
}
```

## 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。

## 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,我們就可以成功通過第三關了。

以下為 `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` 都可以成立。

## 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` 中