破解軟體感悟-PE檔案格式之Import Table(引入表)(四)

來源:互聯網
上載者:User

標籤:des   os   io   strong   ar   for   檔案   資料   div   

先來看一個可執行檔的執行個體:本常式開啟一PE檔案,將所有引入dll和對應的函數名讀入一編輯控制項,同時顯示 IMAGE_IMPORT_DESCRIPTOR 結構各域值。

 

C:\QQDownload\blah.EXE

 

================[ IMAGE_IMPORT_DESCRIPTOR ]=============

OriginalFirstThunk  =  303C

TimeDateStamp  =  0

ForwarderChain = 0

Name = KERNEL32.dll

FirstThunk = 3064

 

Hint  Function

-----------------------------------------

0       CreateFileA

0       FreeLibrary

0       ExitProcess

0       LoadLibraryA

0       ReadFile

0       WriteFile

0       GetProcAddress

 

================[ IMAGE_IMPORT_DESCRIPTOR ]=============

OriginalFirstThunk  =  305C

TimeDateStamp  =  0

ForwarderChain = 0

Name = USER32.dll

FirstThunk = 3084

 

Hint  Function

-----------------------------------------

0           MessageBoxA

上面的資料我們如何得到的呢?

理論:

一.首先,您得瞭解什麼是引入函數。一個引入函數是被某模組調用的但又不在調用者模組中的函數,因而命名為"import(引入)"。引入函數實際位於一個或者更多的DLL裡。調用者模組裡只保留一些函數資訊,包括函數名及其駐留的DLL名。現在,我們怎樣才能找到PE檔案中儲存的資訊呢? 轉到 data directory 尋求答案吧。再回顧一把,下面就是 PE header:

IMAGE_NT_HEADERS STRUCT
   Signature dd ?
   FileHeader IMAGE_FILE_HEADER <>
   OptionalHeader IMAGE_OPTIONAL_HEADER <>
IMAGE_NT_HEADERS ENDS

optional header 最後一個成員就是 data directory(資料目錄):

IMAGE_OPTIONAL_HEADER32 STRUCT
   .... 
   LoaderFlags dd ? 
   NumberOfRvaAndSizes dd ? 
   DataDirectory IMAGE_DATA_DIRECTORY 16 dup(<>) 
IMAGE_OPTIONAL_HEADER32 ENDS

data directory 是一個 IMAGE_DATA_DIRECTORY 結構數組,共有16個成員。如果您還記得節表可以看作是PE檔案各節的根目錄的話,也可以認為 data directory 是儲存在這些節裡的邏輯元素的根目錄。明確點,data directory 包含了PE檔案中各重要資料結構的位置和尺寸資訊。每個成員包含了一個重要資料結構的資訊。

 

Member

Info inside

0

Export symbols

1

Import symbols

2

Resources

3

Exception

4

Security

5

Base relocation

6

Debug

7

Copyright string

8

Unknown

9

Thread local storage (TLS)

10

Load configuration

11

Bound Import

12

Import Address Table

13

Delay Import

14

COM descriptor

 

上面那些金色顯示的是我熟悉的。瞭解 data directory 包含域後,我們可以仔細研究它們了。data directory 的每個成員都是 IMAGE_DATA_DIRECTORY 結構類型的,其定義如下所示:

IMAGE_DATA_DIRECTORY STRUCT 
  VirtualAddress dd ? 
  isize dd ? 
IMAGE_DATA_DIRECTORY ENDS

VirtualAddress 實際上是資料結構的相對虛擬位址(RVA)。比如,如果該結構是關於import symbols的,該域就包含指向IMAGE_IMPORT_DESCRIPTOR 數組的RVA。 
isize 含有VirtualAddress所指向資料結構的位元組數。

下面就是如何找尋PE檔案中重要資料結構的一般方法:

1.      從 DOS header 定位到 PE header

2.      從 optional header 讀取 data directory 的地址。

3.      IMAGE_DATA_DIRECTORY 結構尺寸乘上找尋結構的索引號: 比如您要找尋import symbols的位置資訊,必須用IMAGE_DATA_DIRECTORY 結構尺寸(8 bytes)乘上1(import symbols在data directory中的索引號)。

4.      將上面的結果加上data directory地址,我們就得到包含所查詢資料結構資訊的 IMAGE_DATA_DIRECTORY 結構項。

