日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

PE文件和COFF文件格式分析——导出表

發布時間:2023/11/27 生活经验 42 豆豆
生活随笔 收集整理的這篇文章主要介紹了 PE文件和COFF文件格式分析——导出表 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

? ? ? ? 在之前的《PE可選文件頭》相關博文中我們介紹了可選文件頭中很多重要的屬性,而其中一個非常重要的屬性是(轉載請指明來源于breaksoftware的CSDN博客)

IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; 

? ? ? ? 該數組保存了如下節(不一定全包括,要以IMAGE_OPTIONAL_HEADER32(64)::NumberOfRvaAndSizes來確定)的信息

#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

? ? ? ? 我們在之后會介紹各個節的結構和相關應用,本文我將介紹該數組中第一個元素(DataDirectory[0])的信息——導出表信息。
? ? ? ? 首先我介紹下導出表。我們做程序時,新手一般喜歡做的是copy+paste。這個方法在代碼結構不是很復雜的時候還能過的去。如果像微軟這樣的系統也是這么寫,我想我們的黑客和漏洞挖掘者會非常高興了——因為這樣必定會產生更多的漏洞和bug。因為這樣寫的代碼非常難維護。打個比方,我們有個函數實現了對XML的解析,有ABCDE這么多業務方去copy了這段代碼。若干年后某天XML規則發生了改變,我們要修正XML解析算法,這個時候可能由于原來引入該段代碼的員工離職了或者時間久遠等原因,ABCDE各方都不知道自己的邏輯中用了XML,更不知道要去修正為新的算法。于是如何解決呢?ABCDE方應該讓XML解析算法編寫者提供一個.h和.cpp文件,里面包含了我們可能會調用的XML算法,然后在各自的代碼中include這個XML算法編寫者維護的目錄下的這個.h文件,并調用.h中的方法。這樣,以后XML算法即使改了,各業務方也可以保證我們使用的算法是最新的。但是還別高興的太早,還有個問題放在我們面前。如果我們的程序是一個獨立的Exe發布的話,在后續升級時會帶來些麻煩。比如我們發布的Exe文件是1G,可是發布后我們發現一行代碼寫錯了,于是我改了這行代碼,卻要讓用戶升級一個1G的文件!!流量啊!怎么辦?為了便于升級,我們還是把1G文件合理分割成若干個文件,并保證它們可以協同工作。DLL就是這樣被拆分出來的文件中一個非常重要的組成部分,它里面的導出函數就如同供其他方調用的XML解析的各種方法。導出表就是用于保存這些方法的名稱和地址等信息的地方。

? ? ? ? 現在我們來說下導出表節的結構。在DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]中保存了導出表節的相對虛擬偏移RVA和大小,在之后的章節中我們會發現除了DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY]保存的是RA(相對文件頭的偏移),其他都是RVA。通過該RVA,我們算出RA,從而得到一個描述導出表頭的結構體信息,該結構體是

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 imageDWORD   AddressOfNames;         // RVA from base of imageDWORD   AddressOfNameOrdinals;  // RVA from base of image
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;

? ? ? ? Characteristics是保留字段,要求為0。

? ? ? ? TimeDataStamp保存的生成導出信息的時間。

? ? ? ? MajorVersion和MinorVersion分別是主版本號和此版本號。這些信息是我們可以決定的。

? ? ? ? Name字段保存的該導出文件的名稱的偏移。這兒要注意一點,這個地址是系統不關心的,我們可以將其指向的地址設置為違法的地址,這樣會干擾部分PE分析工具的分析結果。

? ? ? ??Base是導出函數的起始序數值,該值一般為1。如我們用View dependencies打開一個文件,紅色部分就是Base字段相關的

? ? ? ??NumberOfFunctions標志導出函數的函數地址數。該數據是非常重要的,我們要知道該文件導出了多少個函數就是要依據這個信息。我們之后會詳細說的。

? ? ? ??NumberOfNames標志導出函數的函數名數量。

? ? ? ??AddressOfFunctions標志導出函數的函數地址表的RVA。

? ? ? ??AddressOfNames標志導出函數的函數名表的RVA。

? ? ? ??AddressOfNameOrdinals標志導出函數的導出序數表的RVA。

? ? ? ? 以我電腦上desktmon.dll為例,我們看一下該文件中該結構的布局

? ? ? ? 我們再用一個圖來描述一下PE導出表在View dependencies中顯示的相關關系

? ? ? ? 初次研究這個結構的同學可能會注意一個問題,該結構中有三個表的RVA(AddressOfFunctions,AddressOfNames,AddressOfNameOrdinals),而只給出了其中前兩個表的元素個數(NumberOfFunctions,NumberOfNames)。那第三個表——導出序數表的個數是多少?是按導出函數地址表(AddressOfFunctions)中元素個數(NumberOfFunctions)還是按導出函數名稱表(AddressOfNames)中元素個數(NumberOfNames)?還有個問題:為什么要設置Base屬性?這些問題我們先Mark下。我們先來詳細介紹這三個表。

? ? ? ? 導出地址表。顧名思義,該表中保存了函數入口RVA。但是如果僅僅是如此簡單就好了,這個地方保存的還可能是一個指向字符串的RVA!其結構是以下結構體的一個集合。

// 導出表信息
typedef struct _IMAGE_Export_Address_Table_
{union {DWORD dwExportRVA;DWORD dwForwarderRVA;};
}IMAGE_Export_Address_Table, *pIMAGE_Export_Address_Table;

? ? ? ? 如果它保存的是導出函數入口地址,那沒什么好說的。我們說下它保存的是指向一個字符串的偏移的情況。在我的XP系統下Kernel32.dll中AddVectoredExceptionHandler函數的導出函數地址指向的字符串是NTDLL.RtlAddVectoredExceptionHandler??吹竭@樣的名字組合,我想你大概能猜出個眉目。AddVectoredExceptionHandler函數,在Kernel32.dll文件內部是沒有實現的。但是如果有程序需要加載Kernel32.dll并需要調用這個函數,則這樣的寫法會告訴加載器在加載Kernel32.dll時,要將AddVectoredExceptionHandler函數的地址直接改成Ntdll.dll中的RtlAddVectoredExceptionHandler函數地址(即自動加載Ntdll.dll)。這個特性非常有趣吧!我想做加殼的朋友應該對這個場景很熟悉。我之后會介紹利用這個特性去隱性自動加載DLL。最后說一下,我們如何辨別這個字段保存的是函數的入口地址的RVA還是字符串呢?只要判斷該偏移不在導出表節中即可:指向的地址在節中就是字符串的RVA;在節外是函數入口的RVA。

? ? ? ? 導出名稱表。計算機做出來是給人用的,如果給人一堆010101這樣的數據,我想沒誰會有太多興趣去看的。于是出于人性化考慮,人們發明了別名,比如發明了匯編映射二進制指令,從而幫助理解程序邏輯。導出名稱表就是出于這樣的考慮而設計的。其結構是以下結構體的一個集合。

typedef struct _IMAGE_Export_Name_Pointer_Table_ {DWORD dwPointer;
}IMAGE_Export_Name_Pointer_Table,*pIMAGE_Export_Name_Pointer_Table;

? ? ? ? 它是指向字符串的RVA,該字符串是以\0結尾的。

? ? ? ? 說到這兒,我覺得我們可以停下思考一個問題,是不是只要有這兩個表就夠了?如果對于我們自己編寫的且非常標準的DLL,只要有這兩個表的確是夠了。你想,當我們調用GetProcAddress時,我們在導入名稱表中找到該名稱對應的index,然后再返回導出函數地址表中該index的數據即可。

lpFunc = ExportAddressTable[ExportNameTable.find(FuncName)]

? ? ? ? 但是,PE文件設計的遠沒有這么簡單。如果如此簡單,那很多事都好辦了。舉一個特殊的例子來推翻這種簡單的場景: 函數入口地址和函數名之間的關系是1對N(0~n)。我們程序運行起來后,很多時候是要調用其他邏輯,即函數入口??梢哉f一個函數入口可以唯一標注一個邏輯。而我們經常說的某某API,其實只是某個函數過程的一個名字。比如我們一個實現XML解析的函數,我們可以叫做ParseXML,也可以叫XMLParse。不管是叫哪個名字,該函數的功能是不變的,它的入口地址是不變的。如果入口地址變了,那就是另外一個函數了。這就是為什么說函數入口地址和函數名之間是1對N的關系。

