1. 程式人生 > 實用技巧 >EXE檔案的重定位

EXE檔案的重定位

EXE檔案的重定位

這份文件基於EXE的檔案格式

前面在解釋檔案頭的第[6-7] ,[18h-19h]位元組含義時並沒有說明什麼是重定位表,什麼是重定位項。因為這涉及到一個重要的概念叫做重定位。 這一小節就來說明什麼是重定位。

背景知識及定義

一個.asm彙編檔案,經過編譯器編譯後生成.obj檔案,再由連結器(linker)連結生成.exe檔案,也就是可執行程式,這個可執行程式被儲存在硬碟(disk)中。當我們執行這個程式時,載入器(loader)會把在硬碟中的可執行檔案載入到記憶體(memory)上的空餘位置,這時執行中的程式會就是程序。

當編譯器,連結器在生成可執行檔案時,並不會知道程式在真正執行時載入到哪一個實體地址上,所以編譯器對於段,變數,函式的編譯均採用相對地址

的形式進行。但是程式真正在記憶體中執行時,CPU是需要知道段,變數等真正的實體地址的。

因此,在載入程式到記憶體時,誰來將編譯器產生的相對地址轉換成絕對實體地址呢?這個操作又是如何完成的呢?

上面的第一個問題就是重定位要解決的事情,這裡我們給出重定位的定義:

作業系統根據EXE檔案頭中的重定位表將程式中引用的段地址進行修正的過程稱為重定位

接下里的內容由兩大部分組成:第一部分在驗證在編譯完成後,EXE檔案中的變數,段址記錄的都是相對值;第二部分用來說明重定位的具體過程。

儲存在硬碟中的EXE檔案

hello2.asm的具體程式碼如下:

data segment; 1000:0000
abc db "Hello, abc!$"; 12位元組=0Ch位元組 
    db 10h dup(0); 16位元組
data ends

code segment; 
assume cs:code, ds:data, ss:stk
begin:
   mov ax, data
   mov ds, ax
   mov ah, 9
   mov dx, offset abc
   int 21h
   mov ax, code; 編譯時code被設成以下值
               ; (code段-首個段的距離)/10h
   mov ds, ax
   mov ah, 9
   mov dx, offset xyz
   int 21h
   mov ah, 4Ch
   int 21h
xyz db "Hello, xyz!$"
main:
   jmp begin
code ends

stk segment stack
db 100h dup('$'); 為了方便檢視,將堆疊中的初始值改為'$'
stk ends
end main

通過Qview開啟hello2.exe檔案,跳過200h位元組的檔案頭,來到程式開始的地方

可以看到編譯後原始檔中的段,變數被編譯成了相對地址

  • mov ax, data--------------------mov ax, 0000-----------------data與第一個段地址的差(data)=0
  • mov dx, offset abc---------mov dx, 0000-----------------abc位置與該段段首的差=(0200Ch-0200h)=0h
  • mov ax, code-------------------mov ax, 0002-----------------code與data段地址的差=(0220h-0200h)/10h=2h
  • mov dx, offset xyz---------mov dx, 001C-----------------zxy位置與該段段首的差=(023Ch-0220h)=1Ch

根據上述結果,我們可以知道,在程式載入到記憶體中開始執行時,我們只要得到每個段的實體地址,我們就可以計算出每個變數的實體地址。那麼要如何根據程式的程式碼段來算出每個段的實體地址呢?這裡涉及到兩個問題:

  1. 如何判斷編譯後的exe檔案中指令中的立即數是一個地址?例如,mov ax,0000這條指令中0000是一個相對地址還是一個立即數呢?

    這就是檔案頭中重定位表項的作用了。重定位項一定指向需要重定位的段地址所在的地方

  2. 根據重定位項查到一個相對地址後,重定位的具體過程是怎樣的?

    作業系統把需要重定位的段地址取出來,和首段的段地址相加後再寫回去

    等重定位完成後,設定ds=es=psp設定ss:sp,再jmp cs:ip

重定位表

重定位表是由重定位項組成的,每一個重定位項指向需要重定位的段地址所在的地方。為了確定重定位項指向的具體地址,EXE檔案頭採用Δ段地址:偏移量的方式。

所以,每一個重定位項含有四個位元組:其中前兩個位元組是需要重定位的資料的偏移地址,後兩個位元組是段地址的Δ值,即需要(重定位的段地址-首段段地)址。

讀懂檔案頭中的重定位表

現在,我們通過具體的例子來說明重定位的過程。

  • [6-7]位元組:重定位表項的個數
  • [18h-19h]位元組:重定位表的相對檔案頭的偏移位置
  • 根據[18h-19h]位元組找到重定位表,以及[6-7]位元組確定重定位表的長度= 重定位表項的個數*4 個位元組

繼續看hello2.exe的檔案頭

00000000: 4d5a 5001 0200 0200 2000 0000 ffff 0500 MZP..... .......
00000010: 0001 e80e 2800 0200 1e00 0000 0100 0100 ....(...........
00000020: 0200 0d00 0200 0000 0000 0000 0000 0000 ................

加粗的部分均為在檔案頭中和重定位有關係的位元組

  • [6-7]位元組表示重定位表項的個數=0002h
  • [18h-19h]位元組表示重定位表的相對檔案頭的偏移位置位001eh
  • [1eh-25h]位元組表示重定位表中的兩項
    • 第一項:偏移量=0001h, Δ段地址=0002h
    • 第二項:偏移量=000dh, Δ段地址=0002h

獲得程式首段地址:程式的首段地址=psp+10h

假設程式首段地址=1010h,第一項重定位項的重定位過程如下:

  1. 確定需要重定位的位置:(1010+0002):0001=1012:0001
  2. 更改該位置中的段地址值:word ptr 1012:[0001] += 1010h

具體例子

  1. 通過td除錯hello2.exe檔案

  1. 當前ds=es=psp的段地址,首段地址=psp+10h=5292h+10h=52A2h

  2. 根據重定位項的第一項:偏移量=0001h, Δ段地址=0002h,找到需要重定位的地方是在52A4:0001

  3. 然後進行重定位:word ptr 52A4:[0001]=0000h+首段地址=0000h+52A2h=52A2h

    也就是mov ax,52A2這條指令中的52A2