RVA與RWA的關系


 

 

 

 

 

RVA與RWA的關系

原理比較簡單:首先判斷這個地址是否在PE頭中,如果在,文件偏移和內存偏移相等,如果存在於文件的區段中,則利用以下公式:
內存偏移 - 該段起始的RVA(VirtualAddress) = 文件偏移 - 該段的PointerToRawData
內存偏移 = 該段起始的RVA(VirtualAddress) + (文件偏移 - 該段的PointerToRawData)
文件偏移 = 該段的PointerToRawData + (內存偏移 - 該段起始的RVA(VirtualAddress))

 

DOS頭,PE頭,塊表在內存中的偏移和在磁盤中的偏移是一致的。其余的內存偏移和磁盤偏移是需要轉換的。

各種地址的概念:

基址(Image Base):PE文件裝入內存后的起始地址。

相對虛擬地址(Relative Virtual Address,RVA):在內存中相對於PE文件裝入地址的偏移位置,是一個相對地址。

虛擬地址(Virtual Address,VA):裝入內存中的實際地址。

文件偏移(Fill Offset):PE文件存儲在磁盤上時,相對於文件頭的偏移位置。16進制文件編輯器打開后的地址為文件偏移地址。

虛擬地址(VA) = 基址(Image Base) + 相對虛擬地址)(RVA)

RVA轉換為File Offset,給大家一個非常經典的公式:

設:VK為相對虛擬地址RVA與文件偏移地址File Offset的差值

VA=ImageBase+RVA

File Offset = RVA -VK

File Offset = VA-ImageBase-VK

 

 

+---------+---------+---------+---------+---------+---------+
|  段名稱   虛擬地址  虛擬大小  物理地址  物理大小   標志   |
+---------+---------+---------+---------+---------+---------+
|  Name     VOffset    VSize    ROffset    RSize      Flags |
+---------+---------+---------+---------+---------+---------+
|  .text   00001000   00000092  00000400  00000200  60000020|
|  .rdata  00002000   000000F6  00000600  00000200  40000040|
|  .data   00003000   0000018E  00000800  00000200  C0000040|
|  .rsrc   00004000   000003A0  00000A00  00000400  C0000040|
+---------+---------+---------+---------+---------+---------+
文件虛擬偏移地址和文件物理偏移地址的計算公式如下:

>>>>>>>VaToFileOffset(虛擬地址轉文件偏移地址)
如VA = 00401000 (虛擬地址)
ImageBase = 00400000 (基地址)
VRk = VOffset - ROffset = 00001000 - 00000400 = C00 (得出文件虛擬地址和文件物理址之間的VRk值)
FileOffset = VA - ImageBase - VRk = 00401000 - 00400000 - C00 = 400(文件物理地址的偏移地址)

如VA = 00401325,則:
FileOffset = VA - ImageBase - VRk = 00401325 - 00400000 - C00 = 725

>>>>>>FileOffsetToVa(文件偏移地址轉虛擬地址)
如FileOffset = 435(文件偏移地址)
VA = FileOffset + ImageBase + VRk = 435 + 00400000 + C00 = 00401035(虛擬地址)

 

通常,區塊中的數據在邏輯上是關聯的。PE 文件一般至少都會有兩個區塊:一個是代碼塊,另一個是數據塊。每一個區塊都需要有一個截然不同的名字,這個名字主要是用來表達區塊的用途。例如有一個區塊 叫.rdata,表明他是一個只讀區塊。注意:區塊在映像中是按起始地址(RVA)來排列的,而不是按字母表順序。
另外,使用區塊名字只是人們為了認識和編程的方便,而對操作系統來說這些是無關緊要的。微軟給這些區塊取了個有特色的名字,但這不是必須的。當編程從PE 文件中讀取需要的內容時,如輸入表、輸出表,不能以區塊名字作為參考,正確的方法是按照數據目錄表中的字段來進行定位。


各種區塊的描述:

區塊一般是從OBJ 文件開始,被編譯器放置的。鏈接器的工作就是合並左右OBJ 和庫中需要的塊,使其成為一個最終合適的區塊。鏈接器會遵循一套相當完整的規則,它會判斷哪些區塊將被合並以及如何被合並。

合並區塊:
鏈接器的一個有趣特征就是能夠合並區塊。如果兩個區塊有相似、一致性的屬性,那么它們在鏈接的時候能被合並成一個單一的區塊。這取決於是否開啟編譯器的 /merge 開關。事實上合並區塊有一個好處就是可以節省磁盤的內存空間……注意:我們不應該將.rsrc、.reloc、.pdata 合並到++的區塊里。

 

區塊的對齊值:

之前我們簡單了解過區塊是要對齊的,無論是在內存中存放還是在磁盤中存放~但他們一般的對齊值是不同的。

PE 文件頭里邊的FileAligment 定義了磁盤區塊的對齊值。每一個區塊從對齊值的倍數的偏移位置開始存放。而區塊的實際代碼或數據的大小不一定剛好是這么多,所以在多余的地方一般以00h 來填充,這就是區塊間的間隙。

例如,在PE文件中,一個典型的對齊值是200h ,這樣,每個區塊都將從200h 的倍數的文件偏移位置開始,假設第一個區塊在400h 處,長度為90h,那么從文件400h 到490h 為這一區塊的內容,而由於文件的對齊值是200h,所以為了使這一區塊的長度為FileAlignment 的整數倍,490h 到 600h 這一個區間都會被00h 填充,這段空間稱為區塊間隙,下一個區塊的開始地址為600h 。

PE 文件頭里邊的SectionAligment 定義了內存中區塊的對齊值。PE 文件被映射到內存中時,區塊總是至少從一個頁邊界開始。

一般在X86 系列的CPU 中,頁是按4KB(1000h)來排列的;在IA-64 上,是按8KB(2000h)來排列的。所以在X86 系統中,PE文件區塊的內存對齊值一般等於 1000h,每個區塊按1000h 的倍數在內存中存放。


RVA 和文件偏移的轉換

在前邊我們探討過RVA 這個詞,但對於初次接觸PE 文件的朋友來說,顯得尤其陌生和無奈。中國人不喜歡老外的縮寫,但總要**着接受……不過,在有了前邊知識的鋪墊之后,現在來談這個概念大家伙應該能夠得心應手了。起碼不用顯得那么的費解和無奈~

RVA 是相對虛擬地址(Relative Virtual Address)的縮寫,顧名思義,它是一個“相對地址”。PE 文件中的各種數據結構中涉及地址的字段大部分都是以 RVA 表示的,有木有??

更為准確的說,RVA 是當PE 文件被裝載到內存中后,某個數據位置相對於文件頭的偏移量。舉個例子,如果 Windows 裝載器將一個PE 文件裝入到 00400000h 處的內存中,而某個區塊中的某個數據被裝入 0040**xh 處,那么這個數據的 RVA 就是(0040**xh - 00400000h )= **xh,反過來說,將 RVA 的值加上文件被裝載的基地址,就可以找到數據在內存中的實際地址。

DOS 文件頭、PE 文件頭和區塊表的偏移位置與大小均沒有變化。而各個區塊映射到內存后,其偏移位置就發生了變化。

RVA 使得文件裝入內存后的數據定位變得方便,然而卻給我們要定位位於磁盤上的靜態PE 文件帶來了麻煩。


如何換算 RVA 和文件偏移呢?

當處理PE 文件時候,任何的 RVA 必須經過到文件偏移的換算,才能用來定位並訪問文件中的數據,但換算卻無法用一個簡單的公式來完成,事實上,唯一可用的方法就是最土最笨的方法:

步驟一:循環掃描區塊表得出每個區塊在內存中的起始 RVA(根據IMAGE_SECTION_HEADER 中的VirtualAddress 字段),並根據區塊的大小(根據IMAGE_SECTION_HEADER 中的SizeOfRawData 字段)算出區塊的結束 RVA(兩者相加即可),最后判斷目標 RVA 是否落在該區塊內。

步驟二:通過步驟一定位了目標 RVA 處於具體的某個區塊中后,那么用目標 RVA 減去該區塊的起始 RVA ,這樣就能得到目標 RVA 相對於起始地址的偏移量 RVA2.

步驟三:在區塊表中獲取該區塊在文件中所處的偏移地址(根據IMAGE_SECTION_HEADER 中的PointerToRawData 字段), 將這個偏移值加上步驟二得到的 RVA2 值,就得到了真正的文件偏移地址。

 

 

參考

書:《加密與解密》

視頻:小甲魚 解密系列 視頻

 

區塊

     在區塊表 后面的就是一個一個區塊,每個區塊占用對齊值的整數倍,一般的文件都有代碼塊 跟 數據塊( 它們的名字一般為.text 跟 .data 但這是可以修改的)。每個區塊的數據具有相同的屬性。編譯器先在obj中生成不同的區塊, 鏈接器再按照一定的規則合並不同obj跟庫中的快。例如每個obj中肯定有.text 塊, 連接器就會把它們合並成一個單一的.text 塊;再如,如果兩個區塊具有相同的的屬性就有可能被合並成一個塊。

 

文件偏移與RVA轉換

     因為磁盤對齊跟內存對齊可能不同。還有每個塊必須是對齊值的整數倍即在磁盤中可執行文件的每個塊的大小是磁盤對齊值的整數倍,程序加載到內存中后塊的大小是內存對齊值的整數倍。會導致文件偏移與RVA轉換的不同,需要轉換。

 

 

例子:轉換輸入表的RVA與文件偏移

    通過查看可執行文件的二進制可以找到輸入表的RVA,但是不能找到輸入表的文件偏移。

    先查看這個可執行文件的二進制,可以發現這個程序的磁盤對齊為200h內存對齊為1000h(IMAGE_OPTIONAL_HEADER中的SectionAlignment跟FileAlignment)

圖片1

    用LoadPE可以方便的得到:

 

圖片2

  再從可執行文件的二進制找到輸入表的RVA(IMAGE_OPTIONAL_HEADER中的DataDirectory[1]就是輸入表的信息)

 

圖片3

     也可以通過LoadPE方便的查看(點擊目錄,查看輸入表的RVA):

 

圖片4

     轉化文件偏移與RVA,通過上面的信息還不夠,還需要每個區塊在文件中跟在內存中的起始地址,可以用LoadPE方便的查看(點擊 區段 即可):

 

圖片5

 

    輸入表的RVA為204Ch,可以通過區段表知道,它在 .rdata 區段中,它相對 .rdata 頭 的值為 4Ch。.rdata 斷在磁盤中開始 的 文件偏移值為A00h,

 用4Ch 加 A00h 即可得到輸入表的文件偏移為A4Ch,通過LoadPE的“位置計算器” 可驗證輸入表的文件偏移值確實是A4Ch。

 

圖片6

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM