官术网_书友最值得收藏!

1.2 二進制文件概述

1.2.1 PE文件格式

PE(Portable Executable)是Win32平臺下可執行文件遵守的數據格式。常見的可執行文件(如“*.exe”文件和“*.dll”文件)都是典型的PE文件。

一個可執行文件不光包含了二進制的機器代碼,還會自帶許多其他信息,如字符串、菜單、圖標、位圖、字體等。PE文件格式規定了所有的這些信息在可執行文件中如何組織。在程序被執行時,操作系統會按照PE文件格式的約定去相應的地方準確地定位各種類型的資源,并分別裝入內存的不同區域。如果沒有這種通用的文件格式約定,試想可執行文件裝入內存將會變成一件多么困難的事情!

PE文件格式把可執行文件分成若干個數據節(section),不同的資源被存放在不同的節中。一個典型的PE文件中包含的節如下。

.text 由編譯器產生,存放著二進制的機器代碼,也是我們反匯編和調試的對象。

.data 初始化的數據塊,如宏定義、全局變量、靜態變量等。

.idata 可執行文件所使用的動態鏈接庫等外來函數與文件的信息。

.rsrc 存放程序的資源,如圖標、菜單等。

除此以外,還可能出現的節包括“.reloc”、“.edata”、“.tls”、“.rdata”等。

題外話:如果是正常編譯出的標準PE文件,其節信息往往是大致相同的。但這些section的名字只是為了方便人的記憶與使用,使用Microsoft Visual C++中的編譯指示符#pragma data_seg()可以把代碼中的任意部分編譯到PE的任意節中,節名也可以自己定義。如果可執行文件經過了“加殼”處理,PE的節信息就會變得非常“古怪”。在Crack和反病毒分析中需要經常處理這類古怪的PE文件。

1.2.2 虛擬內存

Windows的內存可以被分為兩個層面:物理內存和虛擬內存。其中,物理內存比較復雜,需要進入Windows內核級別ring0才能看到。通常,在用戶模式下,我們用調試器看到的內存地址都是虛擬內存。

如圖1.2.1所示,Windows讓所有的進程都“相信”自己擁有獨立的4GB內存空間。但是,我們計算機中那根實際的內存條可能只有512MB,怎么可能為所有進程都分配4GB的內存呢?這一切都是通過虛擬內存管理器的映射做到的。

圖1.2.1 Windows虛擬內存與物理內存示意圖

雖然每個進程都“相信”自己擁有4GB的空間,但實際上它們運行時真正能用到的空間根本沒有那么多。內存管理器只是分給進程了一片“假地址”,或者說是“虛擬地址”,讓進程們“認為”這些“虛擬地址”都是可以訪問的。如果進程不使用這些“虛擬地址”,它們對進程來說就只是一筆“無形的數字財富”;當需要進行實際的內存操作時,內存管理器才會把“虛擬地址”和“物理地址”聯系起來。

Windows的內存管理機制在很大程度上與日常生活中銀行所起的金融作用有一定的相似性,我們可以通過一個形象的比方來理解虛擬內存。

·進程相當于儲戶。

·內存管理器相當于銀行。

·物理內存相當于鈔票。

·虛擬內存相當于存款。

·進程可能擁有大片的內存,但使用的往往很少;儲戶擁有大筆的存款,但實際生活中的開銷并沒有多少。

·進程不使用虛擬內存時,這些內存只是一些地址,是虛擬存在的,是一筆無形的數字財富。

·進程使用內存時,內存管理器會為這個虛擬地址映射實際的物理內存地址,虛擬內存地址和最終被映射到的物理內存地址之間沒有什么必然聯系;儲戶需要用錢時,銀行才會兌換一定的現金給儲戶,但物理鈔票的號碼與儲戶心目中的數字存款之間可能并沒有任何聯系。

·操作系統的實際物理內存空間可以遠遠小于進程的虛擬內存空間之和,仍能正常調度;銀行中的現金準備可以遠遠小于所有儲戶的儲蓄額總和,仍能正常運轉,因為很少會出現所有儲戶同時要取出全部存款的現象;社會上實際流通的鈔票也可以遠遠小于社會的財富總額。

題外話:實際上,金融學、經濟學、管理學中有很多概念和理論與計算機科學中的知識出奇相似。有時將這些知識互相類比一下會獲得一種融會貫通的清爽。

進程所擁有的4GB虛擬內存中包含了程序運行時所必需的資源,比如代碼、棧空間、堆空間、資源區、動態鏈接庫等。在后面的章節中,我們將不停地輾轉于虛擬內存中的這些區域。

提示:操作系統原理中也有“虛擬內存”的概念,那是指當實際的物理內存不夠時,有時操作系統會把“部分硬盤空間”當做內存使用從而使程序得到裝載運行的現象。請不要將用硬盤充當內存的“虛擬內存”與這里介紹的“虛擬內存”相混淆。此外,本書除第4篇內核安全外,其余所述之“內存”均指Windows用戶態內存映射機制下的虛擬內存。

1.2.3 PE文件與虛擬內存之間的映射

在調試漏洞時,可能經常需要做這樣兩種操作。

(1)靜態反匯編工具看到的PE文件中某條指令的位置是相對于磁盤文件而言的,即所謂的文件偏移,我們可能還需要知道這條指令在內存中所處的位置,即虛擬內存地址(VA)。

(2)反之,在調試時看到的某條指令的地址是虛擬內存地址,我們也經常需要回到PE文件中找到這條指令對應的機器碼。

為此,我們需要弄清楚PE文件地址和虛擬內存地址之間的映射關系。首先,我們先看幾個重要的概念。

(1)文件偏移地址(File Offset)

數據在PE文件中的地址叫文件偏移地址,個人認為叫做文件地址更加準確。這是文件在磁盤上存放時相對于文件開頭的偏移。

(2)裝載基址(Image Base)

PE裝入內存時的基地址。默認情況下,EXE文件在內存中的基地址是0x00400000,DLL文件是0x10000000。這些位置可以通過修改編譯選項更改。

(3)虛擬內存地址(Virtual Address,VA)

PE文件中的指令被裝入內存后的地址。

(4)相對虛擬地址(Relative Virtual Address,RVA)

相對虛擬地址是內存地址相對于映射基址的偏移量。

虛擬內存地址、映射基址、相對虛擬內存地址三者之間有如下關系。

VA=Image Base+RVA

如圖1.2.2所示,在默認情況下,一般PE文件的0字節將對映到虛擬內存的0x00400000位置,這個地址就是所謂的裝載基址(Image Base)。

圖1.2.2 PE文件與虛擬內存的映射關系

文件偏移是相對于文件開始處0字節的偏移,RVA(相對虛擬地址)則是相對于裝載基址0x00400000處的偏移。由于操作系統在進行裝載時“基本”上保持PE中的各種數據結構,所以文件偏移地址和RVA有很大的一致性。

之所以說“基本”上一致是因為還有一些細微的差異。這些差異是由于文件數據的存放單位與內存數據存放單位不同而造成的。

(1)PE文件中的數據按照磁盤數據標準存放,以0x200字節為基本單位進行組織。當一個數據節(section)不足0x200字節時,不足的地方將被0x00填充;當一個數據節超過0x200字節時,下一個0x200塊將分配給這個節使用。因此PE數據節的大小永遠是0x200的整數倍。

(2)當代碼裝入內存后,將按照內存數據標準存放,并以0x1000字節為基本單位進行組織。類似的,不足將被補全,若超出將分配下一個0x1000為其所用。因此,內存中的節總是0x1000的整數倍。

表1-2-1列出的文件偏移地址和RVA之間的對應關系可以讓您更直接地理解這種“細微的差異”。

表1-2-1 文件偏移地址和RVA之間的對應關系

由于內存中數據節相對于裝載基址的偏移量和文件中數據節的偏移量有上述差異,所以進行文件偏移到虛擬內存地址之間的換算時,還要看所轉換的地址位于第幾個節內。

我們把這種由存儲單位差異引起的節基址差稱做節偏移,在上例中:

.text節偏移=0x1000-0x400=0xc00
.rdata節偏移=0x7000-0x6200=0xE00
.data節偏移=0x9000-0x7400=0x1C00
.rsrc節偏移=0x2D000-0x7800=0x25800

那么文件偏移地址與虛擬內存地址之間的換算關系可以用下面的公式來計算。

文件偏移地址=虛擬內存地址(VA)-裝載基址(Image Base)-節偏移=RVA-節偏移

以表1-2-1為例,如果在調試時遇到虛擬內存中0x00404141處的一條指令,那么要換算出這條指令在文件中的偏移量,則有:

文件偏移量=0x00404141-0x00400000-(0x1000-0x400)=0x3541

一些PE工具提供了這類地址轉換,Lord PE就是其中出色的一款,如圖1.2.3所示。單擊“PE Editor”按鈕,選擇需要查看的PE文件,如圖1.2.4所示。

圖1.2.3 LordPE使用1

圖1.2.4 LordPE使用2

用這個工具可以方便地查看PE文件中的節信息,對應于前面表格中的例子,如圖1.2.5所示。也可以方便地換算虛擬內存地址,文件偏移地址和RVA,如圖1.2.6所示。

圖1.2.5 LordPE使用3

圖1.2.6 LordPE使用4

主站蜘蛛池模板: 松滋市| 思茅市| 四平市| 新宾| 璧山县| 平湖市| 茶陵县| 皮山县| 镇江市| 东台市| 嘉兴市| 东乡县| 梅州市| 鄂伦春自治旗| 方山县| 虞城县| 那坡县| 临澧县| 庆元县| 桦川县| 宁城县| 股票| 丹巴县| 南阳市| 鄄城县| 垫江县| 彭泽县| 湖北省| 肥西县| 松滋市| 奈曼旗| 民勤县| 宕昌县| 嘉定区| 西青区| 夏河县| 丹阳市| 民权县| 蓬安县| 垣曲县| 清镇市|