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