前言:什麼是漏洞利用開發?
在開始學習技術細節之前,讓我們先了解一些基本概念:
漏洞利用開發(Exploit Development) 是資訊安全領域中一個專門的技術方向,主要目標是:
- 發現軟體中的安全漏洞
- 理解這些漏洞如何被利用
- 開發程式碼來「觸發」並「利用」這些漏洞
- 協助開發者修補這些安全問題
為什麼要學習漏洞利用開發?
- 防守方觀點:了解攻擊手法才能設計更好的防禦機制
- 紅隊演練:滲透測試需要實際的漏洞利用能力
- 研究目的:發現新的漏洞並負責任地揭露
- 職涯發展:資安研究員、滲透測試工程師的核心技能
學習本文需要的前置知識
- 基本的程式設計概念(變數、函式、迴圈等)
- 對作業系統有初步了解
- 願意學習低階程式設計概念的心態
Windows 系統架構基礎
系統啟動流程:從開機到作業系統
了解 Windows 如何啟動,對於理解記憶體佈局非常重要:
[開機 ROM] → [Boot Loader] → [OS Kernel] → [User Land]
詳細流程:
- Boot ROM(開機 ROM)
- 電腦開機時,首先執行儲存在 ROM 中的程式碼
- 負責載入 Boot Loader(開機載入器)
- Boot Loader(開機載入器)
- 將作業系統載入到 RAM(隨機存取記憶體)中
- 準備系統執行環境
- Kernel(核心) 啟動後執行以下工作:
- 管理記憶體配置
- 初始化各種核心元件
- 建立 User Land(使用者空間)
- User Land(使用者空間)
- 允許執行應用程式
- 「桌面」就是最早啟動的 User Land 應用程式之一
重要觀念:「所有程式都在記憶體中執行」— 這是漏洞利用的核心概念!
Ring 權限等級:核心模式 vs 使用者模式
Windows 採用 Ring(環) 的概念來區分程式的執行權限:
┌─────────────────────────────────────────────────────┐
│ User Land │
│ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ │
│ │程式1│ │程式2│ │程式3│ │程式4│ ← Ring 3 │
│ └──┬──┘ └──┬──┘ └──┬──┘ └──┬──┘ │
│ │ │ │ │ │
│ ▼ ▼ ▼ ▼ │
│ ┌────────────────────────────────┐ │
│ │ System Calls (ntdll.dll) │ │
│ └────────────────────────────────┘ │
├─────────────────────────────────────────────────────┤
│ Kernel │
│ ┌─────────────────┐ ┌──────────────────────────┐ │
│ │ Windows Executive│ │ Low Level OS Functions │ │
│ │ - 記憶體管理 │ │ - 排程 │ │
│ │ - 行程管理 │ │ - 中斷處理 │ │
│ │ - 執行緒管理 │ │ - 例外處理 │ │
│ │ - I/O │ │ - 多處理器同步 │ │
│ │ - 網路 │ └──────────────────────────┘ │
│ │ - IPC │ │
│ └─────────────────┘ ← Ring 0 │
│ ┌─────────────────────────────────────────────┐ │
│ │ HAL │ │
│ │ (Hardware Abstraction Layer) │ │
│ └─────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────┤
│ ┌─────┐ ┌────────────┐ ┌──────────┐ ┌─────┐ │
│ │ CPU │ │ 實體記憶體 │ │ 硬碟儲存 │ │ NIC │ │
│ └─────┘ └────────────┘ └──────────┘ └─────┘ │
│ Hardware │
└─────────────────────────────────────────────────────┘
Ring 3(使用者模式):
– 一般應用程式執行的空間
– 權限受限,無法直接存取硬體
– 需要透過 System Call(系統呼叫)來請求核心服務
Ring 0(核心模式):
– 作業系統核心執行的空間
– 擁有最高權限,可直接存取硬體
– 包含驅動程式、核心元件
資安觀點:漏洞利用的終極目標之一就是「權限提升」— 從 Ring 3 提升到 Ring 0
Windows 初始系統進程
當 Windows 啟動時,會依序建立以下系統進程:
| 進程名稱 | 說明 | 重要性 |
|---|---|---|
| System Idle Process | 系統閒置進程 | – |
| System Process (PID 4) | 系統進程(核心執行緒) | 非真正的使用者模式程式 |
| smss.exe | Session Manager(工作階段管理員) | 第一個使用者模式進程 ⭐ |
| lsm.exe | Local Session Manager | – |
| csrss.exe | Windows Subsystem | 關鍵系統進程 |
| wininit.exe | Session 0 初始化 | 關鍵系統進程 |
| winlogon.exe | 登入進程 | 關鍵系統進程 |
| services.exe | Service Control Manager | 管理 svchost.exe 子進程 |
| lsass.exe | Local Security Authentication Server | 認證服務 |
標示為「關鍵」的進程:如果被終止,系統會當機或重新啟動
記憶體管理深入解析
虛擬記憶體:為什麼重要?
Windows 使用 虛擬記憶體(Virtual Memory) 系統,這是理解漏洞利用的關鍵:
核心概念:
- 實體記憶體(Physical Memory)
- 實際安裝的 RAM
- 被分割成「頁框(Page Frames)」
- 虛擬記憶體(Virtual Memory)
- 每個進程「看到」的記憶體空間
- 被分割成「頁面(Pages)」
- 比實體記憶體大很多!
- 頁面表(Page Table)
- 負責將虛擬位址轉換成實體位址
- 每個進程都有自己的頁面表
┌─────────────────────┐ ┌─────────────────────┐
│ 虛擬記憶體(頁面) │ │ 實體記憶體(頁框) │
│ │ │ │
│ 0x00010000 ────────┼──────────┼→ 頁框 A │
│ 0x00020000 │ │ │
│ 0x00030000 ────────┼──────┐ │ 頁框 B │
│ ... │ │ │ │
│ 0x7fffffff ────────┼──┐ │ │ 頁框 C ←──────────┼─┐
│ │ │ │ │ │ │
└─────────────────────┘ │ │ │ 頁框 D ←──────────┼─┘
│ │ │ │
│ └───┼→ 頁框 E │
│ │ │
└───────┼→ 頁框 F │
│ │
│ ┌─────────┐ │
│ │ Disk │←───────│
│ │ (Swap) │ 交換 │
│ └─────────┘ │
└─────────────────────┘
頁面(Page)的重要特性
| 特性 | 說明 |
|---|---|
| 固定大小 | x86 預設為 4KB(可透過 PAE 設為 2MB) |
| ACL 控制 | 每個頁面都有存取控制(讀/寫/執行) |
| 交換機制 | 不常用的頁面可被「交換」到硬碟 |
| 獨立性 | 不同進程的頁面互相隔離 |
資安觀點:頁面的執行權限(是否可執行程式碼)是現代防禦機制的基礎!
頁面查詢機制
當 CPU 需要存取某個虛擬位址時:
- TLB(Translation Lookaside Buffer):快取查詢
- 如果找到 → 直接使用
- Page Walk(頁面遍歷):完整查詢
- 如果 TLB 沒有 → 查詢頁面表
- 找到後加入 TLB 快取
過多交換 = 「Thrashing(顛簸)」
當系統記憶體不足時:
– 大量頁面被交換到硬碟
– 系統變得極慢
– 這就是為什麼增加 RAM 可以改善效能
Win32 進程與執行緒
進程(Process)是什麼?
進程 是程式執行時的「容器」:
┌─────────────────────────────────────────────────────────┐
│ 進程 (Process) │
│ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ 私有虛擬位址空間 │ │
│ │ (0x00010000 - 0x7FFFFFFF) │ │
│ │ │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ .text │ │ .data │ │ Stack │ │ │
│ │ │ (程式碼)│ │ (資料) │ │ (堆疊) │ │ │
│ │ └─────────┘ └─────────┘ └─────────┘ │ │
│ │ │ │
│ │ ┌─────────┐ ┌─────────────────────┐ │ │
│ │ │ Heap │ │ DLL (kernel32, │ │ │
│ │ │ (堆積) │ │ ntdll, ...) │ │ │
│ │ └─────────┘ └─────────────────────┘ │ │
│ │ │ │
│ └──────────────────────────────────────────────────┘ │
│ │
│ ┌────────────┐ ┌────────────┐ │
│ │ Thread 1 │ │ Thread 2 │ 執行緒 │
│ │ ┌──────┐ │ │ ┌──────┐ │ │
│ │ │Stack │ │ │ │Stack │ │ │
│ │ │TEB │ │ │ │TEB │ │ │
│ │ └──────┘ │ │ └──────┘ │ │
│ └────────────┘ └────────────┘ │
│ │
│ ┌─────────────────────────────┐ │
│ │ PEB │ 進程環境區塊 │
│ │ - 主程式位置 │ │
│ │ - DLL 載入資訊 │ │
│ │ - 堆積資訊 │ │
│ └─────────────────────────────┘ │
│ │
│ 其他資源:Process ID、Access Token、開啟的 Handle... │
│ │
└─────────────────────────────────────────────────────────┘
進程的關鍵元件
1. PEB(Process Environment Block,進程環境區塊)
每個進程只有一個 PEB,包含:
– 主執行檔的位置
– Loader Data(載入器資料):已載入的 DLL 清單
– 堆積(Heap)資訊
2. TEB(Thread Environment Block,執行緒環境區塊)
每個執行緒都有一個 TEB,包含:
– PEB 的位置
– 執行緒所屬的堆疊位置
– SEH 鏈的第一個項目 ← 這在漏洞利用中非常重要!
四大記憶體區段
| 區段 | 說明 | 權限 |
|---|---|---|
| Code (.text) | 程式碼區段,存放 CPU 指令 | 唯讀 + 可執行 |
| Data (.data) | 資料區段,存放變數和緩衝區 | 可讀寫,不可執行 |
| Stack(堆疊) | 存放函式參數、區域變數、返回位址 | 可讀寫(+ 可執行?) |
| Heap(堆積) | 動態配置的記憶體 | 可讀寫 |
進程的虛擬記憶體配置
在 32 位元 Windows 上:
0x00000000 ┌─────────────────────────┐
│ Reserved │ 保留區域
0x00010000 ├─────────────────────────┤
│ │
│ User Land Space │ 使用者空間
│ (約 2GB) │ 進程可使用的範圍
│ │
│ - EXE 映像 │
│ - DLL 映像 │
│ - Stack │
│ - Heap │
│ │
0x7FFFFFFF ├─────────────────────────┤
│ │
│ Kernel Space │ 核心空間
│ (約 2GB) │ 使用者進程無法存取
│ │
0xFFFFFFFF └─────────────────────────┘
重要:使用者模式的進程只能存取 0x00010000 到 0x7FFFFFFF 的範圍!
進程隔離
每個進程的記憶體是邏輯上隔離的:
– 進程 A 無法直接讀取進程 B 的記憶體
– 需要使用特殊 API:ReadProcessMemory() / WriteProcessMemory()
– 例外:共享記憶體區段(需要特別設定)
執行緒(Thread)是什麼?
執行緒是程式碼執行的最小單位:
- 一個進程至少有一個執行緒
- 多執行緒可以「同時」執行(實際上是快速切換)
- 每個執行緒有自己的:
- CPU 狀態/暫存器
- 兩個堆疊(核心模式 + 使用者模式)
- TLS(Thread Local Storage)
- Thread ID
- TEB
Context Switch(上下文切換)
這是多工作業的關鍵機制:
- 核心排程器決定切換執行緒
- 儲存目前執行緒的 CPU 狀態
- 載入下一個執行緒的 CPU 狀態
- 繼續執行
這就是「多工作業的錯覺」— 快速切換讓使用者感覺程式同時在執行!
PE 檔案格式詳解
什麼是 PE 檔案?
PE(Portable Executable) 是 Windows 可執行檔的格式:
– .exe 檔案
– .dll 檔案(動態連結程式庫)
– .sys 檔案(驅動程式)
PE 檔案結構總覽
┌─────────────────────────────────────────┐
│ DOS Header │ MZ 標頭(向後相容)
├─────────────────────────────────────────┤
│ PE Header │ "PE" 簽章
│ - Machine Type (I386, AMD64...) │
│ - Number of Sections │
│ - Timestamp │
├─────────────────────────────────────────┤
│ Optional Header │
│ - AddressOfEntryPoint ← 程式進入點 │
│ - ImageBase ← 預設載入位址 │
│ - SectionAlignment │
│ - SizeOfImage │
│ - SizeOfHeaders │
├─────────────────────────────────────────┤
│ Data Directories │
│ - Import Table ← IAT │
│ - Export Table ← EAT │
│ - Resource Table │
│ - ... │
├─────────────────────────────────────────┤
│ Section Table │
│ - .text (RX) 程式碼 │
│ - .rdata (R) 唯讀資料 │
│ - .data (RW) 可讀寫資料 │
│ - .reloc (R) 重定位資訊 │
├─────────────────────────────────────────┤
│ │
│ Section Data │
│ ┌─────────────────────────────────┐ │
│ │ .text │ │
│ │ (可執行程式碼) │ │
│ └─────────────────────────────────┘ │
│ ┌─────────────────────────────────┐ │
│ │ .data │ │
│ │ (初始化資料) │ │
│ └─────────────────────────────────┘ │
│ ┌─────────────────────────────────┐ │
│ │ .rdata │ │
│ │ (唯讀資料/字串) │ │
│ └─────────────────────────────────┘ │
│ │
└─────────────────────────────────────────┘
從檔案到記憶體:載入過程
當你執行一個 .exe 時:
┌────────────────────┐ ┌────────────────────────┐
│ PE 檔案(硬碟) │ │ 記憶體中的進程 │
│ │ │ │
│ 00000000: MZ... │ │ 0x00010000 ┌────────┐ │
│ 00000100: PE... │ 載入 │ │ Header │ │
│ 00000200: .text │ ────→ │ 0x00011000 ├────────┤ │
│ 00001000: .data │ │ │ .text │ │
│ 00002000: .rdata │ │ │ (RX) │ │
│ │ │ 0x00015000 ├────────┤ │
└────────────────────┘ │ │ .data │ │
│ │ (RW) │ │
│ 0x00017000 ├────────┤ │
│ │ .rdata │ │
│ │ (R) │ │
│ └────────┘ │
│ │
│ 0x7FFFFFFF │
└────────────────────────┘
載入步驟:
- 解析標頭:讀取 PE Header 資訊
- 映射區段:將檔案區段映射到記憶體頁面
- 處理 IAT/EAT:解析匯入/匯出表
- 載入依賴 DLL:載入所需的外部程式庫
- 處理重定位:如果需要的話(ASLR)
- 執行進入點:呼叫 AddressOfEntryPoint
ImageBase 與 ASLR
ImageBase 是 PE 檔案「希望」被載入的位址:
– 預設 EXE:0x00400000
– 預設 DLL:0x10000000
ASLR(Address Space Layout Randomization):
– 現代 Windows 會隨機化載入位址
– 讓攻擊者難以預測記憶體配置
– 這是重要的安全機制!
IAT 與 EAT:模組間的橋樑
IAT(Import Address Table,匯入位址表)
當你的程式要呼叫 MessageBoxA() 時:
1. MessageBoxA 在 user32.dll 中
2. 你的程式 IAT 記錄:「我需要 user32.dll 的 MessageBoxA」
3. 載入器填入實際位址到 IAT
4. 你的程式透過 IAT 間接呼叫
myapp.exe user32.dll
┌───────────────┐ ┌───────────────┐
│ 程式碼: │ │ EAT: │
│ CALL [IAT+0] │───────────┐ │ MessageBoxA │
│ │ │ │ → 0x77D10000 │
├───────────────┤ │ └───────────────┘
│ IAT: │ │ │
│ [0] = ??? │ ←─────────┘ │
│ ↓ │ │
│ 載入時填入 │ ←─────────────────────────┘
│ [0] = 0x77D10000
└───────────────┘
EAT(Export Address Table,匯出位址表)
DLL 用來「公開」函式給其他模組使用:
– 列出所有可被外部呼叫的函式
– 提供函式名稱與實際位址的對應
CPU 暫存器完整介紹
什麼是暫存器?
暫存器(Register) 是 CPU 內部的高速儲存單元:
– 存取速度比記憶體快很多
– 數量有限
– 用於暫時儲存資料和控制執行流程
x86 暫存器分類
1. 通用暫存器(General Purpose Registers)- 32 位元
| 暫存器 | 全名 | 主要用途 | 分割方式 |
|---|---|---|---|
| EAX | Accumulator | 運算結果、函式回傳值 | AX → AH + AL |
| EBX | Base | 通用(無特定用途) | BX → BH + BL |
| ECX | Counter | 迴圈計數器 | CX → CH + CL |
| EDX | Data | EAX 的擴充(乘除法) | DX → DH + DL |
| ESP | Stack Pointer | 指向堆疊頂端 ⭐ | SP |
| EBP | Base Pointer | 指向堆疊框架底部 ⭐ | BP |
| ESI | Source Index | 字串操作的來源位址 | SI |
| EDI | Destination Index | 字串操作的目標位址 | DI |
暫存器的分割結構
┌─────────────────────────────────────────────────┐
│ EAX (32 bits) │
│ ┌──────────────────────┬──────────────────────┐│
│ │ High 16 bits │ AX (16 bits) ││
│ │ ├──────────┬───────────┤│
│ │ │ AH (8b) │ AL (8b) ││
│ └──────────────────────┴──────────┴───────────┘│
│ │
│ 例如:EAX = 0x12345678 │
│ AX = 0x5678 │
│ AH = 0x56 │
│ AL = 0x78 │
└─────────────────────────────────────────────────┘
2. EIP(Instruction Pointer,指令指標)
┌──────────────────────────────────────────────────┐
│ EIP - 最重要的暫存器! │
│ │
│ • 指向「下一條要執行的指令」的位址 │
│ • CPU 會依據 EIP 來決定執行什麼 │
│ • 這是**唯讀**的!你無法直接修改它 │
│ │
│ ⚠️ 但是... │
│ 如果攻擊者能夠「間接」控制 EIP, │
│ 就能控制程式的執行流程! │
│ │
│ 這就是漏洞利用的核心目標! │
└──────────────────────────────────────────────────┘
3. 區段暫存器(Segment Registers)- 16 位元
| 暫存器 | 說明 |
|---|---|
| CS | Code Segment – 程式碼區段基底 |
| DS | Data Segment – 資料區段基底 |
| SS | Stack Segment – 堆疊區段基底 |
| ES | Extra Segment – 額外區段(字串操作) |
| FS | Extra – 指向 TEB! ⭐ |
| GS | Extra |
重要:
FS:[0]在 x86 Windows 指向 TEB,這在 SEH 利用中很關鍵!
4. EFLAGS(旗標暫存器)
這個 32 位元暫存器被分割成許多「旗標位元」:
| 旗標 | 名稱 | 說明 |
|---|---|---|
| CF | Carry Flag | 進位旗標(無號數溢位) |
| ZF | Zero Flag | 運算結果為零時設定 |
| SF | Sign Flag | 結果為負數時設定 |
| OF | Overflow Flag | 有號數溢位 |
| DF | Direction Flag | 字串操作方向 |
| PF | Parity Flag | 同位旗標 |
這些旗標用於條件判斷,決定是否執行分支跳躍!
x86 組合語言基礎
為什麼要學組合語言?
- 理解程式真正在做什麼
- 分析惡意程式
- 撰寫 Shellcode
- 了解漏洞利用原理
基本概念
Opcode vs Assembly
高階語言(C) → 組合語言(ASM) → 機器碼(Opcode)
↓
人類可讀 CPU 可執行
例如:
int a = 5; → MOV EAX, 5 → B8 05 00 00 00
- Assemble(組譯):ASM → Opcode
- Disassemble(反組譯):Opcode → ASM
- Decompile(反編譯):Opcode → 高階語言
組合語言語法
Intel 語法(我們主要使用這種):
指令 目標, 來源
MOV EAX, 5 ; 將 5 放入 EAX
AT&T 語法(Linux 常見):
指令 來源, 目標
movl $5, %eax ; 同樣的事情,但方向相反
常用指令詳解
資料移動
MOV EAX, 5 ; EAX = 5
MOV EAX, EBX ; EAX = EBX
MOV EAX, [EBX] ; EAX = *EBX (從 EBX 指向的位址讀取)
MOV [EAX], EBX ; *EAX = EBX (寫入 EAX 指向的位址)
方括號 [ ] 表示「記憶體位址」,類似 C 的指標解參考
算術運算
ADD EAX, 5 ; EAX = EAX + 5
SUB EAX, 5 ; EAX = EAX - 5
INC EAX ; EAX = EAX + 1
DEC EAX ; EAX = EAX - 1
MUL EBX ; EDX:EAX = EAX * EBX
DIV EBX ; EAX = EDX:EAX / EBX, EDX = 餘數
邏輯運算
AND EAX, EBX ; EAX = EAX & EBX
OR EAX, EBX ; EAX = EAX | EBX
XOR EAX, EAX ; EAX = 0 (自己 XOR 自己 = 0,常用來清零)
NOT EAX ; EAX = ~EAX
NEG EAX ; EAX = -EAX
堆疊操作
PUSH EAX ; ESP = ESP - 4; *ESP = EAX
POP EBX ; EBX = *ESP; ESP = ESP + 4
圖解 PUSH 與 POP:
初始狀態: PUSH EAX 後: POP EBX 後:
EAX = 0x50505050 EAX = 0x50505050 EAX = 0x50505050
EBX = 0x12345678 EBX = 0x12345678 EBX = 0x50505050 ←變了
ESP = 0x00120008 ESP = 0x00120004 ←變了 ESP = 0x00120008
STACK STACK STACK
0x00120000 │41414141│ 0x00120000│41414141│ 0x00120000│41414141│
0x00120004 │42424242│ 0x00120004│50505050│← 0x00120004│50505050│
0x00120008 │43434343│← 0x00120008│43434343│ 0x00120008│43434343│←
注意:堆疊是往低位址成長的!PUSH 會減少 ESP
流程控制
; 無條件跳躍
JMP label ; 跳到 label
; 條件跳躍(依據 EFLAGS)
JE label ; Jump if Equal (ZF=1)
JNE label ; Jump if Not Equal (ZF=0)
JZ label ; Jump if Zero (ZF=1)
JNZ label ; Jump if Not Zero (ZF=0)
JG label ; Jump if Greater (有號數)
JL label ; Jump if Less (有號數)
JA label ; Jump if Above (無號數)
JB label ; Jump if Below (無號數)
; 比較指令(設定 EFLAGS)
CMP EAX, EBX ; 計算 EAX - EBX,只設定旗標,不改變暫存器
TEST EAX, EAX ; 計算 EAX & EAX,只設定旗標
函式呼叫
CALL function ; PUSH 返回位址; JMP function
RET ; POP EIP (返回)
CALL 的詳細流程:
執行前: 執行 CALL 0x401000 後:
EIP = 0x401050 EIP = 0x401000 (跳到函式)
ESP = 0x00120008 ESP = 0x00120004
STACK STACK
0x00120004│ │ 0x00120004│0x401055│← 返回位址
0x00120008│........│← 0x00120008│........│
NOP 指令
NOP ; No Operation,什麼都不做
; Opcode: 0x90
常見用途:
– 對齊程式碼
– 除錯佔位
– NOP Sled(已過時的漏洞利用技巧)
警告:濫用 NOP 是不好的習慣!會使 payload 變大且更容易被偵測
Endianness(位元組順序)
x86 使用 Little Endian(小端序):
數值:0x12345678
在記憶體中的排列:
位址 內容
0x0000 78 ← 最低位元組(LSB)先存
0x0001 56
0x0002 34
0x0003 12 ← 最高位元組(MSB)後存
所以 0x12345678 在記憶體中看起來是:\x78\x56\x34\x12
漏洞利用注意:寫入位址時需要反轉位元組順序!
漏洞利用術語解釋
基本術語
| 術語 | 英文 | 定義 |
|---|---|---|
| 漏洞 | Vulnerability | 可能被利用的軟體弱點(溢位、邏輯錯誤、資訊洩露等) |
| 漏洞利用 | Exploit | 利用漏洞來執行攻擊者控制操作的程式碼 |
| 0day | Zero-day | 在開發者得知之前就被利用的漏洞 |
| Payload | Payload | 漏洞利用成功後執行的程式碼 |
| Shellcode | Shellcode | 通常用於取得 shell 的 payload |
記憶體相關漏洞類型
1. Buffer Overflow(緩衝區溢位)
當資料超出緩衝區的預定大小:
char buffer[8];
strcpy(buffer, "AAAAAAAAAAAAAAAAAAAA"); // 寫入超過 8 bytes!
正常情況: 溢位後:
┌──────────────┐ ┌──────────────┐
│ buffer[8] │ │ AAAAAAAA │
├──────────────┤ ├──────────────┤
│ 其他變數 │ │ AAAA... │ ← 被覆蓋!
├──────────────┤ ├──────────────┤
│ 返回位址 │ │ 0x41414141 │ ← 危險!
└──────────────┘ └──────────────┘
2. Stack Overflow(堆疊溢位)
Buffer Overflow 發生在堆疊上,可能覆蓋:
– 區域變數
– 儲存的 EBP
– 返回位址 ← 最危險的目標!
3. Heap Overflow(堆積溢位)
Buffer Overflow 發生在堆積上,可能覆蓋:
– 堆積管理結構
– 其他動態配置的資料
4. Format String(格式化字串漏洞)
當使用者輸入被當作格式字串:
printf(user_input); // 危險!
// 如果 user_input = "%x %x %x",會洩露堆疊內容
// 如果 user_input = "%n",可以寫入記憶體!
5. Off-by-One(差一錯誤)
緩衝區大小計算錯誤,多寫入 1 個位元組:
char buffer[10];
for (int i = 0; i <= 10; i++) { // 應該是 i < 10
buffer[i] = 'A'; // buffer[10] 溢位!
}
重要區別
┌────────────────────────────────────────────────────────┐
│ Stack Buffer Overflow ≠ Stack Overflow │
│ │
│ Stack Buffer Overflow: │
│ 堆疊上的緩衝區被溢位覆蓋 │
│ │
│ Stack Overflow: │
│ 通常指堆疊空間耗盡(如無窮遞迴) │
└────────────────────────────────────────────────────────┘
總結與學習資源
本文重點回顧
- Windows 架構
- Ring 0(核心)與 Ring 3(使用者)的區別
- 進程、執行緒的概念
- 記憶體管理
- 虛擬記憶體與實體記憶體
- 頁面與頁框
- 進程位址空間配置
- PE 檔案格式
- 從檔案到記憶體的載入過程
- IAT/EAT 的作用
- CPU 暫存器
- 通用暫存器的用途
- EIP 的重要性
- EFLAGS 與條件跳躍
- 組合語言
- 基本指令(MOV, PUSH, POP, CALL, RET…)
- Little Endian 位元組順序
- 漏洞類型
- Buffer Overflow
- Stack Overflow
- Format String
- Off-by-One
延伸學習資源
線上教程:
– LiveOverflow YouTube 頻道
– Exploit Education 練習平台
書籍推薦:
– Hacking: The Art of Exploitation by Jon Erickson
– The Shellcoder’s Handbook
– A Bug Hunter’s Diary by Tobias Klein
參考資料:
– Intel 64 and IA-32 Architectures Software Developer Manuals
– MSDN Windows 開發文件
– PE 格式規格(Microsoft 官方)
