# NCtfU - 10/12 (Reverse)(A204)
###### tags: `新手場` `nctfu2022fall`
:::success
:::spoiler 目錄
[toc]
:::
:::info
[簡報連結](https://speakerdeck.com/terrynini/cheng-gong-gao-zhong-jiang-yi)
[直播連結](https://youtu.be/RGrblEmUzlg)
[錄影連結](https://www.youtube.com/watch?v=OvAG7V1UUvI)
[Slido](https://app.sli.do/event/8AC7pxgapD3fShni2vzizc)
:::
## Lab 連結
[reverse lab.zip](https://drive.google.com/drive/folders/1NW8rKibP7aUks64TOLiOgg9r1bBNyHiW?usp=sharing)
## 共筆
- GCC
GCC(GNU Compiler Collection)
- 執行檔怎麼來的
```bash
Windows PowerShell 或 cmd 或 linux terminal
前處理
gcc -E HelloWorld.c -o HelloWorld.i
編譯
gcc -S HelloWorld.c -o HelloWorld.s
組譯
gcc -c HelloWorld.c -o HelloWorld.o
完成
gcc HelloWorld.c -o HelloWorld
優化(O0 ~ O3。0為不優化,3為所有優化)
gcc -O3 HelloWorld.c -o HelloWorld
編譯為 32bit 要加-m32
```
- intel vs AT&T 表示法
- intel 目標暫存器在前來源暫存器在後,AT&T 相反
- AT&T 暫存器前有 % 常數前有 $,intel 沒有
### 組合語言型別
- RISC vs CISC
- CISC: x86, ...
- RISC: ARM, MIPS, ...
### 型態
```assambly
BYTE (8 bit)
WORD (16bit)
DWORD(32bit)
QWORD(64bit)
```
:::info
[Note] 1 Byte = 8 bit = 16進位的2位數
例如:0xAB = (1010 1011)~b~ 為8bit → 1 Byte
:::
### 暫存器
- RAX(64bit) EAX(32bit) AX(16bit) AH AL(8bit)
- RAX, RBX, RCX, RDX 通用型暫存器
- ESP (指向stack頂部) EBP (指向stack底部)
- EDI, ESI 陣列索引使用
- EIP 下一個指令執行位置
* EAX
* AX(後16bit)
* AH(前8bit)
* AL (後8bit)
* EBX
* BX(後16bit)
* BH(前8bit)
* BL (後8bit)
* ECX(可決定迴圈結束)
* CX(後16bit)
* CH(前8bit)
* CL (後8bit)
* EDX
* DX(後16bit)
* DH(前8bit)
* DL (後8bit)
* ESI(可用於陣列位置)
* EDI(可用於陣列位置)
* ESP(指向STACK頂部)
* EBP(指向STACK底部)
* EIP(下一指令,一般不可手動設定 ~~但如果BOF...~~)
### Little endian
數字高(低)位,放記憶體編號較大(小)的位置
eg. AB CD EF 10 -> 0x10EFCDAB
反之則為 big endian
### Assembly
- add/sub/mul/div 四則運算
- inc/dec 遞增遞減
- e.g. `inc eax` (eax++)
- mov 賦值
- e.g. `mov eax, ebx` (eax = ebx)
- lea 計算記憶體位置
- e.g. ESI = 0xffffcf35
- `lea EAX, DWORD PTR [ESI + 4]`
- EAX = 0xffffcf39
- and/or/not/xor logic gate(位元運算,數字的每個位元都分別拿來算)
- 舉例:`1010 and 1100 == 1000`
- `xor EAX, EAX` == `mov EAX, 0`
- 一般來說會用前者方式清零,因暫存器運算會比記憶體取值來的快很多
- neg 取負數(2補數, 取一補數補上1)
- 101001 -> (取一補數) -> 010110 -> (+1) -> 010111
- test 同 and,但不會將結果儲存 (但是會影響旗標暫存器 EFLAGS)
- cmp 就compare
- cmp eax, ebx
| ZF | CF | Result |
| -------- | -------- | -------- |
| False | True | eax < ebx |
| False | False | eax > ebx |
| True | False | eax == ebx |
### 各種jump
| 符號 | 解釋 |
| ---- | ------------------------ |
| J | Jump if(如果...則跳轉) |
| N | Not(不是) |
| A | Above(無號整數:前>後) |
| B | Below(無號整數:前<後) |
| G | Greater(有號整數:前>後) |
| L | Less(有號整數:前<後) |
| E | Equal(前=後) |
如果括號內成立,則跳轉
unsigned int
```
JA /JNBE(>)
JAE/JNB (>=)
JB /JNAE(<)
JBE/JNA (<=)
```
signed int
```
JG /JNLE(>)
JGE/JNL (>=)
JL /JNGE(<)
JLE/JNG (<=)
```
other
```
JE (==)
JNE(!=)
JMP(True)
```
舉例:jle 如果上一行結果為(前<=後),則跳轉
```assembly=
mov eax, 1
mov eax, 2
cmp eax, ebx
jle target ; jump to target if eax <= ebx
target:
mov eax 1
```
等同
```cpp=
int eax = 1,
ebx = 2;
if(eax <= ebx)
goto target;// 直接去target所在行數
target:
eax = 1;
```
||https://i.redd.it/osoqocy36za71.jpg||
其他相關判斷指令:
- jl: <
- jge: >=
- jg: >
- jz/je: ==
- jnz/jne: !=
> 以上是有號數,無號數的話就是把 g 改 a、l 改 b
### EFLAGS
>注意:mov不會影響Flag
- OF 判斷當作有號數時,運算後有沒有 overflow
- SF 數字是不是負數
- ZF 運算後值是否為 0
- CF 當作無號數時,運算後是否~~進位~~overflow
### Analyze
- static 靜態: objdump, Ghidra(這好像也可以算dynamic analyze?)
- dynamic 動態: gdb, x64DBG
### Calling convention
- 非強制,視 compiler 廠商設定
- 一般來說,call function 時,參數右至左 push 進 stack
- push: 放到stack頂部 e.g. `push rbp` 將rbp push到stack頂部(記憶體低位)
- pop: 取出stack頂部 e.g. `pop rbp` 將最頂端的東西移至rbp
- stack由記憶體高位往低位長
- caller: call function 的
- callee: 被 call 的
- leave: `mov esp, ebp; pop ebp`
- ret: `pop eip`
### 記憶體中的stack
Virtual Address Space 結構 (非實際上實體記憶體位置)
(高位)OS → stack → HEAP → BSS(未初始化) → DATA(初始化) → TEXT(低位)
| structure |
| --------- |
| OS(high) |
| stack |
| |
| |
| |
| |
| heap |
| bss |
| data |
| text(low) |
call function時:
```assembly
call func ; 呼叫函數func (push eip; jmp func)
; function prologue
push ebp ; 呼叫函數前,此時stack會用來執行函數
mov ebp, esp
; function裡的東西
leave ;回復call前的esp ebp (mov esp, ebp; pop ebp),stack還給外面
ret ;將eip設為call前的下一個instruction (pop eip)
```
:::danger
當stack撞到HEAP(例如過多次的遞迴造成),程式就會崩潰 (stack overflow)
> linux 的 process 上 ,每個 segment 都有各自大小,一個 segment 會分配給 stack,如果存取地址上不存在 segment 或沒有權限,就會發生 Segmentation fault
> [name=r8]
如果程式沒有限制寫入大小(`gets()`)便**有機會**能控制eip <- 這邊應該是指 buffer overflow? 跟 stack overflow 不太一樣 <- Y
:::