久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

Windows下動態(tài)鏈接之一:DLL插件機(jī)制的裝載和使用

 waston 2018-07-17

1. Windows和Linux下動態(tài)鏈接的原則不同

Linux系統(tǒng)以.so共享對象設(shè)計共享庫,,并在設(shè)計共享對象的過程,花費(fèi)很多精力實現(xiàn).so對象的代碼段.text多進(jìn)程共享,,提升空間利用率(如PIC機(jī)制,、SO-NAME機(jī)制、符號版本機(jī)制等),;Windows系統(tǒng)設(shè)計.dll(DLL文件還可以是別的擴(kuò)展名,,如OCX控件.ocx,控制面板程序.cpl)則并不像Linux那么小家子氣,,并沒有花費(fèi)太多精力設(shè)計一些精巧的結(jié)構(gòu)或機(jī)制來提升空間利用率,,更側(cè)重程序邏輯上的模塊化,使得各模塊之間能夠自由松散地組合,、重用和升級,。所以Windows系統(tǒng)下可以看到各種各樣的軟件是通過升級DLL的形式來進(jìn)行版本迭代,微軟本身的系統(tǒng)更新也是將這些升級補(bǔ)丁積累到一定程度后發(fā)布一個軟件更新包來實現(xiàn)系統(tǒng)升級,。

Windows COFF/PE文件結(jié)構(gòu)下的.exe可執(zhí)行文件或.dll文件都需要被映射到虛擬內(nèi)存空間中才能得以運(yùn)行,,相比于Linux采用的精打細(xì)算的代碼地址無關(guān)機(jī)制,,Windows下的文件采用的是一種叫做基地址重載的方式,即并沒有采用代碼地址無關(guān),,所有DLL涉及到的絕對地址的引用在實際裝載時都需要重定位,。所以要理解PE文件結(jié)構(gòu)下的文件鏈接和裝載過程就不得不談兩個概念:映像基地址(Image-Base-Address)和相對地址(RVA, Relative-Virtual-Address),。

2.映像基地址

在Windows PE文件結(jié)構(gòu)下,,當(dāng)一個PE文件被裝載時,其對應(yīng)的虛擬空間的起始地址便是基地址,,而任一PE文件在編譯時便存在一個指定(或默認(rèn))的優(yōu)先裝載地址,,如.exe文件一般的基地址為0x0040 0000,而.dll文件的基地址一般默認(rèn)是0x1000 0000,。系統(tǒng)在裝載.exe文件時,,因為可執(zhí)行文件是第一個被加載的文件,顯然沒人和.exe搶默認(rèn)的基地址空間,,從而.exe文件是不需要基地址重定位的,;而DLL文件裝載時,則可能遇到默認(rèn)指定的優(yōu)先基地址空間被別人搶占了,,故而這時就需要重新選擇可用的空閑地址,,這時整個文件將產(chǎn)生整體位移。

3.RVA

相對地址顧名思義就是在PE文件基地址確認(rèn)后,,一個地址相對于基地址的偏移量,,比如一個PE DLL文件默認(rèn)基地址為0x1000 0000,一個符號存儲位置的RVA為0x1000,,DLL文件編譯時有以下賦值操作:

MOV  DWORD PTR [0x1000 1000],  0x20

該文件實際裝載時,,被基地址重載到0x4000 0000,則符號對應(yīng)的賦值操作應(yīng)該變成:

MOV  DWORD PTR [0x1000 1000 + 0x2000 0000 - 0x1000 0000],  0x20 //此處減法只是顯示重定位的原理,,實際這一步計算在鏈接階段就已經(jīng)完成了,,此處填入的應(yīng)該是計算結(jié)果

4.DLL文件的符號導(dǎo)出聲明

