天天看點

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 檔案中讀取需要的内容時,如輸入表、輸出表,不能以區塊名字作為參考,正确的方法是按照資料目錄表中的字段來進行定位。

各種區塊的描述:

RVA與RWA的關系
RVA與RWA的關系
RVA與RWA的關系

區塊一般是從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與RWA的關系

例子:轉換輸入表的RVA與檔案偏移

    通過檢視可執行檔案的二進制可以找到輸入表的RVA,但是不能找到輸入表的檔案偏移。

    先檢視這個可執行檔案的二進制,可以發現這個程式的磁盤對齊為200h記憶體對齊為1000h(IMAGE_OPTIONAL_HEADER中的SectionAlignment跟FileAlignment)

圖檔1

RVA與RWA的關系

    用LoadPE可以友善的得到:

圖檔2

RVA與RWA的關系

  再從可執行檔案的二進制找到輸入表的RVA(IMAGE_OPTIONAL_HEADER中的DataDirectory[1]就是輸入表的資訊)

圖檔3

RVA與RWA的關系

     也可以通過LoadPE友善的檢視(點選目錄,檢視輸入表的RVA):

圖檔4

RVA與RWA的關系

     轉化檔案偏移與RVA,通過上面的資訊還不夠,還需要每個區塊在檔案中跟在記憶體中的起始位址,可以用LoadPE友善的檢視(點選 區段 即可):

圖檔5

RVA與RWA的關系

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

 用4Ch 加 A00h 即可得到輸入表的檔案偏移為A4Ch,通過LoadPE的“位置電腦” 可驗證輸入表的檔案偏移值确實是A4Ch。

圖檔6

RVA與RWA的關系

轉載于:https://www.cnblogs.com/gd-luojialin/p/11306329.html