PE文件
是在 windows系統下的可執行文件,他是微軟在 UNIX 的 COFF
格式為基礎製作而成的。而PE文件
是指32位元的可執行文件,也稱為 PE32
。但是64位元則稱為 PE+
或 PE32+
,不可稱為 PE64
。
在介紹 PE 格式
前,我們要知道一份程式是甚麼由程式碼變成 PE 文件。我們都學過程序語言分成高階語言
、組合語言
、指令碼
等等的,當一份高階語言
要執行時,就會透過一系列步驟變成指令碼
,再經過指令週期
後就能達成我們的任務。
( 會作梗圖了 \OwO/ )
這是一個會寫 hello world 的視窗。
int main()
{
MessageBoxA(0,"hello world","This is title",0);
return 0;
}
這個程序會生出一個寫 hello world 的視窗。
然後 IDE 會根據 calling convention
(呼叫慣例)
來生成一段組合語言 :
push 0
push "This is title" //push 到 stack
push "hello world"
push 0
call MassageBoxA
xor eax,eax
ret
太好了,我們把他轉成機器碼前做幾件事來封裝他 :
.rdata
( 用來只能讀取的空間 )title -> 6 byte
放到 .rdata + 0
hello world -> 12 byte
放到 .rdata + 6
MassageBoxA
這東西處理器看不懂在哪裡,所以把它使用的資料位置放入 .idata
( Import Address Table
)有些 IDE 會把這放到
.rdata
,沒有唯一標準 ( 像是 Visual Studio )
.text
然後幾點要注意的 :
PE model
,所以掛載的 process
都依 PE 格式
封裝image base
( 跟 virtual address
很像 ),指向該 model
內容image base
通常是 0x400000然後把這些轉換成機器碼,有興趣可以看白算盤跟MIPS
指令集來算算看。
https://inst.eecs.berkeley.edu/~cs61c/resources/MIPS_Green_Sheet.pdf
注意 Little / Big endian
:
然後把它組譯成 COFF
格式,就是組譯器生產的封裝檔案。
執行 g++ -c main.cpp
生成 main.o ( 他就是COFF
格式 )
用 PEview 看它的結構
然後透過 linker
為這個 COFF
進行連結,就變成 EXE程式
了
我們來看看 linking loader
中會用到的步驟 :
Binding
( 決定 Process 起始位置 )。若是在 linker loader
時做 Binding
產生出 obj
的稱為 "relocatable object file
"
linker
把必要資料依 PE格式 寫入 OptionalHeader
詳細可以閱讀文件 : https://docs.microsoft.com/zh-tw/windows/win32/debug/pe-format
向 OS
要求起始位置
依 Complier
交辦 linking
修正資訊做出 linking
修正
Relocation
修正
等等步驟。
太棒了,你現在有了一份 EXE
檔案了 \OwO/
當你運行這個 EXE
時,就會由靜態檔案
變成 process
那運行時發生了甚麼呢?
比如說我在 cmd.exe
輸入 msgbox.exe
,會發生以下步驟 :
父程序 cmd.exe
發出 system call
請求子程序
到 kernel mode
。OS
生成 Thread
給子程序,並在 userland
紀錄 PEB
( Process environment block ) 跟 TEB
( Thread environment block )。
到 user mode
。然後對 Thread
初始化,把 PE
裝到 memory
還有 PE model
修正。
application loader
修正完後把 EIP
送到 Entry point
,開始程序。
這邊看起來很麻煩,不過好像其實平常用不太到,希望未來多看幾次就可以能熟能生巧。