ELF文件.so共享對象,在默認(rèn)情況下,,文件中所有的全局變量和全局函數(shù)都是導(dǎo)出的(除非加static修飾符限制方位范圍),。 
DLL文件在默認(rèn)情況下,是不導(dǎo)出任何符號的,,如果要導(dǎo)出需要手動指明,,有兩種方法: 
1. “__declspec(dllexport)” 修飾符指明該符號導(dǎo)出,對應(yīng)的,,”__delspec(dllimport)”修飾符是指明該符號從外部導(dǎo)入(如果是C++ 文件,,但是希望導(dǎo)出函數(shù)符號的修飾規(guī)則使用C的簡潔修飾規(guī)則,那么需要再在函數(shù)符號前面添加external “C”,。實際上,,不推薦使用C++編寫DLL,,因為C++只規(guī)定了語言層面的規(guī)則,但是ABI二進(jìn)制層面并沒有定義,,故而不同編譯器甚至同一編譯器的不同版本的具體實現(xiàn)都可能不同,,故而很容易出現(xiàn)版本不兼容或升級困難等問題。如果一定要用C++編寫,,需要涉及到COM(Component Object Model)技術(shù)); 
2. 編寫.def鏈接腳本,,批量聲明導(dǎo)出符號。如下是某.def鏈接腳本聲明該DLL的導(dǎo)出符號,。

LIBRARY  Math
EXPORTS
Add   @1
Sub   @2
Mul   @3
Div    @4  NONAME

.def文件是在輸入編譯指令通過/DEF聲明傳遞給link

cl math.c /LD /DEF /math.def

系統(tǒng)級軟件開發(fā)時,,一般推薦使用.def模塊腳本批量定義導(dǎo)出符號。一方面,,C/C++編譯器可能會在編譯后將函數(shù)修飾的面目全非,,如_Add@16之類的,這時在.def文件中可以重命名,,這時__declspec()這種方式無法做到的,;另一方面是除了LIBRARY/EXPORTS關(guān)鍵字,還可以通過NAME/VERSION/SECTIONS/STACKSIZE/HEAPSIZE等關(guān)鍵字來定義輸出文件名/DLL版本/各段的屬性/默認(rèn)堆棧大小/默認(rèn)堆大小,。顯然.def模塊腳本的操作空間更大,,可以封裝更多的細(xì)節(jié)。

EXPORTS
Add = _Add@16

5.PE文件頭下的DataDirectory

這里先介紹以下DataDirectory這一關(guān)鍵的結(jié)構(gòu)數(shù)組,,因為其概念將引出后續(xù)的符號導(dǎo)入表和導(dǎo)出表,。 
首先PE文件中除去sectiontable/.data/.code等段信息外,還存在PE HEADER段,,即文件頭,。這里需要深度解析下PE文件的各段組成DataDirectory的內(nèi)容。

//IMAGE_NT_HEADERS
typedef struct{
    DWORD                   Signature;//PE頭的標(biāo)識,,類似魔數(shù),,50h 45h 00h 00h
    IMAGE_FILE_HEADER       FileHeader;//20字節(jié)的數(shù)據(jù)結(jié)構(gòu),包含文件的物理層信息及文件屬性
    IMAGE_OPTIONAL_HEADER32 OptionalHeader;
}IMAGE_NT_HEADERS;

//IMAGE_FILE_HEADER
typedef struct{
    WORD     Machine;//標(biāo)識機(jī)器平臺,,如x86
    WORD     NumberOfSection;//該P(yáng)E文件的段數(shù)目
    WORD     TimeDataStamp;//PE文件的時間戳
    DWORD    PointerToSymbolTable;//指向符號表的指針
    DWORD    NumberOfSymbols;//PE文件的符號總數(shù)
    WORD     SizeOfOptionalHeader;//可選頭optionalHeader的size
    WORD     Characteristics;//主要用來標(biāo)識當(dāng)前文件是可執(zhí)行文件還是DLL文件,其余各位也各有定義
}IMAGE_FILE_HEADER;

