我知道我可以閱讀PE specification爲了編寫這樣做的代碼。 但是,由於我手上沒有太多時間,我希望你們中的一些人可能已經準備好了這樣的代碼示例。如何讀取C#中(native)DLL的導出函數名稱?
重要注意事項: 32位和64位有什麼不同?
謝謝你的時間!
我知道我可以閱讀PE specification爲了編寫這樣做的代碼。 但是,由於我手上沒有太多時間,我希望你們中的一些人可能已經準備好了這樣的代碼示例。如何讀取C#中(native)DLL的導出函數名稱?
重要注意事項: 32位和64位有什麼不同?
謝謝你的時間!
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),它們都相互平行。要查找關於第四個函數的所有信息,您需要查找每個數組中的第四個元素。
圖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。
+1的答案:) – 2010-10-28 11:59:07
的長度非常感謝你的詳細解答! – Elad 2010-10-28 12:02:09
你碰巧在C#中有一段代碼是這樣做的嗎? – Elad 2010-10-28 12:02:39