# 學習成為人體 PE Parser
:::info
筆者最近在閱讀 aaaddress1 的大作: [Windows APT Warfare:惡意程式前線戰術指南](https://www.tenlong.com.tw/products/9789864347544),因為書中圍繞著 PE File 進行,腦容量太小的我又一直忘記 PE 的檔案結構,最後決定還是認真的把它研究一遍並寫成筆記。
:::
PE (Portable Executable) 是一種用於可執行文件、目標文件和動態連結庫的文件格式,主要使用在 32 位和 64 位的 Windows 作業系統上。
> 有點像是 Linux 作業系統中的 elf 檔。

## DOS Header

在一串連續的記憶體中, DOS Header 一定會是記憶體中的首段內容, DOS Header 中的幾項資訊會是比較重要的:
- e_magic
e_magic 幫助我們辨認該 PE 檔案是否合法,一般來說,它應該永遠等於 **MZ** 字串。
如果以 C/C++ 檢查 PE File ,可以這樣做:
```c=
#include <windows.h>
// ...
void parser(char* filePtr){
IMAGE_DOS_HEADER* dosHdr = (IMAGE_DOS_HEADER *)filePtr;
if(dosHdr->e_magic != IMAGE_DOS_SIGNATURE){
return;
}
// ...
}
// ...
```
- e_lfanew
觀察 e_lfanew 之前,必須先了解什麼是 RVA (Relative Virtual Address), RVA 是程式入口點的參考位址,舉例來說:
如果程式被放入虛擬地址(Virtual address, VA)的 `0x01000000` 處,且 RVA 位於 `0x102D6C` 處,那麼程式在記憶體中的實際入口就會是 **VA + RVA**:
```
0x01000000
+ 0x00102D6C
= 0x01102D6C
```
e_lfanew 其實就是指向了 NT Headers 的 RVA ,換個角度思考,我們將前面的 dosHdr 加上偏移量 (在這邊指 RVA),就可以獲得 NT Headers 的起始位址:
```c=
IMAGE_NT_HEADERS* ntHdrs = (IMAGE_NT_HEADERS *)((size_t)dosHdr + dosHdr->e_lfanew);
```
## NT Headers
透過讀取 DOS Header 獲得 NT Headers 的起始位址以後,我們就可以對 PE 檔案做更進一步的檢驗。
NT Headers 共包含了兩大結構,分別是 File Header 以及 Optional Header 。
### File Header

參考上圖,在 File Header 結構中有多個屬性,每個屬性代表的資訊如下:
- Machine
紀錄 PE 檔案所存放的機械碼屬於哪一種指令集架構:
- x86
- ARM
- x64
- NumberOfSections
一個 PE File 通常會有好幾段塊狀區域, NumberofSections 紀錄了 PE 檔案的區段數量。
> 這個參數對我們撰寫程式解析 PE File 非常有幫助,至於那些塊狀區段存了什麼,晚點會提到。
- TimeDateStamp
紀錄程式編譯時間的時間戳。
- PointerToSymbolTable
符號表地址,用於除錯,一般為 0 。
- NumberOfSymbols
如果符號表存在,這邊會記錄符號數量。
- Characteristics
紀錄了整個 PE 的屬性,包含:
- Executable
- Info of redirection
- 32-bit or not
- DLL modules
### Optional Header

> [Source](https://www.researchgate.net/figure/Structure-of-the-PE-optional-header-and-location-of-the-IAT_fig2_312032405)
Optional Header 的中文稱**可選段**,實際上,如果要讓 PE 能夠順利地被執行程式裝載器使用, Optional Header 為必備的。
> **補充**:
> Optional Header 不存在於 Object File (COFF),而是在編譯的連結階段才會由連結器補上。
參考上圖, Optional Header 包含了很多參數,下面針對重要的參數作介紹:
- Address of entry point
程式碼編譯後,程式的入口點,也就代表當 Program 被作業系統載入時, Process 會從這邊開始執行。
> 一般來說,入口點會指向 .text section 的函式開頭。
- ImageBase
記錄了 PE 檔案 mapping 到記憶體上的預設位址,通常為 `0x400000` 或是 `0x800000` 。
- SizeOfImage
記錄了當程式處於動態執行階段需要多少空間才能存放整個 Image 。
- Section alignment
動態的區域對齊, 32-bit 的環境下預設大小為 0x1000 bytes 。
- File alignment
靜態的區域對齊, 32-bit 的環境下預設大小為 0x200 bytes 。
> 假設有不足 0x200 bytes 的資料要放進塊狀區段,塊狀區段的大小為 0x200 bytes ,如果資料多於預設大小,塊狀區段的大小則為 0x400 bytes 。
- Size of headers
DOS Header + NT Headers + Section Headers 的大小。
- Data directory
- Export table
- Import table
- Ressource table
- Exception table
- Import Address table
## Section Headers
Section Headers 的位址緊隨在 NT Headers 的後方,使用 C/C++ 可以輕鬆的獲得其位址:
```c=
IMAGE_SECTION_HEADER* sectHdr = (IMAGE_SECTION_HEADER *)((size_t)ntHdrs + sizeof(*ntHdrs));
```
至於 Section Headers 的本體到底是什麼呢?它其實就是一個存放塊狀區段資訊的陣列:
```c=
for (size_t i = 0; i < ntHdrs->FileHeader.NumberOfSections; i++){
printf("\t#%.2x - %8s - %.8x - %.8x \n", i, sectHdr[i].Name, sectHdr[i].PointerToRawData, sectHdr[i].SizeOfRawData);
}
```
每一塊存放塊狀區段資訊的空間都會有以下屬性:
- PointerToRawData
該區段處存在靜態檔案的偏移量。
- SizeofRawData
該區段的實際大小。
- VirtualAddress
相較於映像基址的相對偏移量。
- VirtualSize
顯示該區段需要被分配多少動態空間。
- Characteristics
紀錄該區段是否可讀、可寫、可執行。
## 常見區段
- .text
用於存放程式碼。
- .data
用來宣告已初始化的資料與常數。
- .bss
存放已宣告但尚未初始化的變數。
- .rdata
存放唯讀資料。
- .idata
存放引入的函式與資料,這些資料會在 Process 建立時,由執行程式裝載器負責填充。
- .edata
存放用來導出給其他程式使用的函式與資料。
- .rsrc
用於記錄程式使用了哪些資源。
- reloc
重定位,當 PE 程式載入失敗,會以此段作為參考進行調整。
## 總結
了解 PE File 的基本結構後,我們就可以將~~惡意~~ shellcode 添加至目標檔案再將 Entry point 指向惡意程式區段的 Virtual Address 做些壞壞的事(?)

本文章介紹的內容大概只有 Windows APT Warfare:惡意程式前線戰術指南整本書的皮毛,如果想學更多就去下單買一本[ Windows APT Warfare:惡意程式前線戰術指南](https://www.tenlong.com.tw/products/9789864347544)吧!
## References
- [Portable Executable](https://zh.wikipedia.org/wiki/%E5%8F%AF%E7%A7%BB%E6%A4%8D%E5%8F%AF%E6%89%A7%E8%A1%8C)
- [Windows PE 檔案結構及其載入機制](https://www.itread01.com/content/1545708077.html)
- [Windows-APT-Warfare Repo](https://github.com/aaaddress1/Windows-APT-Warfare/blob/main/source/chapter%2302/peParser/peParser.cpp)
- [Windows PE文件各个节(Section)分析](https://blog.csdn.net/weixin_38164023/article/details/106665175)