//IMAGE_OPTIONAL_HEADER32
typedef struct{
    WORD     Magic;
    BYTE     MajorLinkerVersion;
    BYTE     MinorLinkerVersion;
    DWORD    SizeOfCode;
    DWORD    SizeOfInitializedData;
    DWORD    SizeOfUninitializedData;
    DWORD    AddressOfEntryPoint;//PE文件的執(zhí)行入口地址,,如果是靜態(tài)鏈接,,一般是main(),如果是動態(tài)鏈接,則入口地址是動態(tài)鏈接器代碼的地址,是一個RVA地址,,HOOK鉤子常修改這里
    DWORD    BaseOfCode;//.code段的起始地址
    DWORD    BaseOfData,;//.data段的起始地址
    DWORD    ImageBase;//PE文件的優(yōu)先裝載基地址,.exe默認(rèn)是0x0040 0000,.dll默認(rèn)是0x1000 0000
    DWORD    SectionAlignment;//文件在內(nèi)存中的各段的對齊粒度,,如4096(0x1000h),則代表在內(nèi)存中,,每一段的其實地址都是4096的整數(shù)倍
    DWORD    FileAlignment;//文件在磁盤的對齊格式,,一般是磁盤塊的大小,如512 B
    ...
    DWORD    SizeOfImage;
    DWORD    SizeOfHeaders;//所有的頭如DOS HEADER/DOS stub/PE HEADER + SectionTable的大小,,即除去正經(jīng)段的頭部大小,,一般用來定位第一個section的位置,如.data
    DWORD    CheckSum,;
    ...
    DWORD    NumberOfRvaAndSizes;//在IMAGE_NT_HEADERS結(jié)構(gòu)的末尾是一個IMAGE_DATA_DIRECTORY結(jié)構(gòu)數(shù)組,。此域包含了這個數(shù)組的元素個數(shù)。自從最早的Windows NT發(fā)布以來這個域的值一直是16,。
    IMAGE_DATA_DIRECTORY  DataDirectory;//記錄了16個重要的數(shù)據(jù)的結(jié)構(gòu)數(shù)組,,16個IMAGE_DATA_DIRECTORY結(jié)構(gòu),每個都對應(yīng)一個重要的信息,,如符號導(dǎo)入導(dǎo)出表,、文件打開列表
}IMAGE_OPTIONAL_HEADER32;

//IMAGE_DATA_DIRECTORY對應(yīng)一個重要的數(shù)據(jù)信息
typedef struct{
    DWORD   VirtualAddress;//對應(yīng)數(shù)據(jù)結(jié)構(gòu)的RVA地址
    DWORD   iSize;//對應(yīng)數(shù)據(jù)的大小
}IMAGE_DATA_DIRECTORY;
/* 16個重要數(shù)據(jù)結(jié)構(gòu)列舉如下 
1. IMAGE_DIRECTORY_ENTRY_EXPORT //導(dǎo)出表http://blog.csdn.net/obuyiseng/article/details/50231677
2. IMAGE_DIRECTORY_ENTRY_IMPORT //導(dǎo)入表,,是一個IMAGE_IMPORT_DESCRIPTOR的結(jié)構(gòu)體數(shù)組,,
//每個IMAGE_IMPORT_DESCRIPTOR對應(yīng)一個被導(dǎo)入的DLL,這個結(jié)構(gòu)體定義在“Winnt.h"
3. IMAGE_DIRECTORY_ENTRY_RESOURCE//資源目錄http://blog.csdn.net/obuyiseng/article/details/50260671
4. IMAGE_DIRECTORY_ENTRY_EXCEPTION//異常目錄
5. IMAGE_DIRECTORY_ENTRY_SECURITY//安全目錄
6. IMAGE_DIRECTORY_ENTRY_BASERELOC//重定位基本信息http://blog.csdn.net/obuyiseng/article/details/50261305
7. IMAGE_DIRECTORY_ENTRY_DEBUG//調(diào)試目錄
8. IMAGE_DIRECTORY_ENTRY_COPYRIGHT//描述字串
9. IMAGE_DIRECTORY_ENTRY_GLOBALPTR//機(jī)器值
10. IMAGE_DIRECTORY_ENTRY_TLS //Thread Local Storage線程局部存儲TLS目錄
11. IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG//載入配置目錄
12. IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT//綁定輸入表
13. IMAGE_DIRECTORY_ENTRY_IAT //導(dǎo)入地址數(shù)組Import Address Table,屬于導(dǎo)入表的重要組成部分,,
//IAT中每個元素對應(yīng)一個被導(dǎo)入的符號,,在沒有重定位或符號解析之前,IAT中的元素值表示相對應(yīng)的導(dǎo)入符號的序號或
//符號名,,當(dāng)重定位和符號解析完成后,,IAT中元素值將被改寫成符號的真正地址,和ELF文件結(jié)構(gòu)下GOT相似
14. IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT  //延遲導(dǎo)入,,用于啟動加速
15. IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR//COM信息
16. IMAGE_DIRECTORY_ENTRY_ENTRIES
*/

