2010-10-28 89 views

回答

4

PE文件進出口

導入功能的相反的出口用於通過的EXE或其他DLL使用的功能。 PE文件在.edata節中存儲有關其導出函數的信息。通常,Microsoft鏈接器生成的PE EXE文件不會導出任何內容,因此它們沒有.edata節。 Borland的TLINK32總是從EXE導出至少一個符號。大多數DLL都會導出函數並具有.edata節。 .edata節的主要組件(又稱導出表)是函數名稱,入口點地址和導出序號值的表。在NE文件中,導出表的等同項是條目表,常駐名稱表和非常駐名稱表。這些表格作爲NE報頭的一部分存儲,而不是存儲在不同的段或資源中。

在.edata節的開頭是一個IMAGE_EXPORT_DIRECTORY結構(參見表10)。這種結構之後緊接着結構中的字段指向的數據。

表10 IMAGE_EXPORT_DIRECTORY格式

DWORD Characteristics 

此字段似乎是未使用的並且總是被設置爲0。

DWORD TimeDateStamp 

的時間/日期戳創建此文件時指示。

WORD MajorVersion 
WORD MinorVersion 

這些字段似乎是未使用的並且被設置爲0。

DWORD Name 

一個ASCIIZ串與該DLL的名稱的RVA。

DWORD Base 

導出函數的起始序號。例如,如果文件導出序數值爲10,11和12的函數,則此字段包含10.要獲取函數的導出序數,您需要將此值添加到AddressOfNameOrdinals數組的相應元素。

DWORD NumberOfFunctions 

AddressOfFunctions數組中的元素數。該值也是該模塊導出的函數的數量。理論上,該值可能與NumberOfNames字段(下一個)不同,但實際上它們始終相同。

DWORD NumberOfNames 

AddressOfNames數組中的元素數。該值似乎總是與NumberOfFunctions字段相同,導出函數的數量也是如此。

PDWORD *AddressOfFunctions 

該字段是一個RVA並指向函數地址的數組。函數地址是該模塊中每個導出函數的入口點(RVA)。

PDWORD *AddressOfNames 

此字段是一個RVA並指向一個字符串指針數組。字符串是此模塊中導出函數的名稱。

PWORD *AddressOfNameOrdinals 

此字段是RVA並指向WORD數組。 WORDs是本模塊中所有導出函數的導出序號。但是,不要忘記添加基本字段中指定的起始序號。

導出表的佈局有點奇怪(參見圖4和表10)。正如我前面提到的,導出函數的要求是名稱,地址和導出序號。你會認爲PE格式的設計者會將所有這三個項目放到一個結構中,然後有一組這樣的結構。相反,導出條目的每個組件都是數組中的一個元素。這些數組中有三個(AddressOfFunctions,AddressOfNames,AddressOfNameOrdinals),它們都相互平行。要查找關於第四個函數的所有信息,您需要查找每個數組中的第四個元素。

alt text

圖4.導出表佈局

表11.典型的出口從一個EXE文件

Name:   KERNEL32.dll 
    Characteristics: 00000000 
    TimeDateStamp: 2C4857D3 
    Version:   0.00 
    Ordinal base: 00000001 
    # of functions: 0000021F 
    # of Names:  0000021F 

    Entry Pt Ordn Name 
    00005090  1 AddAtomA 
    00005100  2 AddAtomW 
    00025540  3 AddConsoleAliasA 
    00025500  4 AddConsoleAliasW 
    00026AC0  5 AllocConsole 
    00001000  6 BackupRead 
    00001E90  7 BackupSeek 
    00002100  8 BackupWrite 
    0002520C  9 BaseAttachCompleteThunk 
    00024C50 10 BasepDebugDump 
    // Rest of table omitted... 

順便說一句,如果你傾倒了從出口表Windows NT系統DLL(例如,KERNEL32.DLL和USER32.DLL),你會注意到,在許多情況下,有兩個功能,只有不同名稱末尾的一個字符,例如CreateWindowExA和CreateWindowExW。 UNICODE支持如何透明地實現。以A結尾的函數是ASCII(或ANSI)兼容函數,而以W結尾的函數是函數的UNICODE版本。在你的代碼中,你沒有明確指定要調用的函數。相反,通過預處理程序#ifdefs在WINDOWS.H中選擇適當的函數。這從Windows NT WINDOWS.H摘錄顯示了這是如何工作的例子: 複製

#ifdef UNICODE 
#define DefWindowProc DefWindowProcW 
#else 
#define DefWindowProc DefWindowProcA 
#endif // !UNICODE 

// 
// Export Format 
// 

typedef struct _IMAGE_EXPORT_DIRECTORY { 
    DWORD Characteristics; 
    DWORD TimeDateStamp; 
    WORD MajorVersion; 
    WORD MinorVersion; 
    DWORD Name; 
    DWORD Base; 
    DWORD NumberOfFunctions; 
    DWORD NumberOfNames; 
    DWORD AddressOfFunctions;  // RVA from base of image 
    DWORD AddressOfNames;   // RVA from base of image 
    DWORD AddressOfNameOrdinals; // RVA from base of image 
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY; 

編輯:在PE格式導出表不指出錯誤,地址函數只是一個64位地址的RVA。

來源:http://msdn.microsoft.com/en-us/library/ms809762.aspx

+1

+1的答案:) – 2010-10-28 11:59:07

+1

的長度非常感謝你的詳細解答! – Elad 2010-10-28 12:02:09

+0

你碰巧在C#中有一段代碼是這樣做的嗎? – Elad 2010-10-28 12:02:39