程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> 托管PE文件

托管PE文件

編輯:關於.NET

中間語言

在.NET框架中,公共語言基礎結構使用CLS來綁定不同的語言。通過要求不同的語言至少要實現CTS包含在CLS中的部分,公共語言基礎結構允許不同的語言使用.NET框架。因此,在.NET框架中,所有的語言(C#、VB.NET、Effil.NET等)最後都被轉換為了一種通用語言:微軟中間語言(Microsoft Intermediate Language,MSIL,以下簡稱IL)。

IL是一種介於高級語言和基於Intel的匯編語言的中間語言,是.NET平台的匯編語言。當用戶編譯一個.NET程序時,編譯器將源代碼翻譯成一組可以有效地轉換為本機代碼且獨立於CPU的指令。當執行這些指令時,實時編譯器將它們轉化為CPU特定的代碼。由於CLR支持多種實時編譯器,因此同一段IL代碼可以被不同的編譯器實時編譯並運行在不同的結構上。

IL包括用於加載、存儲和初始化對象以及對對象調用方法的指令,還包括用於算術和邏輯運算、控制流、直接內存訪問、異常處理和其他操作的指令。要使代碼可運行,必須先將 IL 轉換為特定於 CPU 的代碼,這通常是通過實時(JIT) 編譯器來完成的。由於CLR為它支持的每種計算機結構都提供了一種或多種JIT編譯器,因此同一組IL可以在所支持的任何結構上JIT編譯和運行。

當編譯器產生IL時,它也產生元數據。元數據描述代碼中的類型,包括每種類型的定義、每種類型的成員的簽名、代碼引用的成員和運行庫在執行時使用的其他數據。IL和元數據包含在一個可移植可執行(PE)文件中,下面重點介紹托管PE文件,以及元數據的相關知識。

1.3.1       托管PE文件

PE(Portable Execute,可移植執行體)是微軟Windows操作系統上的程序文件,常見的如EXE、DLL、OCX、SYS、COM。圖1-3展示了標准的PE/COFF文件頭部格式。

 

圖1-3 標准的PE/COFF文件頭部格式

MS DOS頭是DOS系統的遺傳內容,表示一個應用程序可以在DOS環境下運行。MS DOS根(stub)是一段代碼,如果Windows程序在DOS環境下運行,會給出“該程序不能在DOS環境下運行”(This program cannot be run in DOS mode)的提示。在偏移量0x3c處,MS DOS頭指向了PE標識(PE Signature)的地址。

PE標識表示該文件是一個PE文件。其值始終為00004550h,45h代表字符E,50h代表字符P。

COFF頭(COFF Header)提供了COFF或者可執行文件的最一般的信息。

PE頭(PE Header)提供了操作系統加載文件所需的信息。這對於PE文件是最重要的地方,其間包含了數據索引表和節。

關於標准PE文件的詳細內容請讀者閱讀相關資料,本節內容只關注托管PE文件的特殊信息。CLR對傳統的PE文件進行了擴展,如圖1-4所示是托管PE文件的格式。

圖1-4 托管PE文件的格式

標准的Windows PE文件頭和COFF(通用對象文件格式)頭類似,分為PE32和PE32+兩種。如果文件頭采用PE32格式,則該文件可運行在32位或64位操作系統上。如果文件頭采用PE32+格式,則該文件只能在64位的操作系統上運行。PE32 或者 PE32+ 頭也包含文件類型信息:GUI、CUI或者DLL。如果包含本地CPU代碼的模塊,則PE32或者PE32+ 頭將包含有關本地CPU代碼的相關信息。

CLR頭包含使這個模塊被托管的相關信息。這些信息包括CLR需要的版本信息、一些標識、入口方法的元數據信息、模塊的元數據位置和大小信息、資源信息、強名稱和一些其他信息。

每一個托管模塊都包含元數據表。元數據表有兩種,一種是描述源代碼中的類型描述和成員描述的元數據表,另一種是包含源代碼引用的類型描述和成員描述的元數據表。

IL代碼是編譯器編譯產生的中間代碼,程序運行時CLR負責將中間代碼編譯成本地代碼執行。

CLR頭定義在.NET Framework的CorHdr.h中,代碼如代碼清單1-4所示。

代碼清單1-4 CLR 頭定義

typedef struct IMAGE_COR20_HEADER
    
{
    
ULONG cb;
    
USHORT MajorRuntimeVersion;
    
USHORT MinorRuntimeVersion;
    
// Symbol table and startup information
    
IMAGE_DATA_DIRECTORY MetaData;
    
ULONG Flags;
    
union {
    
DWORD EntryPointToken;
    
DWORD EntryPointRVA;
    
};
    
// Binding information
    
IMAGE_DATA_DIRECTORY Resources;
    
IMAGE_DATA_DIRECTORY StrongNameSignature;
    
// Regular fixup and binding information
    
IMAGE_DATA_DIRECTORY CodeManagerTable;
    
IMAGE_DATA_DIRECTORY VTableFixups;
    
IMAGE_DATA_DIRECTORY ExportAddressTableJumps;
    
IMAGE_DATA_DIRECTORY ManagedNativeHeader;
    
} IMAGE_COR20_HEADER;

關於CLR頭中的各個字段的解釋見表1-1,後文會對PE文件中的節信息做簡要介紹,關於PE文件的詳細信息參看書後附錄中的參考書籍。

表1-1  CLR頭字段說明

查看本欄目

頭信息的主要代碼如代碼清單1-5所示。

代碼清單1-5 HelloWorld.exe 頭信息

----- DOS Header:
    
Magic:                      0x5a4d
    
Bytes on last page:         0x0090
    
......(省略)
    
File addr. of COFF header:  0x0080
    
----- COFF/PE Headers:
    
Signature:                  0x00004550
    
----- COFF Header:
    
Machine:                    0x014c
    
Number of sections:         0x0003
    
Time-date stamp:            0x4b1b1d3a
    
Ptr to symbol table:        0x00000000
    
Number of symbols:          0x00000000
    
Size of optional header:    0x00e0
    
Characteristics:            0x0102
    
----- PE Optional Header (32 bit):
    
Magic:                          0x010b

    
......(省略)
    
Directory:      
    
......(省略)
    
Table:     
    
0x00000000 [0x00000000] address [size] of Delay Load IAT:           
    
0x00002008 [0x00000048] address [size] of CLR Header:               
    
......(節信息,略)
    
     
    
Base Relocation Table
    
              0x00002000 Page RVA
    
              0x0000000c Block Size
    
              0x00000002 Number of Entries
    
              Entry 1: Type 0x3 Offset 0x000007a0
    
              Entry 2: Type 0x0 Offset 0x00000000
    
     
    
Import Address Table
    
     DLL : mscoree.dll
    
          ......(省略)
    
     
    
Delay Load Import Address Table
    
// No data.
    
     
    
Entry point code:
    
FF 25 00 20 40 00
    
     
    
     
    
----- CLR Header:
    
Header size:                        0x00000048
    
Major runtime version:              0x0002
    
Minor runtime version:              0x0005
    
......(省略)        
    
     
    
Metadata Header
    
    Storage Signature:
    
             ......(省略)
    
    Storage Header:
    
                    0x00 Flags
    
                  0x0005 Number of Streams
    
    Stream 1:
    
              0x0000006c Offset
    
              0x000001e8 Size
    
              '#~' Name
    
   ......(省略)
    
    Stream 5:
    
              0x00000510 Offset
    
              0x00000130 Size
    
              '#Blob' Name
    
     
    
    Metadata Stream Header:
    
              0x00000000 Reserved
    
                    0x02 Major
    
                    0x00 Minor
    
                    0x00 Heaps
    
                    0x01 Rid
    
      0x0000000900001547 MaskValid
    
      0x000016003325fa00 Sorted
    
Code Manager Table:
    
  default
    
Export Address Table Jumps:
    
// No data.

上面代碼中涉及很多節信息,下面做簡要論述。

1. Relocation(重定位)

映像文件的.reloc節包括了Fixup表,它包含了映像文件中的所有定位項。.reloc節的RVA和大小都由PE頭的基地址重定位(Base Relocation)表目錄定義。Fixup表由定位塊組成,每個塊都包括了一個4KB頁的定位。這些塊都是4字節對齊的。

每個定位都描述了映像文件中特定地址的位置,以及操作系統加載程序在將映像文件載入內存的時候,應該如何修改這個位置上的地址。

每個定位塊都開始於兩個4字節無符號整數:頁面的RVA,這個頁面包含了需要定位的地址、塊的大小。緊隨其後的是頁面的定位項每個項都是16位寬的,其中的4個最高權重位包括了所需要的重定位類型,剩下的12位包括了頁面中重定位地址的偏移量。

為了對地址進行重定位,操作系統加載程序會計算出首選的基地址(PE頭的ImageBase字段)和實際加載映像文件的基地址之間的差異(delta)。接著根據重定位的類型,將這個delta應用到地址上。如果在首選位置加載映像文件,就不需要進行定位。

說明 Windows XP或者更新的版本都是支持CLR的操作系統,既不需要CLR的啟動Stub,也不需要IAT來調用CLR。因此,如果CLR頭標志指出映像文件是純IL(COMIMAGE_FLAGS_ ILONLY),那麼操作系統就會完全地忽略.reloc節。

2. Text(文本)

PE文件的.text節是只讀節。在托管PE文件中,它包括了元數據表、IL代碼、導入表、CLR頭、CLR非托管啟動Stub。在由IL匯編器生成的映像文件中,這個節還包括了托管資源、強簽名的散列值、調試數據以及非托管導出Stub。所以.text節是托管PE文件對傳統PE文件改變最多的地方。

圖1-6總結了由IL匯編器生成的映像文件的.text節的通用結構。

圖1-6 .text節的通用結構

3. Data(數據)

由IL匯編器生成的映像文件的數據節(.sdata)是可讀寫的節,它包括了數據常量、V表、非托管導出表以及TLS的目錄結構。聲明為特定於線程的數據位於一個不同的節,也就是.tls節。

4. Data Constants(數據常量)

數據常量代表了靜態字段的映射,通常包括映射字段的初始化數據。

字段映射是一種使用ANSI字符串、Blob或結構來初始化靜態字段的方法。另一種初始化靜態字段的方法(對於CLR來說更正式的方法)是通過在類的構造函數中顯式地進行初始化。

一方面,映射到數據節的字段就像類型控制和垃圾收集那樣,是CLR控制機制所觸及不到的;另一方面,它是完全開放的,可以不受限制地訪問和修改。這將導致加載程序阻止特定的字段類型被映射。映射字段的類型不能包括對象引用、向量、數組或任何非公共的子結構。如果為靜態字段初始化使用類的構造函數,就不會出現這樣的問題。

5. V-Table(V表)

在純粹的托管代碼模塊中,V表用於將托管方法公開給非托管代碼來調用。V表由一些項組成,每個項又由一個或多個槽組成。V表的這些項和槽都定義在V表定位中。每個定位指定了每個項中槽的數量和寬度(4字節或8字節)。V表的每個槽都包含各個方法的元數據標記,這些元數據標記在運行期間將會替換成方法本身的地址或者封送thunk,用於提供方法的非托管入口。因為這些定位是在運行期間執行的,所以托管PE文件的V表必須駐留於可讀寫的節中。IL匯編器將這個V表放在了.sdata節中,不像VTFixup表是駐留於.text節中的。

非托管映像文件的V表完全在鏈接期間定義,並只需操作系統加載程序執行的基地址重定位。因為在執行期間不需要改變V表(例如把方法標記替換成托管映像中的地址),所以非托管映像文件可以把它們的V表放在只讀節中。

6. Unmanaged Export Table(非托管導出表)

在非托管映像文件中的非托管導出表占據一個單獨的節——.edata。在IL匯編器生成的映像文件中,非托管導出表和它引用的V表都駐留於.sdata節中。

7. Thread Local Storage(線程局部存儲)

ILAsm和VC++允許用戶定義屬於TLS的數據常量,並將靜態字段映射到這些數據常量上。TLS是一種特殊的存儲類,類中的數據對象不是棧變量而是各個獨立線程的局部變量。因此,每個線程都可以為這樣的變量維護不同的值。

TLS數據在TLS目錄中描述,IL匯編器將其放置於.sdata節中。32位映像文件的TLS目錄結構定義在Winnt.h中,如代碼清單1-6所示。

代碼清單1-6 32位映像文件的TLS目錄結構

typedef struct _IMAGE_TLS_DIRECTORY32 {
    
ULONG StartAddressOfRawData;
    
ULONG EndAddressOfRawData;
    
ULONG AddressOfIndex;
    
ULONG AddressOfCallBacks;
    
ULONG SizeOfZeroFill;
    
ULONG Characteristics;
    
} IMAGE_TLS_DIRECTORY32;

64位映像(IMAGE_TLS_DIRECTORY64)的TLS目錄結構是類似的,除了開頭的4個字段是8字節無符號整數(ULONGLONG),而不是4字節無符號整數(ULONG)。

TLS目錄結構的RVA和大小存儲在PE頭的第10個數據目錄(TLS)中。構成了TLS模板的TLS數據常量,駐留於映像文件的.tls節中。

8. Resources(資源)

在托管PE文件中可以嵌入兩種不同的資源:特定於平台的非托管資源和特定於CLR的托管資源。它們駐留於托管映像文件的不同節,並通過不同的API進行訪問。

(1) Unmanaged Resources(非托管資源)

非托管資源在PE文件的.rsrc節中。嵌入的非托管資源的起始RVA和大小都在PE頭的資源數據目錄中表示。

非托管資源由類型、名稱和語言進行索引,並根據這三個特征的順序進行二進制排序。

IL匯編器創建.rsrc節,並且會嵌入命令行選項指定的.res文件中的非托管資源。編譯器只能為每個模塊嵌入一個非托管資源文件。

當IL反匯編器分析托管PE文件並找到.rsrc節的時候,它會從這個節中讀取數據和結構,並流並釋放出包括在PE文件中所有非托管資源的.res文件。

(2) Managed Resources(托管資源)

CLR頭的Resource字段包括了內嵌在PE文件中的托管資源的RVA和大小。它與PE頭的Resource目錄無關,後者指定了特定於平台的非托管資源的RVA和大小。

在IL匯編器生成的PE文件中,非托管資源駐留於映像文件的.rsrc節中,而托管資源和元數據、IL代碼等都位於.text節中。托管資源在.text節中連續地存放。元數據攜帶著ManifestResource記錄,每一筆記錄對應著一個托管資源,包括了托管資源的名稱以及從CLR頭的Resources字段中指定的起始RVA算起的資源開始處的偏移量。在這個偏移位置上,會使用4字節無符號整數指出資源的字節長度。緊跟其後的是資源本身。

當IL反匯編器處理托管映像文件並找到嵌入的托管資源時,它會將每個資源各自寫到根據資源名稱命名的單獨文件中。

當IL匯編器創建PE文件時,它會根據資源名稱讀取在源代碼中定義為嵌入資源的所有托管資源,將它們寫到.text節中,並在每個資源的前面放置該資源的指定長度。

作者:玄魂

出處:http://www.cnblogs.com/xuanhun/

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved