4、函數(shù)轉(zhuǎn)發(fā)器 函數(shù)轉(zhuǎn)發(fā)器(function forwarder)是DLL輸出段中的一個(gè)條目,用來將一個(gè)函數(shù)調(diào)用轉(zhuǎn)發(fā)到另一個(gè)DLL中的另一個(gè)函數(shù)。例如:如果用Visual C++的Dumpbin工具來查看Kernel32.dll那么我們會(huì)看到類似下面的輸出。 c:/Windows/System32>dumpbin -exports Kernel32.dll
710 2C5 RtlFillMemory (forwarded to NTDLL.RtlFillMemory) 711 2C6 RtlMoveMemory (forwarded to NTDLL.RtlMoveMemory) 712 2C7 RtlUnwind (forwarded to NTDLL.RtlUnwind) 713 2C8 RtlZeroMemory (forwarded to NTDLL.RtlZeroMemory) 這個(gè)輸出顯示了4個(gè)被轉(zhuǎn)發(fā)的函數(shù),如果應(yīng)用程序調(diào)用了RtlFillMemory,那么我們的可執(zhí)行文件會(huì)被動(dòng)的鏈接到Kernel32.dll。當(dāng)可執(zhí)行文件運(yùn)行的時(shí)候,加載程序會(huì)載入Kernel32.dll并發(fā)現(xiàn)被轉(zhuǎn)發(fā)的函數(shù)實(shí)際上是在NTDLL.dll中,然后他會(huì)將NTDLL.dll模塊也一并載入。當(dāng)可執(zhí)行文件調(diào)用RtlFillMemory的時(shí)候,它實(shí)際上調(diào)用的是NTDLL.dll中的RtlFillMemory函數(shù)。 如果我們調(diào)用RtlFillMemory ,那么GerProcAddress會(huì)先在Kernel32.dll的導(dǎo)出段中查找,并發(fā)現(xiàn)RtlFillMemory是一個(gè)轉(zhuǎn)發(fā)器函數(shù),于是它會(huì)遞歸調(diào)用GetProcAddress,在NTDLL的導(dǎo)出段中查找RtlFillMemory 函數(shù)。 我們也可以在自己的DLL模塊中使用函數(shù)轉(zhuǎn)發(fā)器,最簡單的方法是使用pragma指示符,如下所示: #pragma comment(linker,"/export:someFunc=DllWork.someOtherFunc") 這個(gè)pragma告訴鏈接器,正在編譯的DLL應(yīng)該導(dǎo)出一個(gè)名為someFunc的函數(shù),但實(shí)際上實(shí)現(xiàn)someFunc的是另一個(gè)名為someOtherFunc的函數(shù),該函數(shù)被包含在另一個(gè)DllWork.dll的模塊中。我們必須為每個(gè)想要轉(zhuǎn)發(fā)的函數(shù)單獨(dú)創(chuàng)建一行pragma。 5、已知的DLL 系統(tǒng)對(duì)操作系統(tǒng)提供的某些DLL進(jìn)行了特殊處理,這些DLL被稱為已知的DLL(known DLL)。除了操作系統(tǒng)在載入它們的時(shí)候總是在同一個(gè)目錄中查找之外,它們與其它的DLL并沒有什么不同。在注冊表中有這么一個(gè)注冊表項(xiàng): HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Control/Session Manager/KnownDLLs 我們可以看到,這個(gè)注冊表項(xiàng)包含了一組值名,這些值名是一些DLL的名稱。每個(gè)值名的數(shù)據(jù)正好等于值名加上.dll擴(kuò)展名。(但這并不是必須的,稍候就會(huì)看到)。當(dāng)LoadLibrary或LoadLibraryEx被調(diào)用的時(shí)候,函數(shù)首先會(huì)檢查我們傳入的DLL的名字是否包含了.dll擴(kuò)展名。如果沒有包含,那么函數(shù)會(huì)用正常的搜索順序來搜索這個(gè)DLL。 如果我們指定了.dll擴(kuò)展名,那么這兩個(gè)函數(shù)會(huì)先將擴(kuò)展名去掉,然后再在KnownDLLs注冊表中搜索,看其中是否有與之相符的值名。如果沒有值名與之相符,那么函數(shù)會(huì)使用正常的搜索規(guī)則。但是,如果找到了與之相符的值名,那么系統(tǒng)會(huì)查看與值名相對(duì)于的數(shù)據(jù),并試圖用該數(shù)據(jù)來載入DLL。系統(tǒng)還會(huì)從注冊表項(xiàng)的DllDirectory值所表示的目錄中開始搜索DLL。在Windows XP中DllDirectory的默認(rèn)值為%SystemRoot%/system32 為了舉例說明這一過程,假設(shè)我們在KnownDLLs注冊表項(xiàng)中添加了下列值: value name:SomeLib value data:SomeOtherLib.dll 調(diào)用LoadLibrary("SomeLib");的時(shí)候,系統(tǒng)會(huì)用正常的搜索規(guī)則來對(duì)這個(gè)DLL進(jìn)行定位. 但是,如果調(diào)用LoadLibrary("SomeLib.dll");的時(shí)候,系統(tǒng)會(huì)在knownDllsz注冊表中發(fā)現(xiàn)有一個(gè)與之相符的名稱。因此系統(tǒng)試圖載入的DLL是SomeOtherLib.dll而不是SomeLib.dll。它首先會(huì)在%SystemRoot%/system32目錄中查找SomeOtherLib.dll。如果在這個(gè)目錄中找到了該文件,那么系統(tǒng)就會(huì)將它載入,如果系統(tǒng)未能在這個(gè)目錄中找到該文件,那么LoadLibrary會(huì)失敗并返回NULL,這時(shí)調(diào)用GetLastError將返回ERROR_FILE_NOT_FOUND。 6、DLL重定向 最初開發(fā)Windows的時(shí)候,內(nèi)存和磁盤空間都非常寶貴,因此為了節(jié)約這些寶貴的資源,Windows的設(shè)計(jì)目標(biāo)是盡可能的共享資源。出于這樣的考慮,Microsoft建議將多個(gè)應(yīng)用程序所共享的模塊放在Windows的系統(tǒng)目錄中,這使得系統(tǒng)能方便的定位和共享文件。C/C++運(yùn)行庫以及MFC就是很好的例子。 隨著時(shí)間的推移,這成為了一個(gè)嚴(yán)重的問題,這是因?yàn)榘惭b程序可能會(huì)用老版本的文件覆蓋這個(gè)目錄中的文件,或用不完全兼容的新版本的文件覆蓋這個(gè)目錄中的文件,從而妨礙用戶的其它應(yīng)用程序的正常運(yùn)行。今天,硬盤不僅容量大而且價(jià)格便宜,內(nèi)存也夠用而且相對(duì)來說價(jià)格比以前要便宜很多。因此,Microsoft現(xiàn)在強(qiáng)烈建議開發(fā)人員將應(yīng)用程序的文件放到自己的目錄中,并且絕對(duì)不要碰Windows系統(tǒng)目錄中的任何東西。這樣既可以防止我們的應(yīng)用程序妨礙其它應(yīng)用程序,也可以避免其它應(yīng)用程序妨礙我們的應(yīng)用程序。 為了幫助開發(fā)人員,Microsoft從Windows 2000開始新增了一項(xiàng)DLL重定向特性。這項(xiàng)特性強(qiáng)制操作系統(tǒng)的加載程序 首先從應(yīng)用程序的目錄中載入模塊。只有加載程序無法找到要找的文件時(shí),才會(huì)在其它目錄中搜索。 為了強(qiáng)制加載程序總是先檢查應(yīng)用程序的目錄,我們所要做的就是將一個(gè)文件放到應(yīng)用程序的目錄中。這個(gè)文件的內(nèi)容無關(guān)緊要,但它的文件名必須是AppName.local。舉個(gè)例子,如果我們有一個(gè)名為SuperApp.exe的可執(zhí)行文件,那么重定向文件的名稱必須是SuperApp.exe.local。 LoadLibrary(Ex)在內(nèi)部做了修改,來檢查這個(gè)文件存在與否。如果應(yīng)用程序的目錄中存在這個(gè)文件,那么系統(tǒng)會(huì)載入這個(gè)目錄中的模塊。如果應(yīng)用程序的目錄中不存在這個(gè)文件,那么LoadLibrary的工作方式與以往相同。注意:除了創(chuàng)建一個(gè).local文件,我們還可以創(chuàng)建一個(gè)名為.locald的文件夾。在這種情況下,我們可以將自己的DLL保存在這個(gè)文件夾中,讓W(xué)indows能夠輕易找到他們。 注意:為了安全性的緣故,Windows vista中這項(xiàng)特性在默認(rèn)情況下是關(guān)閉的----因?yàn)樗赡軙?huì)使系統(tǒng)從應(yīng)用程序的文件夾中載入偽造的系統(tǒng)DLL,而不是從Windows的系統(tǒng)文件夾中載入真正的系統(tǒng)DLL。為了打開這項(xiàng)特性,我們必須在HKLM/SoftWare/Microsoft/WindowsNT/CurrentVersion/Image File Execution Options注冊表項(xiàng)中增加一個(gè)條目DWORD DevOverrideEnable,并將它的值設(shè)為1。 |
|