?

? ? ? ??

? ? ? ? 針對以上問題,可能有人會想到,有多少個導出函數名(以導出函數名的數量為標準)就設置多少個導出地址,導出地址表中數據可以重復,比如上圖中ParseXML和XMLParse函數名對應的導出地址都設置成0xXXXXXXXX就行了嘛。如

但是還有個場景:windows平臺可以通過序數導入一個函數地址(GerProcAddress的第二個參數傳序數),那么這就意味著函數可以沒有函數名!!因為序數也可以看成一個函數的編號嘛,雖然這樣非常不友好,但是仍然是一種可行的方法。那么如果在這種場景下,我們還能以導出函數名的數量為標準么?不可以了吧,因為函數名表元素數量可能是0!其實這類文件挺多,如mfc40u.dll,見下圖

? ? ? ? 通過以上分析,我們可以得出,我們還是要一個能在導出函數地址表和導出函數名稱表建立紐帶的結構體。這個我們期待的輔助結構體就是我們下面介紹的導出序數表。

? ? ? ? 導出序數表。該表保存的是導出地址表的序數偏移!切記這個重要的概念。那這個偏移是相對什么偏移的呢?是針對IMAGE_EXPORT_DIRECTORY::Base屬性的。即這個表中保存的值加上Base,就是導出地址表的序數。其結構是以下結構體的一個集合。

typedef struct _IMAGE_Export_Ordinal_Table_ {WORD dwOrdinal;
}IMAGE_Export_Ordinal_Table,*pIMAGE_Export_Ordinal_Table;

? ? ? ? 從這個表的命名(AddressOfNameOrdinals )看,應該可以發現這個表應該和導出名稱表存在一定的關系!是的,它的元素的數量和導出名稱表的元素數量是一樣的??赡苡腥藭蓡?#xff0c;什么這個表元素的個數不是和導出地址表元素個數一致呢?因為如上面所說,一個函數過程可以對應多個函數名,如果導出序數表元素個數和導出函數地址表元素個數一樣,則無法讓地址與函數名對應上。比如我們導出地址表有1個函數入口,而我們有2個函數名都指向這個地址,那么導出序數表個數如果是1,則如何表示這兩個名稱與函數入口的對應呢?如果導出序數表格式是2個,則我們可以讓這兩個元素都“指向”同一個導出函數入口即可。OK,這兒我就解答了上面我們Mark過的那個問題:導出序數表個數和導出名稱表個數一致。

? ? ? ?那么這三個表之間具體什么關系呢?我首先以一個簡單的、常規的文件為例,這個文件是上面提到的deskmon.dll。我們看一下View Dependencies的分析結果:

? ? ? ? 我們再把它的PE文件拿出來看下

? ? ? ? 我們把各個信息提取出來看下:

Characteristics;        0x00000000
TimeDateStamp;          0x3B7D74B7
MajorVersion;           0x0000
MinorVersion;           0x0000
Name;                   0x00002E6C
Base;                   0x00000001
NumberOfFunctions;      0x00000002 
NumberOfNames;          0x00000002
AddressOfFunctions;     0x00002E58
AddressOfNames;         0x00002E60
AddressOfNameOrdinals;  0x00002E68

? ? ? ? 可以看到這個Dll的導出地址表有2個元素,導出名稱表和導出序數表也是有2個元素的。用之前《PE文件和COFF文件格式分析——RVA和RA相互計算》介紹的算法,我們可以得出

?? ? ? ?導出地址表RVA(0x00002E58)對應的RA是0x00002258。兩個元素分別為{ {0,0x0002218},{1,0x00002534}}。和View Dependencis分析結果對比發現,這組數據是一致的。

