RVA與RWA的關係編程
原理比較簡單:首先判斷這個地址是否在PE頭中,若是在,文件偏移和內存偏移相等,若是存在於文件的區段中,則利用如下公式:
內存偏移 - 該段起始的RVA(VirtualAddress) = 文件偏移 - 該段的PointerToRawData
內存偏移 = 該段起始的RVA(VirtualAddress) + (文件偏移 - 該段的PointerToRawData)
文件偏移 = 該段的PointerToRawData + (內存偏移 - 該段起始的RVA(VirtualAddress))數據結構
DOS頭,PE頭,塊表在內存中的偏移和在磁盤中的偏移是一致的。其他的內存偏移和磁盤偏移是須要轉換的。編輯器
各類地址的概念:加密
基址(Image Base):PE文件裝入內存後的起始地址。spa
相對虛擬地址(Relative Virtual Address,RVA):在內存中相對於PE文件裝入地址的偏移位置,是一個相對地址。操作系統
虛擬地址(Virtual Address,VA):裝入內存中的實際地址。3d
文件偏移(Fill Offset):PE文件存儲在磁盤上時,相對於文件頭的偏移位置。16進制文件編輯器打開後的地址爲文件偏移地址。視頻
虛擬地址(VA) = 基址(Image Base) + 相對虛擬地址)(RVA)blog
將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