小段總結:以上說明data directory是一個結構數組,這裡麵包含16個結構成員,每一個結構成員是IMAGE_DATA_DIRECTORY 結構類型,而這種結構類型包含VirtualAddress dd ? 
  isize dd ?

 

二.現在我們開始真正討論引入表了。

 

1

Import symbols

 

從上面已經分析過了,這是一個IMAGE_DATA_DIRECTORY 結構類型,這個結構類型中包含VirtualAddress,這個裡面放的是引入表地址

什麼是引入表呢?

引入表實際上是一個 IMAGE_IMPORT_DESCRIPTOR 結構數組。如下:

IMAGE_IMPORT_DESCRIPTOR STRUCT 
  union 
    Characteristics dd ? 
    OriginalFirstThunk dd ? 
  ends 
  TimeDateStamp dd ? 
  ForwarderChain dd ? 
  Name1 dd ? 
  FirstThunk dd ? 
IMAGE_IMPORT_DESCRIPTOR ENDS

結構第一項是一個union子結構。 事實上,這個union子結構只是給 OriginalFirstThunk 增添了個別名,您也可以稱其為"Characteristics"。 該成員項含有指向一個 IMAGE_THUNK_DATA 結構數組的RVA。

什麼是 IMAGE_THUNK_DATA? 這是一個dword類型的集合。通常我們將其解釋為指向一個 IMAGE_IMPORT_BY_NAME 結構的指標。注意 IMAGE_THUNK_DATA 包含了指向一個IMAGE_IMPORT_BY_NAME 結構的指標: 而不是結構本身。

我們用通俗的語言表示就是:

現有幾個 IMAGE_IMPORT_BY_NAME 結構,我們收集起這些結構的RVA (IMAGE_THUNK_DATA)組成一個數組,並以0結尾,然後再將數組的RVA放入 OriginalFirstThunk

此 IMAGE_IMPORT_BY_NAME 結構存有一個引入函數的相關資訊。再來研究 IMAGE_IMPORT_BY_NAME 結構到底是什麼樣子的呢:

IMAGE_IMPORT_BY_NAME STRUCT 
  Hint dw ? 
  Name1 db ? 
IMAGE_IMPORT_BY_NAME ENDS

Hint 指示本函數在其所駐留DLL的引出表中的索引號。該域被PE裝載器用來在DLL的引出表裡快速查詢函數。該值不是必須的,一些連接器將此值設為0。
Name1 含有引入函數的函數名。函數名是一個ASCIIZ字串。注意這裡雖然將Name1的大小定義成位元組,其實它是可變尺寸域,只不過我們沒有更好方法來表示結構中的可變尺寸域。The structure is provided so that you can refer to the data structure with descriptive names.

小段總結: 可以這樣理解上面這段話:

OriginalFirstThunk中的每個成員項實際上是一個指向IMAGE_THUNK_DATA結構數組中的每個成員

IMAGE_THUNK_DATA結構數組中存放的是指向IMAGE_IMPORT_BY_NAME 結構數組的指標

IMAGE_IMPORT_BY_NAME結構數組存放的是每個函數的RAV

三.好了,如果您還在犯糊塗,就朝這邊看過來: 現在有幾個 IMAGE_IMPORT_BY_NAME 結構,同時您又建立了兩個結構數組,並同樣寸入指向那些 IMAGE_IMPORT_BY_NAME 結構的RVAs,這樣兩個數組就包含相同數值了(可謂相當精確的複製啊)。 最後您決定將第一個數組的RVA賦給 OriginalFirstThunk第二個數組的RVA賦給 FirstThunk,這樣一切都很清楚了。

 

OriginalFirstThunk

 

IMAGE_IMPORT_BY_NAME

 

FirstThunk

|

 

 

 

|

IMAGE_THUNK_DATA

IMAGE_THUNK_DATA

IMAGE_THUNK_DATA

IMAGE_THUNK_DATA

...

IMAGE_THUNK_DATA

 

--->

--->

--->

--->

--->

--->

 

Function 1

Function 2

Function 3

Function 4

...

Function n

 

<---

<---

<---

<---

<---

<---

 

IMAGE_THUNK_DATA

IMAGE_THUNK_DATA

IMAGE_THUNK_DATA

IMAGE_THUNK_DATA

...

IMAGE_THUNK_DATA

 

 

