PE文件結構詳解(三)PE導出表

上篇文章 PE文件結構詳解(二)可執行文件頭 的結尾出現了一個大數組,這個數組中的每一項都是一個特定的結構,通過函數獲取數組中的項可以用RtlImageDirectoryEntryToData函數,DataDirectory中的每一項都可以用這個函數獲取,函數原型如下:

PVOID NTAPI RtlImageDirectoryEntryToData(PVOID Base, BOOLEAN MappedAsImage, USHORT Directory, PULONG Size);

Base:模塊基地址。

MappedAsImage:是否映射爲映象。

Directory:數據目錄項的索引。

#define IMAGE_DIRECTORY_ENTRY_EXPORT          0   // Export Directory
#define IMAGE_DIRECTORY_ENTRY_IMPORT          1   // Import Directory
#define IMAGE_DIRECTORY_ENTRY_RESOURCE        2   // Resource Directory
#define IMAGE_DIRECTORY_ENTRY_EXCEPTION       3   // Exception Directory
#define IMAGE_DIRECTORY_ENTRY_SECURITY        4   // Security Directory
#define IMAGE_DIRECTORY_ENTRY_BASERELOC       5   // Base Relocation Table
#define IMAGE_DIRECTORY_ENTRY_DEBUG           6   // Debug Directory
//      IMAGE_DIRECTORY_ENTRY_COPYRIGHT       7   // (X86 usage)
#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE    7   // Architecture Specific Data
#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR       8   // RVA of GP
#define IMAGE_DIRECTORY_ENTRY_TLS             9   // TLS Directory
#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG    10   // Load Configuration Directory
#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT   11   // Bound Import Directory in headers
#define IMAGE_DIRECTORY_ENTRY_IAT            12   // Import Address Table
#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT   13   // Delay Load Import Descriptors
#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14   // COM Runtime descriptor

Size:對應數據目錄項的大小,比如Directory爲0,則表示導出表的大小。

返回值表示數據目錄項的起始地址。

這次來看看第一項:導出表。

導出表是用來描述模塊中的導出函數的結構,如果一個模塊導出了函數,那麼這個函數會被記錄在導出表中,這樣通過GetProcAddress函數就能動態獲取到函數的地址。函數導出的方式有兩種,一種是按名字導出,一種是按序號導出。這兩種導出方式在導出表中的描述方式也不相同。模塊的導出函數可以通過Dependency walker工具來查看:

上圖中紅框位置顯示的就是模塊的導出函數,有時候顯示的導出函數名字中有一些符號,像 ??0CP2PDownloadUIInterface@@QAE@ABV0@@Z,這種是導出了C++的函數名,編譯器將名字進行了修飾。

下面看一下導出表的定義吧:

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;

結構還算比較簡單,具體每一項的含義如下:

Characteristics:現在沒有用到,一般爲0。

TimeDateStamp:導出表生成的時間戳,由連接器生成。

MajorVersion,MinorVersion:看名字是版本,實際貌似沒有用,都是0。

Name:模塊的名字。

Base:序號的基數,按序號導出函數的序號值從Base開始遞增。

NumberOfFunctions:所有導出函數的數量。

NumberOfNames:按名字導出函數的數量。

AddressOfFunctions:一個RVA,指向一個DWORD數組,數組中的每一項是一個導出函數的RVA,順序與導出序號相同。

AddressOfNames:一個RVA,依然指向一個DWORD數組,數組中的每一項仍然是一個RVA,指向一個表示函數名字。

AddressOfNameOrdinals:一個RVA,還是指向一個WORD數組,數組中的每一項與AddressOfNames中的每一項對應,表示該名字的函數在AddressOfFunctions中的序號。

第一次接觸這個結構的童鞋被後面的5項搞暈了吧,理解這個結構比結構本身看上去要複雜一些,文字描述不管怎麼說都顯得晦澀,所謂一圖勝千言,無圖無真相,直接上圖:


在上圖中,AddressOfNames指向一個數組,數組裏保存着一組RVA,每個RVA指向一個字符串,這個字符串即導出的函數名,與這個函數名對應的是AddressOfNameOrdinals中的對應項。獲取導出函數地址時,先在AddressOfNames中找到對應的名字,比如Func2,他在AddressOfNames中是第二項,然後從AddressOfNameOrdinals中取出第二項的值,這裏是2,表示函數入口保存在AddressOfFunctions這個數組中下標爲2的項裏,即第三項,取出其中的值,加上模塊基地址便是導出函數的地址。如果函數是以序號導出的,那麼查找的時候直接用序號減去Base,得到的值就是函數在AddressOfFunctions中的下標。

用代碼實現如下:

DWORD* CEAT::SearchEAT( const char* szName)
{
    if (IS_VALID_PTR(m_pTable))
    {
        bool bByOrdinal = HIWORD(szName) == 0;
        DWORD* pProcs = (DWORD*)((char*)RVA2VA(m_pTable->AddressOfFunctions));
        if (bByOrdinal)
        {
            DWORD dwOrdinal = (DWORD)szName; 
            if (dwOrdinal < m_pTable->NumberOfFunctions && dwOrdinal >= m_pTable->Base)
            {
                return &pProcs[dwOrdinal-m_pTable->Base];
            }
        }
        else
        {
            WORD* pOrdinals = (WORD*)((char*)RVA2VA(m_pTable->AddressOfNameOrdinals));
            DWORD* pNames = (DWORD*)((char*)RVA2VA(m_pTable->AddressOfNames));
            for (unsigned int i=0; i<m_pTable->NumberOfNames; ++i)
            {
                char* pNameVA = (char*)RVA2VA(pNames[i]);
                if (strcmp(szName, pNameVA) != 0)
                {
                    continue;
                }
                return &pProcs[pOrdinals[i]];
            }
        }
    }
    return NULL;
}

by evil.eagle 轉載請註明出處。

http://blog.csdn.net/evileagle/article/details/12176797

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章