//IMAGE_IMPORT_DESCRIPTORS對應(yīng)一個被導(dǎo)入的DLL的導(dǎo)入符號信息
typedef struct{
    DWORD  OriginalFirstThunk;//指向INT
    DWORD  TimeDataStamp;
    DWORD  ForwarderChain;
    DWORD  Name;//綁定的DLL文件名字
    DWORD  FirstThunk; //指向IAT
}IMAGE_IMPORT_DESCRIPTORS;

// IMAGE_EXPORT_DIRECTORYM對應(yīng)DLL文件的導(dǎo)出符號信息
typedef struct _IMAGE_EXPORT_DIRECTORY {  
        DWORD Characteristics;          //保留 總是定義為0  
        DWORD TimeDateStamp;            //文件生成時間  
        WORD  MajorVersion;             //主版本號 一般不賦值  
        WORD  MinorVersion;             //次版本號 一般不賦值  
        DWORD Name;                     //模塊的真實名稱  
        DWORD Base;                     //索引基數(shù) 加上序數(shù)就是函數(shù)地址數(shù)組的索引值  
        DWORD NumberOfFunctions;        //地址表中導(dǎo)出函數(shù)符號的個數(shù)  
        DWORD NumberOfNames;            //名稱表的個數(shù)  
        DWORD AddressOfFunctions;       //輸出函數(shù)地址數(shù)組的RVA  
        DWORD AddressOfNames;           //輸出函數(shù)名字?jǐn)?shù)組的RVA  
        DWORD AddressOfNameOrdinals;    //輸出函數(shù)序號數(shù)組的RVA  
} IMAGE_EXPORT_DIRECTORYM;   
DataDirectory中項目定義
IMAGE_DIRECTORY_ENTRY_EXPORT指向?qū)С霰?一個IMAGE_EXPORT_DIRECTORY結(jié)構(gòu)),。
IMAGE_DIRECTORY_ENTRY_IMPORT指向?qū)氡?一個IMAGE_IMPORT_DESCRIPTOR結(jié)構(gòu)數(shù)組)。
IMAGE_DIRECTORY_ENTRY_RESOURCE指向資源(一個IMAGE_RESOURCE_DIRECTORY結(jié)構(gòu),。是PE文件結(jié)構(gòu)下最為重要且難懂的地方,。
IMAGE_DIRECTORY_ENTRY_EXCEPTION指向異常處理表(一個IMAGE_RUNTIME_FUNCTION_ENTRY結(jié)構(gòu)數(shù)組)。CPU特定的并且基于表的異常處理,。用于除x86之外的其它CPU上,。
IMAGE_DIRECTORY_ENTRY_SECURITY指向一個WIN_CERTIFICATE結(jié)構(gòu)的列表,它定義在WinTrust.H中,。不會被映射到內(nèi)存中,。因此,VirtualAddress域是一個文件偏移,,而不是一個RVA,。
IMAGE_DIRECTORY_ENTRY_RESOURCE指向資源(一個IMAGE_RESOURCE_DIRECTORY結(jié)構(gòu)。是PE文件結(jié)構(gòu)下最為重要且難懂的地方。
IMAGE_DIRECTORY_ENTRY_BASERELOC指向基址重定位信息,。
IMAGE_DIRECTORY_ENTRY_DEBUG指向一個IMAGE_DEBUG_DIRECTORY結(jié)構(gòu)數(shù)組,,其中每個結(jié)構(gòu)描述了映像的一些調(diào)試信息。早期的Borland鏈接器設(shè)置這個IMAGE_DATA_DIRECTORY結(jié)構(gòu)的Size域為結(jié)構(gòu)的數(shù)目,,而不是字節(jié)大小,。要得到IMAGE_DEBUG_DIRECTORY結(jié)構(gòu)的數(shù)目,用IMAGE_DEBUG_DIRECTORY 的大小除以這個Size域,。
IMAGE_DIRECTORY_ENTRY_ARCHITECTURE指向特定架構(gòu)數(shù)據(jù),,它是一個IMAGE_ARCHITECTURE_HEADER結(jié)構(gòu)數(shù)組。不用于x86或IA-64,,但看來已用于DEC/Compaq Alpha,。
IMAGE_DIRECTORY_ENTRY_GLOBALPTR在某些架構(gòu)體系上VirtualAddress域是一個RVA,被用來作為全局指針(gp),。不用于x86,,而用于IA-64。Size域沒有被使用,。參見2000年11月的Under The Hood 專欄可得到關(guān)于IA-64 gp的更多信息,。
IMAGE_DIRECTORY_ENTRY_TLS指向線程局部存儲初始化段。
IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG指向一個IMAGE_LOAD_CONFIG_DIRECTORY結(jié)構(gòu),。IMAGE_LOAD_CONFIG_ DIRECTORY中的信息是特定于Windows NT,、Windows 2000和 Windows XP的(例如 GlobalFlag 值)。要把這個結(jié)構(gòu)放到你的可執(zhí)行文件中,,你必須用名字__load_config_used 定義一個全局結(jié)構(gòu),,類型是IMAGE_LOAD_CONFIG_ DIRECTORY。 對于非x86的其它體系,,符號名是_load_config_used (只有一個下劃線),。如果你確實要包含一個IMAGE_LOAD_CONFIG_DIRECTORY,那么在 C++ 中要得到正確的名字比較棘手,。鏈接器看到的符號名必須是__load_config_used (兩個下劃線),。C++ 編譯器會在全局符號前加一個下劃線。另外,,它還用類型信息修飾全局符號名,。因此,要使一切正常,,在 C++ 中就必須像下面這樣使用: extern “C” IMAGE_LOAD_CONFIG_DIRECTORY _load_config_used = {…}
IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT指向一個 IMAGE_BOUND_IMPORT_DESCRIPTOR結(jié)構(gòu)數(shù)組,,對應(yīng)于這個映像綁定的每個DLL。數(shù)組元素中的時間戳允許加載器快速判斷綁定是否是新的,。如果不是,加載器忽略綁定信息并且按正常方式解決導(dǎo)入API。
IMAGE_DIRECTORY_ENTRY_IAT指向第一個導(dǎo)入地址表(IAT)的開始位置,。對應(yīng)于每個被導(dǎo)入DLL的IAT都連續(xù)地排列在內(nèi)存中,。Size域指出了所有IAT的總的大小。在寫入導(dǎo)入函數(shù)的地址時加載器使用這個地址和Size域指定的大小臨時地標(biāo)記IAT為可讀寫,。
IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT指向延遲加載信息,,它是一個CImgDelayDescr結(jié)構(gòu)數(shù)組,定義在Visual C++的頭文件DELAYIMP.H中,。延遲加載的DLL直到對它們中的API進(jìn)行第一次調(diào)用發(fā)生時才會被裝入,。Windows中并沒有關(guān)于延遲加載DLL的知識,認(rèn)識到這一點很重要,。延遲加載的特征完全是由鏈接器和運(yùn)行時庫實現(xiàn)的,。
IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR在最近更新的系統(tǒng)頭文件中這個值已被改名為IMAGE_DIRECTORY_ENTRY_ COMHEADER。它指向可執(zhí)行文件中.NET信息的最高級別信息,,包括元數(shù)據(jù),。這個信息是一個IMAGE_COR20_HEADER結(jié)構(gòu)。

6.DLL文件的符號導(dǎo)出表

和ELF文件結(jié)構(gòu)下的.dynsym意義相同,,對于DLL文件,,其在被加載時,顯然需要有個集中存放導(dǎo)出符號的段或數(shù)據(jù)結(jié)構(gòu)來供鏈接器快速收集當(dāng)前DLL的導(dǎo)出信息,。COFF PE文件結(jié)構(gòu)中,,這些導(dǎo)出符號被放在文件的導(dǎo)出表中。導(dǎo)出表提供一個符號名和符號地址的映射關(guān)系,,即可以通過符號名查找該符號對應(yīng)的變量或函數(shù)的具體地址,。

// IMAGE_EXPORT_DIRECTORYM對應(yīng)DLL文件的導(dǎo)出符號信息
typedef struct _IMAGE_EXPORT_DIRECTORY {  
        DWORD Characteristics;          //保留 總是定義為0  
        DWORD TimeDateStamp;            //文件生成時間  
        WORD  MajorVersion;             //主版本號 一般不賦值  
        WORD  MinorVersion;             //次版本號 一般不賦值  
        DWORD Name;                     //模塊的真實名稱  
        DWORD Base;                     //索引基數(shù) 加上序數(shù)就是函數(shù)地址數(shù)組的索引值  
        DWORD NumberOfFunctions;        //地址表中導(dǎo)出函數(shù)符號的個數(shù)  
        DWORD NumberOfNames;            //名稱表的個數(shù)  
        DWORD AddressOfFunctions;       //導(dǎo)出地址表的首地址(RVA)  
        DWORD AddressOfNames;           //導(dǎo)出符號名表的首地址(RVA) 
        DWORD AddressOfNameOrdinals;    //名字序號對應(yīng)表的首地址(RVA) 
} IMAGE_EXPORT_DIRECTORYM;   

可以看到對于DLL的導(dǎo)出表中有三個數(shù)組是最為核心的結(jié)構(gòu),分別是導(dǎo)出地址表(EAT,,Export Address Table),、符號名表(Name Table)和名字序號對應(yīng)表(Name-Ordinal Table)。導(dǎo)出地址表EAT對應(yīng)的是DLL各導(dǎo)出函數(shù)的RVA地址,,符號名表存儲的則是DLL各導(dǎo)出函數(shù)名,,序號表則是和符號名表一一對應(yīng),用以指明相應(yīng)的函數(shù)名的RVA地址在EAT數(shù)組中的下標(biāo),。

7.PE文件的符號導(dǎo)入表

一個DLL文件只有一個導(dǎo)出表(.exe可執(zhí)行文件不存在導(dǎo)出符號),,但是一個PE文件可能依賴多個文件(從多個DLL文件中導(dǎo)入符號),所以要為每個依賴文件單獨(dú)弄一份導(dǎo)入符號集合,,再將所有依賴文件的導(dǎo)入符號集合集中在一起便成了PE文件的導(dǎo)入表,。在PE文件中,記錄每個依賴文件的導(dǎo)入符號信息的是一個IMAGE_IMPORT_DESCRIPTOR結(jié)構(gòu)體,,_IMAGE_IMPORT_DIRECTOTY_ENTRY指向的便是一個該結(jié)構(gòu)體的數(shù)組,。

//IMAGE_IMPORT_DESCRIPTORS對應(yīng)一個被導(dǎo)入的DLL的導(dǎo)入符號信息
typedef struct{
    DWORD  OriginalFirstThunk;//指向INT(Import Name Table),和IAT一致,但是涉及到DLL優(yōu)化,,暫且可以忽略
    DWORD  TimeDataStamp;
    DWORD  ForwarderChain;
    DWORD  Name;//綁定的DLL文件名字
    DWORD  FirstThunk; //指向IAT導(dǎo)入地址數(shù)組(IAT, Import Address Table)
}IMAGE_IMPORT_DESCRIPTORS;

IAT中每個元素對應(yīng)一個被導(dǎo)入的符號,,在沒有重定位或符號解析之前,IAT中的元素值表示相對應(yīng)的導(dǎo)入符號的序號或符號名,,當(dāng)重定位和符號解析完成后,,IAT中元素值將被改寫成符號的真正地址,和ELF文件結(jié)構(gòu)下GOT功能相似,。

IAT(INT)的元素為IMAGE_THUNK_DATA32結(jié)構(gòu),,而其指向為IMAGE_IMPORT_BY_NAME結(jié)構(gòu),這兩個結(jié)構(gòu)的定義如下,。

typedef struct _IMAGE_THUNK_DATA32 {
    union {
        DWORD ForwarderString;      // PBYTE 
        DWORD Function;             // PDWORD
        DWORD Ordinal;
        DWORD AddressOfData;        //RVA 指向_IMAGE_IMPORT_BY_NAME 
    } u1; //聯(lián)合體,對于32位PE來說,,來說如果最高位為1,那么低31位則直接就是導(dǎo)入符號的序號值,;如果沒有,,那么這個IMAGE_THUNK_DATA32啟用的是AddressOfData,其指向一個_IMAGE_IMPORT_BY_NAME結(jié)構(gòu)體
} IMAGE_THUNK_DATA32;
typedef IMAGE_THUNK_DATA32 * PIMAGE_THUNK_DATA32;

typedef struct _IMAGE_IMPORT_BY_NAME {
    WORD    Hint;       //可能為0,,編譯器決定,,如果不為0,是函數(shù)在導(dǎo)出表中的序號
    BYTE    Name[1];    //函數(shù)名稱,,以0結(jié)尾,,由于不知道到底多長,所以干脆只給出第一個字符,,找到0結(jié)束
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;

對于32位PE來說,,來說如果最高位為1,那么低31位則直接就是導(dǎo)入符號的序號值,;如果沒有,,那么這個IMAGE_THUNK_DATA32啟用的是AddressOfData,其指向一個_IMAGE_IMPORT_BY_NAME結(jié)構(gòu)體,。使用_IMAGE_IMPORT_BY_NAME結(jié)構(gòu)體時,,先根據(jù)Hint值去往相應(yīng)DLL導(dǎo)出表中查找是否對應(yīng)的符號名是所需的符號,如果不是,,則需要按照符號名去符號名數(shù)組中二分查找(符號名數(shù)組在收集導(dǎo)出符號做了預(yù)處理,,按照字典序進(jìn)行排列,所以可以進(jìn)行二分查找),。

Fig.1 COFF PE文件結(jié)構(gòu)下的符號導(dǎo)入導(dǎo)出表對應(yīng)關(guān)系

Q: 在導(dǎo)出表中可以看到函數(shù)名序號數(shù)組的存在其實蠻多余的,,明明可以通過函數(shù)符號名表和EAT地址表一一對應(yīng)來解決問題,卻非要通過一個序號表中轉(zhuǎn)下,,這是為什么,? 
A: 有很多說法,,但核心還是考慮DLL兼容性。在Windows系統(tǒng)還是16bits的年代,,顯然保留導(dǎo)出函數(shù)的函數(shù)名數(shù)組是一件極為奢侈的事情,,故而出于節(jié)省空間的考慮,將DLL導(dǎo)出函數(shù)符號分配唯一的序號用以代表,,從而在完成重定位的任務(wù)下,也可盡可能地節(jié)省內(nèi)存占用,。如下面.def模塊腳本的內(nèi)容,,為各導(dǎo)出函數(shù)手動綁定序號。

LIBRARY  Math
    EXPORTS
    Add   @1
    Sub   @2
    Mul   @3
    Div   @4  NONAME

使用序號雖好,,可一旦發(fā)生函數(shù)變更增減,,則需要再次手動更新一遍函數(shù)序號,但這也會影響到使用老版本DLL的程序的中函數(shù)調(diào)用,,因為這種強(qiáng)綁定關(guān)系導(dǎo)致采用序號機(jī)制的DLL升級較為繁瑣,。后來隨著計算機(jī)內(nèi)存的增加,自然保留導(dǎo)出函數(shù)名成為主流選擇,。而為了兼容性考慮,,序號機(jī)制這一歷史便依舊被傳承下來了。

7.DLL顯式運(yùn)行時加載鏈接demo

#include <windows.h>
#include <stdio.h>

typedef double (*Func) (double, double);

int main(int argc, char **argv)
{
    Func function;
    double result;

    //load DLL using Library()
    HINSTANCE hinstLib = LoadLibrary("Math.dll");
    if (hinstLib == NULL) {
        printf("ERROR: unable to load DLL\n");
        return -1;
    }

    //DLL裝載成功,,則直接通過GetProcAddress()獲取目標(biāo)函數(shù)符號的地址
    function = (Func) GetProcAddress(hinstLib, "Add");
    if(function == NULL) {
        printf("ERROR: Unable to find target function\n");
        FreeLibrary(hinstLib);
        return 1;
    }

    result = function(1.0, 2.0);
    printf("Result = %f\n", result);

    FreeLibrary(hinstLib);
    return 0;
}

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點,。請注意甄別內(nèi)容中的聯(lián)系方式,、誘導(dǎo)購買等信息,謹(jǐn)防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多