現在您應該明白我的意思。不要被IMAGE_THUNK_DATA這個名字弄糊塗: 它僅是指向 IMAGE_IMPORT_BY_NAME 結構的RVA。 如果將 IMAGE_THUNK_DATA 字眼想象成RVA,就更容易明白了。OriginalFirstThunk 和 FirstThunk 所指向的這兩個數組大小取決於PE檔案從DLL中引入函數的數目。比如,如果PE檔案從kernel32.dll中引入10個函數,那麼IMAGE_IMPORT_DESCRIPTOR 結構的 Name1域包含指向字串"kernel32.dll"的RVA,同時每個IMAGE_THUNK_DATA 數組有10個元素。

下一個問題是: 為什麼我們需要兩個完全相同的數組? 為了回答該問題,我們需要瞭解當PE檔案被裝載到記憶體時,PE裝載器將尋找IMAGE_THUNK_DATA 和 IMAGE_IMPORT_BY_NAME 這些結構數組,以此決定引入函數的地址。然後用引入函數真真實位址來替代由FirstThunk指向的 IMAGE_THUNK_DATA 數組裡的元素值。因此當PE檔案準備執行時,已轉換成:

 

OriginalFirstThunk

 

IMAGE_IMPORT_BY_NAME

 

FirstThunk

|

 

 

 

|

IMAGE_THUNK_DATA

IMAGE_THUNK_DATA

IMAGE_THUNK_DATA

IMAGE_THUNK_DATA

...

IMAGE_THUNK_DATA

 

--->

--->

--->

--->

--->

--->

 

Function 1

Function 2

Function 3

Function 4

...

Function n

 

   

 

 

 

 

 

 

Address of Function 1

Address of Function 2

Address of Function 3

Address of Function 4

...

Address of Function n

 

 

OriginalFirstThunk 指向的RVA數組始終不會改變,所以若還反過頭來尋找引入函數名,PE裝載器還能找尋到。
當然再簡單的事物都有其複雜的一面。有些情況下一些函數僅由序數引出,也就是說您不能用函數名來調用它們: 您只能用它們的位置來調用。此時,調用者模組中就不存在該函數的IMAGE_IMPORT_BY_NAME 結構。不同的,對應該函數的 IMAGE_THUNK_DATA 值的低位字指示函數序數,而最高二進位 (MSB)設為1。例如,如果一個函數只由序數引出且其序數是1234h,那麼對應該函數的 IMAGE_THUNK_DATA 值是80001234h。Microsoft提供了一個方便的常量來測試dword值的MSB位,就是 IMAGE_ORDINAL_FLAG32,其值為80000000h。
假設我們要列出某個PE檔案的所有引入函數,可以照著下面步驟走:

  1. 校正檔案是否是有效PE。
  2. 從 DOS header 定位到 PE header。
  3. 擷取位於 OptionalHeader 資料目錄位址。
  4. 轉至資料目錄的第二個成員提取其VirtualAddress值。
  5. 利用上值定位第一個 IMAGE_IMPORT_DESCRIPTOR 結構。
  6. 檢查 OriginalFirstThunk值。若不為0,順著 OriginalFirstThunk 裡的RVA值轉入那個RVA數組。若 OriginalFirstThunk 為0,就改用FirstThunk值。有些連接器產生PE檔案時會置OriginalFirstThunk值為0,這應該算是個bug。不過為了安全起見,我們還是檢查 OriginalFirstThunk值先。
  7. 對於每個數組元素,我們比對元素值是否等於IMAGE_ORDINAL_FLAG32如果該元素值的最高二進位為1, 那麼函數是由序數引入的,可以從該值的低位元組提取序數。
  8. 如果元素值的最高二進位為0,就可將該值作為RVA轉入 IMAGE_IMPORT_BY_NAME 數組,跳過 Hint 就是函數名字了。
  9. 再跳至下一個數組元素提取函數名一直到數組底部(它以null結尾)。現在我們已遍曆完一個DLL的引入函數,接下去處理下一個DLL。
  10. 即跳轉到下一個 IMAGE_IMPORT_DESCRIPTOR 並處理之,如此這般迴圈直到數組見底。(IMAGE_IMPORT_DESCRIPTOR 數組以一個全0域元素結尾)。

 

破解軟體感悟-PE檔案格式之Import Table(引入表)(四)

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.