? ? ? ? 導出名稱表RVA(0x00002E60)對應的RA是0x00002260,其數據是{0,0x00002E78}和{1,0x00002E88}。0x00002E78是函數名的RVA,其對應的RA是0x00002278,即字符串“DllCanUnloadNow”;0x00002E88也是函數名稱的RVA,其RA是0x00002288,即字符串“DllGetClassObject”。于是可以把導出函數名表看成{{0,DllCanUnloadNow},{1,DllGetClassObject}}。這個數據和View Dependencies中信息一致。

? ? ? ? 導出序數表RVA(0x00002E68)對應的RA是0x00002268,其數據是{{0,0x0000},{1,0x0001}}。但是這并不是最終數據,剛才我在介紹導出序數表時,說過這個表保存的是相對Base的偏移,該文件的Base是1,于是真實的數據是{{0,0x0001},{1,0x0002}}。

? ? ? ?我們用圖來說一下這三者的關系。

? ? ? ?比如我們試圖得到DllGetClassObject的函數地址。我們現在名稱表中找到它的index是1。然后在序數表中找到index是1的元素的值0x00000002,。0x00000002要減去Base的值1得到值1。最后在地址表中找到index為1的元素的值,這個值就是DllGetClassObject函數的入口地址。表達式是

i = Search_ExportNamePointerTable (ExportName);
ordinal = ExportOrdinalTable [i];
SymbolRVA = ExportAddressTable [ordinal - OrdinalBase];

? ? ? ? 看了上面的邏輯, 我們在序數表中“加上”Base,然后要通過名稱去找函數入口時又要從序數表中“減去”Base,是不是這兒Base是多余的?如果單從通過名稱獲取函數地址來看,Base的確是多余的。那么如果通過序數來獲取函數地址呢?我構造了一個DLL——DllTestIndex.dll(工程地址)

LIBRARY	"DllTestIndex"
EXPORTSRet1 @2.Ret2 @4Ret3 @8Ret4 @6

?

? ? ? ? 發現通過序數去得到Ordinal為3的函數地址時會出錯。這兒有兩種可能:

? ? ? ? A GetProcAddress看到函數地址是0x00000000就認為獲取出錯

