不知道有木有騷年和我一樣曾經疑惑過,我們說的分段機制轉換最開始的邏輯地址哪裏來的,比如程序的指令地址?要明白這個,我們首先需要知道代碼編譯成二進制後在磁盤上是怎麼組織的(以windows下的exe文件爲例)
舉個栗子:
.386
.model flat,stdcall
Option Casemap:none
include windows.inc
include user32.inc
includelib user32.lib
include kernel32.inc
includelib kernel32.lib
.data
szText db 'fwddsg',0
.code
Start:
invoke MessageBoxA,NULL,offset szText,offset szText,MB_OK
invoke ExitProcess,0
end Start
這段彙編使用MASMPlus編譯後會生成一個exe文件,功能很簡單,就是彈出個提示框。這段代碼啓動後第一條指令的地址是多少?
這邊是OD加載程序後的截圖,這邊可以看到,Start處的地址爲0x401000,’fwddsg’字符串的地址是0x403000,這些邏輯地址怎麼來的呢?我們隨便用款PE文件解析器打開程序:
PE頭信息:
這邊很容易看出,加載基址是0x400000,入口地址是0x1000,按這麼算第一條指令的地址恰好是0x401000,我們再看下代碼段和數據段在磁盤上的內容(想看文件在磁盤的內容,用UE打開exe文件即可)
代碼段:
這邊RVA虛地址0x1000在磁盤0x400(節數據地址)上對應的內容和OD上的內容一模一樣,也就是說此節保存的就是代碼編譯後的指令(當然.text不一定保存的全是指令,只要節屬性相同的內容,數據也可以放這邊),所以第一條指令的起始地址就是0x40000+0x1000=0x401000,這邊由於程序的main地址就是代碼節起始地址,所以可以這樣計算
數據段:
這邊的RVA虛地址0x3000在磁盤0x800(節數據地址)上對應的內容是’fwddsg’,加上起始地址0x400000=0x403000,這下知道常量字符串的地址怎麼算出來的嗎?(這邊要說下,dsg的含義據說語文好的認爲是大帥哥,語文不好的就認爲是大騷狗,很奇怪的是我身邊的人的語文好像都不怎麼好~)
上面PE文件的相關信息是在程序編譯連接的時候由編譯器和連接器完成的(注意這個時候保存的是RVA),所以最初的邏輯地址在程序編譯連接完成後基本就可以計算出來了,細心的同學會發現大部分程序的加載基址都是0x400000(高10位爲1),這個表示使用頁目錄的第一項,所有程序都用這一個基址沒問題嗎?別忘了,程序每次啓動,操作系統都會爲其分配一個獨一無二的頁目錄基址,所以程序線性地址的索引相同也無所謂
(這邊所有描述基本基於EXE文件,其他格式的PE文件可能有點不同,PE文件結構和加載詳細過程可參考《琢石成器》相關章節)