第五章 Windows PE/COFF
5.1 Windows的二進(jìn)制文件格式PE/COFF
- 組成和大多ELF一樣匈子,文件頭,后面若干個(gè)段,后面符號(hào)表,調(diào)試信息的內(nèi)容
- COFF的文件頭包括描述文件的總體結(jié)構(gòu)炭菌,屬性的Image Header,另外一個(gè)是段表
6.1 進(jìn)程虛擬地址空間
- 每個(gè)程序被運(yùn)行起來(lái)都擁有獨(dú)立的虛擬地址空間,具體大小是硬件決定的逛漫,理論上是硬件內(nèi)存大小黑低,例如32位硬件,虛擬內(nèi)存大小就是4GB
- 由于程序在運(yùn)行的時(shí)候是在操作系統(tǒng)之下進(jìn)行的酌毡,在分配給程序的虛擬空間中克握,有一部分是操作系統(tǒng)所占用的
6.2 裝載的方式
- 程序執(zhí)行時(shí)所需要的指令和數(shù)據(jù)必須在呢村中才能正常運(yùn)行,最簡(jiǎn)單的就是全部裝入內(nèi)存枷踏,問(wèn)題是當(dāng)內(nèi)存小的時(shí)候菩暗,并不能完全裝入內(nèi)存
- 考慮到了內(nèi)存大小不夠的情況,出現(xiàn)兩種典型加載內(nèi)存的方式:覆蓋裝入旭蠕、頁(yè)映射停团,都是動(dòng)態(tài)裝載方法,核心思想就是用到哪個(gè)模塊掏熬,就將哪個(gè)模塊裝入內(nèi)存佑稠,如果不用就存放在磁盤中
6.2.1 覆蓋裝入
- 目前已經(jīng)被淘汰,典型的時(shí)間換空間的做法旗芬,將原有的寫入磁盤舌胶,新的直接覆蓋,需要用以前的再進(jìn)行置換
6.2.2 頁(yè)映射
- 在內(nèi)存中劃分的最小單位為“頁(yè)”岗屏,都是以頁(yè)的方式進(jìn)行置換
6.3 從操作系統(tǒng)角度看可執(zhí)行文件的裝載
- 現(xiàn)在硬件的MMU提供地址轉(zhuǎn)換的功能辆琅,因?yàn)轫?yè)從磁盤加載到物理內(nèi)存中漱办,還得進(jìn)行重定位这刷,MMU就是干這個(gè)的,MMU-》硬件地址轉(zhuǎn)換以及頁(yè)映射機(jī)制
6.3.1 進(jìn)程的建立
- 從操作系統(tǒng)的角度看娩井,進(jìn)程最關(guān)鍵的特征是獨(dú)立的虛擬地址空間暇屋,這使得它有別于其他進(jìn)程
- 創(chuàng)建一個(gè)進(jìn)程,然后裝載相應(yīng)的可執(zhí)行文件并且執(zhí)行主要步驟:
1.創(chuàng)建獨(dú)立的虛擬空間
2.讀取可執(zhí)行文件頭洞辣,建立虛擬內(nèi)存與可執(zhí)行文件的映射關(guān)系
3.將CPU的指令寄存器設(shè)置成可執(zhí)行文件的入口地址咐刨,啟動(dòng)運(yùn)行
6.3.2 頁(yè)錯(cuò)誤
- 虛擬內(nèi)存區(qū)域:VMA
- 當(dāng)讀取的頁(yè)沒(méi)在內(nèi)存中,就認(rèn)為這是一個(gè)頁(yè)錯(cuò)誤扬霜,Page Fault定鸟,CPU將控制權(quán)交給操作系統(tǒng),操作系統(tǒng)負(fù)責(zé)后續(xù)處理,操作系統(tǒng)會(huì)根據(jù)映射關(guān)系找到這個(gè)數(shù)據(jù)結(jié)構(gòu)著瓶,并找到空白頁(yè)联予,建立虛擬頁(yè)與物理頁(yè)的映射關(guān)系,此時(shí)相關(guān)數(shù)據(jù)已經(jīng)準(zhǔn)備好了,再將控制權(quán)交給進(jìn)程沸久,進(jìn)程從剛才的頁(yè)錯(cuò)誤的位置重新開(kāi)始執(zhí)行
6.4 進(jìn)程虛擬空間分布
- 操作系統(tǒng)并不care加載的內(nèi)容是什么季眷,他只關(guān)心和加載相關(guān)的問(wèn)題,比如段的權(quán)限(可讀卷胯,可寫子刮,可執(zhí)行)
- 通常代碼段的權(quán)限是可讀可執(zhí)行,數(shù)據(jù)段和BSS段是可讀可寫窑睁,只讀數(shù)據(jù)段的權(quán)限是只讀的段
- ELF文件引入Segment概念挺峡,將相同權(quán)限的東西放在一起的劃分概念,會(huì)把相同權(quán)限的section都劃分到一個(gè)segment中
- 描述section屬性的結(jié)構(gòu)膠段表担钮,描述segment的叫程序頭沙郭,它描述了ELF文件該如何被操作系統(tǒng)映射到進(jìn)程的虛擬空間
- 在加載虛擬內(nèi)存的時(shí)候,會(huì)按照不同的segment去分配VMA
- ELF可執(zhí)行文件中有一個(gè)專門的數(shù)據(jù)結(jié)構(gòu)叫程序頭表裳朋,用來(lái)保存segment的信息病线,它描述了ELF文件該如何北操作系統(tǒng)映射到進(jìn)程的虛擬空間
- ELF目標(biāo)文件因?yàn)椴恍枰谎b載,就沒(méi)有程序頭表
6.4.2 堆和棧
- VMA除了被用來(lái)映射可執(zhí)行文件中的各個(gè)“segment”鲤嫡,還有其他作用送挑,操作系統(tǒng)通過(guò)使用VMA對(duì)進(jìn)程的地址空間進(jìn)行管理
- 多數(shù)情況下每一個(gè)進(jìn)程的棧和堆都有對(duì)應(yīng)的VMA
6.4.5 進(jìn)程棧初始化
- 進(jìn)程在剛啟動(dòng)的時(shí)候需要一些進(jìn)程運(yùn)行的環(huán)境,最基本的就是系統(tǒng)環(huán)境變量和進(jìn)程的運(yùn)行參數(shù)暖眼,常見(jiàn)的做法是操作系統(tǒng)在進(jìn)程啟動(dòng)前將這些信息提前保存到進(jìn)程的虛擬空間的棧中
6.5 Linux內(nèi)核裝載ELF過(guò)程簡(jiǎn)介
- fork是分身惕耕,execve函數(shù)是變身,fork能復(fù)制出當(dāng)前進(jìn)程诫肠,execve可以在讓當(dāng)前進(jìn)程改動(dòng)
- bash進(jìn)程會(huì)調(diào)用fork()創(chuàng)建一個(gè)新的進(jìn)程司澎,再在新的進(jìn)程中調(diào)用execve(),調(diào)用指定的可執(zhí)行文件
7.6 動(dòng)態(tài)鏈接的步驟和實(shí)現(xiàn)
- 動(dòng)態(tài)鏈接分3步:
1.啟動(dòng)動(dòng)態(tài)鏈接器
2.裝載所需要的共享對(duì)象
3.重定位和初始化
7.6.2 裝載共享對(duì)象
- 動(dòng)態(tài)鏈接器將可執(zhí)行文件和鏈接器本身的符號(hào)表都合并到一個(gè)符號(hào)表中,我們稱為全局符號(hào)表
- 鏈接器加載文件常見(jiàn)的是廣度優(yōu)先
- 共享對(duì)象里面的全局符號(hào)唄另一個(gè)共享對(duì)象的同名全局符號(hào)覆蓋的現(xiàn)象叫共享對(duì)象全局符號(hào)介入
- 在Linux下一個(gè)符號(hào)需要被加入全局符號(hào)表時(shí)栋豫,如果相同的符號(hào)名已經(jīng)存在挤安,后加入的符號(hào)會(huì)被忽略