? ? ? ? B GetProcAddress是發現序數3不在序數表中(該文件導出序數表為{2,4,6,8},于是返回出錯。

? ? ? ? 那么到底是那種呢?我將這個文件修改成如下,即將Ordinal為3的函數地址修改成一個有效的函數地址,得到一個文件DllTestIndex_Modify

? ? ? ? 如果是B原因,則此時我們去獲取Ordinal為3的函數地址還是會失敗。可是結果呢?GetProcAddress成功了,并正確返回了0x00011069這個函數入口地址。這個實驗證明A原因是對的。這從而證明Base這個字段,對通過函數名尋找函數入口地址的算法的確是多余的信息。如果真要找個原因,可能從文件大小的說起。PE文件序數表的元素是WORD為單位的,而Base是DWORD。那么就是說,我們最多可以有0x10000(0x0000~0xFFFF)個導出函數。假如這些函數都在導出序數表中有對應元素,且導出序數表每個元素用DWORD描述,則需要sizeof(DWORD)*0x10000的空間。如果采用Base+WORD的方法,則只需要sizeof(WORD)*0x10000+sizeof(DWORD)的空間。采用后者最多可以節省0x3FFF8(0x40000-8)byte空間。其實這個空間很小的,可以忽略不計的。

? ? ? ?除了之上那個非常強求的原因,Base就沒用了么?不是!我們繼續看上面那個例子,從我們DEF文件中看出,我們希望導出的4個函數的序號分別是2、4、8、6。我們看下PE文件中的布局

? ? ? ? 我們看到信息如下:

? ? ? ? Base是0x00000002;NumberOfFunctions是0x00000007;導出函數地址分別為0x00011069、0x00000000、0x000110F5、0x00000000、0x000110A5、0x00000000、0x000110C3;導出名稱是按我們在DEF申明的順序是一致的,分別是:Ret1、Ret2、Ret3、Ret4。導出序數表是0x0000、0x0002、0x0006、0x0004。

? ? ? ? 注意View Dependencies的Ordinal列,該列的信息是函數地址的Index加上Base的值。于是

? ? ? 當我們如此調用時

typedef int(WINAPI* PRetN)();void ExprotFunc(LPSTR lpFileName) {HMODULE hDll = NULL;do {printf("%s\n", lpFileName);hDll = LoadLibraryA(lpFileName);if ( NULL == hDll ) {break;}for ( int nIndex = 0; nIndex <  10; nIndex ++ ) {PRetN pRetn = (PRetN)GetProcAddress( hDll, (LPCSTR)(LPVOID)(nIndex) );if ( NULL != pRetn ) {printf("nIndex is %d: Value is %d\n",nIndex,pRetn());}}FreeLibrary(hDll);} while (0);
}int _tmain(int argc, _TCHAR* argv[])
{ExprotFunc("DllTestIndex.dll");ExprotFunc("DllTestIndex_Modify.dll");system("pause");return 0;
}

? ? ? ? 此時GetProcAddress的第二個參數就是上圖中間一列的信息,即View Dependencies的Ordinals信息。這兒要特別注意這個Ordinals和導出序數表(AddressOfNameOrdinals指向的表)不是一個東西。這樣我就解答了上面我們Mark的那個問題——Base到底是為什么設計的?是為了通過序數導出函數而設計的。

? ? ? ? 之后我將會介紹幾個對導出表好玩的應用。

? ? ? ? 最后貼一段導出表解析的代碼

BOOL CGetPEInfo::GetExportInfo()
{BOOL bSuc = ( 0 == m_ExpDir.Characteristics ) ? TRUE : FALSE;if ( FALSE == bSuc ) {_ASSERT(FALSE);}m_ExpFullInfo.ExpDir = m_ExpDir;std::string wszTime;if ( FALSE == GetTime( m_ExpDir.TimeDateStamp, wszTime ) ) {_ASSERT(FALSE);}m_ExpFullInfo.wszTime = wszTime;std::string wszDllName;DWORD dwNameRA = 0;if ( FALSE == GetRAByRVA( m_ExpDir.Name, dwNameRA ) ) {_ASSERT(FALSE);}else {LPBYTE lpFileName = m_lpFileStart + dwNameRA;if ( lpFileName < m_lpFileStart || lpFileName > m_lpFileEnd ) {wszDllName.clear();}else {wszDllName = (LPSTR)lpFileName;}}m_ExpFullInfo.szImgName = wszDllName;MapIMAGE_Export_Address_Table MapImageExpAddrTable;DWORD dwFunAddrTableRA = 0;if ( FALSE ==  GetRAByRVA( m_ExpDir.AddressOfFunctions, dwFunAddrTableRA ) ) {if ( 0 != m_ExpDir.AddressOfFunctions ) {//_ASSERT(FALSE);}else {}}else {LPBYTE lpStart = m_lpFileStart + dwFunAddrTableRA;for ( WORD i = 0; i < m_ExpDir.NumberOfFunctions; i++ ) {IMAGE_Export_Address_Table ImgExpAddrTable;if ( FALSE == SafeCopy( &ImgExpAddrTable, lpStart, sizeof(IMAGE_Export_Address_Table)) ) {break;}MapImageExpAddrTable[i] = ImgExpAddrTable;lpStart += sizeof(IMAGE_Export_Address_Table);}}MapIMAGE_Export_Name_Pointer_Table MapImageExpNamePointerTable;DWORD dwFunNameTableRA = 0;if ( FALSE == GetRAByRVA( m_ExpDir.AddressOfNames, dwFunNameTableRA ) ) {if ( 0 != m_ExpDir.AddressOfNames ) {_ASSERT(FALSE);}else {}}else {LPBYTE lpStart = m_lpFileStart + dwFunNameTableRA;for ( DWORD i = 0; i < m_ExpDir.NumberOfNames; i++ ) {IMAGE_Export_Name_Pointer_Table ImgExpNamePointer;if ( FALSE == SafeCopy( &ImgExpNamePointer, lpStart , sizeof(IMAGE_Export_Name_Pointer_Table) ) ) {break;}MapImageExpNamePointerTable[i] = ImgExpNamePointer;lpStart += sizeof(IMAGE_Export_Name_Pointer_Table);}}MapIMAGE_Export_Ordinal_Table MapImageExpOrdinalTable;DWORD dwOrdinalTableRA = 0;if ( FALSE == GetRAByRVA( m_ExpDir.AddressOfNameOrdinals, dwOrdinalTableRA ) ) {if (  0 != m_ExpDir.AddressOfNameOrdinals ) {_ASSERT(FALSE);}else {//C:\Config.Msi\1ecac1a.rbf}}else {LPBYTE lpStart = m_lpFileStart + dwOrdinalTableRA;for ( WORD i = 0; i < m_ExpDir.NumberOfNames; i++ ) {IMAGE_Export_Ordinal_Table ImgExpOrdinalTable;if ( FALSE == SafeCopy( &ImgExpOrdinalTable, lpStart, sizeof(IMAGE_Export_Ordinal_Table) ) ) {break;}MapImageExpOrdinalTable[i] = ImgExpOrdinalTable;lpStart += sizeof(IMAGE_Export_Ordinal_Table);}}EXPORT_TABLE_FULL_INFO ExpTableFullInfo;if ( 0 != m_ExpDir.Base && 1 != m_ExpDir.Base ) {_ASSERT(FALSE);}if ( m_ExpDir.NumberOfNames != m_ExpDir.NumberOfFunctions ) {// _ASSERT(FALSE);}//  應該按地址數量來算for ( MapIMAGE_Export_Address_TableIter ImgExpAdIter = MapImageExpAddrTable.begin();ImgExpAdIter != MapImageExpAddrTable.end();ImgExpAdIter++ ){std::string strName;strName.empty();ExpTableFullInfo.wHint = 0xFFFF;ExpTableFullInfo.wOrdinal = ImgExpAdIter->first + (WORD)m_ExpDir.Base;DWORD dwRVA = ImgExpAdIter->second.dwExportRVA;if ( FALSE == IsRVAinSection( dwRVA, IMAGE_DIRECTORY_ENTRY_EXPORT )&& 0 != dwRVA ) {ExpTableFullInfo.bForwarderRVA = FALSE;ExpTableFullInfo.dwRVA.dwExportRVA = dwRVA;}else{ExpTableFullInfo.dwRVA.dwForwarderRVA = dwRVA;ExpTableFullInfo.bForwarderRVA = TRUE;DWORD dwForwarderRA;if ( FALSE == GetRAByRVA( ExpTableFullInfo.dwRVA.dwForwarderRVA, dwForwarderRA ) ) {if (  0 != ExpTableFullInfo.dwRVA.dwForwarderRVA ) {_ASSERT(FALSE);}else {// C:\4f1b3cac6fdc7b2cb9092b46e7c0fc71\Mobile Partner-dial\Mobile Partner-dial\mfc40u.dll// _ASSERT(FALSE);}//continue;}else {ExpTableFullInfo.strForwarder = (LPSTR)(m_lpFileStart + dwForwarderRA);}}MapIMAGE_Export_Ordinal_TableIter ImgExpOrdIter = MapImageExpOrdinalTable.begin();for ( ;ImgExpOrdIter != MapImageExpOrdinalTable.end();ImgExpOrdIter++ ){if ( ImgExpAdIter->first != ImgExpOrdIter->second.dwOrdinal ) {continue;}ExpTableFullInfo.wHint = ImgExpOrdIter->first;for ( MapIMAGE_Export_Name_Pointer_TableIter ImgExpNamePointerIter = MapImageExpNamePointerTable.begin();ImgExpNamePointerIter != MapImageExpNamePointerTable.end();ImgExpNamePointerIter++ ){if ( ImgExpNamePointerIter->first != ExpTableFullInfo.wHint ) {continue;}DWORD dwNamePointerRA = 0;if ( FALSE == GetRAByRVA( ImgExpNamePointerIter->second.dwPointer, dwNamePointerRA ) ) {continue;}else {strName =(LPSTR)(m_lpFileStart + dwNamePointerRA);}break;}break;}ExpTableFullInfo.strFuncName = strName;m_ExpFullInfo.vecExpTable.push_back( ExpTableFullInfo );}return bSuc;
}

?

總結

以上是生活随笔為你收集整理的PE文件和COFF文件格式分析——导出表的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。