欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

LoadLibrary深入案例詳解

 更新時(shí)間:2021年08月17日 09:14:07   作者:xdesk  
這篇文章主要介紹了LoadLibrary深入案例詳解,本篇文章通過簡要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下

LoadLibrary流程分析

在Windows開發(fā)中,我們都有過一個(gè)規(guī)定:在DllMain中不應(yīng)該處理過于復(fù)雜的事情,防止死鎖的發(fā)生。

那么,到底為什么DllMain中容易導(dǎo)致死鎖呢?下面我們來分析一下LoadLibrary的整個(gè)流程和原理。

1. 使用

我們來看一下LoadLibrary怎么使用的,由于這個(gè)函數(shù)底層是調(diào)用LoadLibraryEx我們看LoadLibraryEx的使用情況。

1.1 聲明

HMODULE WINAPI LoadLibraryEx(
  _In_       LPCTSTR lpFileName,
  _Reserved_ HANDLE  hFile,
  _In_       DWORD   dwFlags
);

這里主要第三個(gè)參數(shù)使用起來有需要注意的地方:

  1. DONT_RESOLVE_DLL_REFERENCES : 這個(gè)標(biāo)志用于告訴系統(tǒng)將DLL映射到調(diào)用進(jìn)程的地址空間中,但是不調(diào)用DllMain并且不加載依賴Dll(只映射自己本身)。
  2. LOAD_LIBRARY_AS_DATAFILE : 這個(gè)標(biāo)志與DONT_RESOLVE_DLL_REFERENCES標(biāo)志相類似,因?yàn)橄到y(tǒng)只是將DLL映射到進(jìn)程的地址空間中,就像它是數(shù)據(jù)文件一樣。系統(tǒng)并不花費(fèi)額外的時(shí)間來準(zhǔn)備執(zhí)行文件中的任何代碼。
  3. LOAD_LIBRARY_SEARCH_USER_DIRS : 搜索路徑的使用使用AddDllDirectory和SetDllDirectory設(shè)置的路徑(保護(hù)Dll自己和依賴Dll)。
  4. LOAD_LIBRARY_SEARCH_SYSTEM32 : 從%windows%\system32加載Dll和其依賴項(xiàng)。
  5. LOAD_LIBRARY_SEARCH_APPLICATION_DIR : 應(yīng)用程序安裝路徑搜索Dll和其依賴項(xiàng)。
  6. LOAD_WITH_ALTERED_SEARCH_PATH : 按照如下目錄搜索:
    1. 進(jìn)程當(dāng)前目錄。
    2. Windows的系統(tǒng)目錄。
    3. 16 位Windows的系統(tǒng)目錄。
    4. Windows目錄。
    5. path環(huán)境變量目錄。

默認(rèn)情況下,LoadLibrary和LoadLibrary按照如下目錄搜索:

  1. 進(jìn)程當(dāng)前目錄。
  2. SetDllDirectory設(shè)置的文件夾路徑。
  3. Windows的系統(tǒng)目錄。
  4. 16 位Windows的系統(tǒng)目錄。
  5. Windows目錄。
  6. path環(huán)境變量目錄。

1.2 SetDllDirectoryW

這個(gè)函數(shù)的實(shí)現(xiàn)如下:

在這里插入圖片描述

其中KernelBaseGetGlobalData返回的結(jié)果信息如下:

在這里插入圖片描述

如下信息為:

0:003> dd KERNELBASE!KernelBaseGlobalData
75b155a0  00000000 00000000 00160014 7f9a1240
75b155b0  00280026 7f9a1260 00000000 00000000
75b155c0  00000000 00e60000 75b156c0 7fff0000
75b155d0  00c4b494 0000011a 00cc1914 0000012c
75b155e0  00cb9f34 00000253 00cc2658 00cc5e20
75b155f0  00000000 00e84380 0000000f ffffffff
75b15600  ffffffff 00000000 00000000 00000000
75b15610  020007d0 75950000 00000000 00000000
0:003> du 7f9a1240
7f9a1240  "C:\Windows"
0:003> du 7f9a1260 
7f9a1260  "C:\Windows\system32"

2. LoadLibrary分析

下面來分析一下這個(gè)函數(shù)的執(zhí)行過程:

HMODULE __stdcall LoadLibraryW(LPCWSTR lpLibFileName)
{
  return LoadLibraryExW(lpLibFileName, 0, 0);
}

2.1 LoadLibraryExW

對于LoadLibraryW的主要流程如下:

HMODULE __stdcall LoadLibraryExW(LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags)
{
    //...
    SearchPath = BaseGetProcessDllPath(
                       &dwFlags,
                       (chFlags & LOAD_WITH_ALTERED_SEARCH_PATH) != 0 ? DllName.Buffer : 0,
                       0,
                       (int)&dwFlags);

    //...
    ntStatus = LdrLoadDll(SearchPath, (PULONG)&lpLibFileName, &DllName, &hFile);
}

通過BaseGetProcessDllPath獲取到的路徑為:

00072acc  "C:\Users\xxx\Desktop;C:\Windows\"
00072b0c  "system32;C:\Windows\system;C:\Wi"
00072b4c  "ndows;.;C:\Windows\system32;C:\W"
00072b8c  "indows;C:\Windows\System32\Wbem;"
00072bcc  "C:\Windows\System32\WindowsPower"
00072c0c  "Shell\v1.0\"

接下來就是使用LdrLoadDll價(jià)值dll了。

2.2 LdrLoadDll

NTSTATUS __stdcall LdrLoadDll(PWSTR SearchPath, PULONG LoadFlags, PUNICODE_STRING DllName, PVOID *BaseAddress)
{
  //...
  if ( SearchPath )
  {
    result = RtlInitUnicodeStringEx(&DestinationString, SearchPath);
    if ( result < 0 )
      return result;
    NewSearchPath = &DestinationString;
  }
  else
  {
    NewSearchPath = &LdrpDefaultPath;
  }
  //...
  v7 = LdrpLoadDll(DllName, (int)NewSearchPath, v6, 1, 0, (int)&DllName);
  //...
  return v7;
}

這里有一個(gè)默認(rèn)的加載路徑為LdrpDefaultPath,路徑信息如下:

0:000> dS LdrpDefaultPath
00061560  "C:\Users\xxx\Desktop;C:\Windows\"
000615a0  "system32;C:\Windows\system;C:\Wi"
000615e0  "ndows;.;C:\Windows\system32;C:\W"
00061620  "indows;C:\Windows\System32\Wbem;"
00061660  "C:\Windows\System32\WindowsPower"
000616a0  "Shell\v1.0\"

2.3 LdrpLoadDll

int __stdcall LdrpLoadDll(PCUNICODE_STRING Source, int a2, int a3, char a4, int a5, int a6)
{
    //...
    if ( !LdrpInLdrInit )
      RtlEnterCriticalSection(&LdrpLoaderLock);
    
    //...
    LdrpFindOrMapDll(*(PCUNICODE_STRING *)((char *)&v31 + 1), v29, a3, v27[0], (int)&v33, (int)&v31);

    //...
    if ( v21 & 0x1000000 )
        v22 = LdrpCorProcessImports((void *)v21, v33);
    else
        v22 = LdrpProcessStaticImports(v33, v29);
    //...

    LdrpRunInitializeRoutines(0);

    //...

    if ( !LdrpInLdrInit )
      RtlLeaveCriticalSection(&LdrpLoaderLock);
}

這個(gè)函數(shù)的整理流程如下:

獲取加載鎖RtlEnterCriticalSection(&LdrpLoaderLock);嘗試加載dll: LdrpFindOrMapDll。處理導(dǎo)入表信息。運(yùn)行回調(diào)函數(shù)LdrpRunInitializeRoutines。釋放鎖RtlLeaveCriticalSection(&LdrpLoaderLock);。

這里值得注意的地方就是LdrpRunInitializeRoutines是調(diào)用DllMain函數(shù),調(diào)用堆棧信息如下:

 # ChildEBP RetAddr  Args to Child              
00 0029d718 67f42c22 67ef0000 00000001 00000000 DllTest!DllMain 
01 0029d75c 67f42def 67ef0000 00000001 00000000 DllTest!dllmain_dispatch+0xb2 
02 0029d770 777b89d8 67ef0000 00000001 00000000 DllTest!_DllMainCRTStartup+0x1f 
03 0029d790 777c5c41 67f3dcc5 67ef0000 00000001 ntdll!LdrpCallInitRoutine+0x14
04 0029d884 777c052e 00000000 73bd12d3 777a7c9a ntdll!LdrpRunInitializeRoutines+0x26f
05 0029d9f0 777c232c 0029da50 0029da1c 00000000 ntdll!LdrpLoadDll+0x4d1
06 0029da24 75b688ee 00072acc 0029da64 0029da50 ntdll!LdrLoadDll+0x92
07 0029da5c 763d3c12 00000000 00000000 00000001 KERNELBASE!LoadLibraryExW+0x15a

加載dll的時(shí)候,會獲取鎖LdrpLoaderLock;然而加載的時(shí)候又會調(diào)用LdrpRunInitializeRoutines進(jìn)入DllMain。

2.4 LdrpFindOrMapDll

加載和映射的dll的函數(shù)是LdrpFindOrMapDll,這個(gè)函數(shù)在加載Dll之前,需要判斷Dll是否被加載過, 已經(jīng)加載的dll,通過一個(gè)哈希表加載,哈希表如下:

LIST_ENTRY LdrpHashTable[LDR_HASH_TABLE_ENTRIES];

其實(shí)每個(gè)LdrpHashTable都是通過一個(gè)_LDR_DATA_TABLE_ENTRY的成員HashLinks鏈接起來,結(jié)構(gòu)如下:

0:000> dt ntdll!_LDR_DATA_TABLE_ENTRY 
   +0x000 InLoadOrderLinks : _LIST_ENTRY
   +0x008 InMemoryOrderLinks : _LIST_ENTRY
   +0x010 InInitializationOrderLinks : _LIST_ENTRY
   +0x018 DllBase          : Ptr32 Void
   +0x01c EntryPoint       : Ptr32 Void
   +0x020 SizeOfImage      : Uint4B
   +0x024 FullDllName      : _UNICODE_STRING
   +0x02c BaseDllName      : _UNICODE_STRING
   +0x034 Flags            : Uint4B
   +0x038 LoadCount        : Uint2B
   +0x03a TlsIndex         : Uint2B
   +0x03c HashLinks        : _LIST_ENTRY  //LdrpHashTable連接的列表結(jié)構(gòu)
   +0x03c SectionPointer   : Ptr32 Void
   +0x040 CheckSum         : Uint4B
   +0x044 TimeDateStamp    : Uint4B
   +0x044 LoadedImports    : Ptr32 Void
   +0x048 EntryPointActivationContext : Ptr32 _ACTIVATION_CONTEXT
   +0x04c PatchInformation : Ptr32 Void
   +0x050 ForwarderLinks   : _LIST_ENTRY
   +0x058 ServiceTagLinks  : _LIST_ENTRY
   +0x060 StaticLinks      : _LIST_ENTRY
   +0x068 ContextInformation : Ptr32 Void
   +0x06c OriginalBase     : Uint4B
   +0x070 LoadTime         : _LARGE_INTEGER

對于每個(gè)加載的DLL,都會有兩種形式:

  1. dll名稱加載。
  2. dll全路徑加載。

在LdrpFindLoadedDllByName也會根據(jù)不同的加載來匹配不同的情況:

  1. 如果使用dll名稱加載,那么比較BaseDllName;使用RtlEqualUnicodeString.
  2. 如果使用dll全路徑加載,那么比較FullDllName;使用RtlEqualUnicodeString.

例如:

0:000> dt ntdll!_LDR_DATA_TABLE_ENTRY 006f6270
   +0x000 InLoadOrderLinks : _LIST_ENTRY [ 0x6f6358 - 0x6f5eb8 ]
   +0x008 InMemoryOrderLinks : _LIST_ENTRY [ 0x6f6360 - 0x6f5ec0 ]
   +0x010 InInitializationOrderLinks : _LIST_ENTRY [ 0x6f6e38 - 0x6f6368 ]
   +0x018 DllBase          : 0x75b30000 Void
   +0x01c EntryPoint       : 0x75b43273 Void
   +0x020 SizeOfImage      : 0x110000
   +0x024 FullDllName      : _UNICODE_STRING "C:\Windows\syswow64\kernel32.dll"   //全路徑匹配這個(gè)
   +0x02c BaseDllName      : _UNICODE_STRING "kernel32.dll"   //Dll名稱匹配這個(gè)
   +0x034 Flags            : 0x84004
   +0x038 LoadCount        : 0xffff
   +0x03a TlsIndex         : 0
   +0x03c HashLinks        : _LIST_ENTRY [ 0x77c148a0 - 0x77c148a0 ]
   +0x03c SectionPointer   : 0x77c148a0 Void
   +0x040 CheckSum         : 0x77c148a0
   +0x044 TimeDateStamp    : 0x589c961f
   +0x044 LoadedImports    : 0x589c961f Void
   +0x048 EntryPointActivationContext : (null) 
   +0x04c PatchInformation : (null) 
   +0x050 ForwarderLinks   : _LIST_ENTRY [ 0x718580 - 0x718580 ]
   +0x058 ServiceTagLinks  : _LIST_ENTRY [ 0x6f62c8 - 0x6f62c8 ]
   +0x060 StaticLinks      : _LIST_ENTRY [ 0x6f63f0 - 0x6f62f0 ]
   +0x068 ContextInformation : 0x77b4ef40 Void
   +0x06c OriginalBase     : 0x7dd60000
   +0x070 LoadTime         : _LARGE_INTEGER 0x01d48576`ee26eab0

如果,這里匹配成功了,那么Dll就不會再加載了。

3. 總結(jié)

3.1 同名DLL

一個(gè)進(jìn)程是否可以加載相同名字的Dll呢?按照上面分析有一個(gè)加載規(guī)則

在LdrpFindLoadedDllByName也會根據(jù)不同的加載來匹配不同的情況:

  1. 如果使用dll名稱加載,那么比較BaseDllName;使用RtlEqualUnicodeString.
  2. 如果使用dll全路徑加載,那么比較FullDllName;使用RtlEqualUnicodeString.

那么可以從這個(gè)規(guī)則來看,是看加載是的方式:

int main(int args, char* argv[])
{
	LoadLibraryW(L"C:\\DllTest.dll");
	LoadLibraryW(L"DllTest.dll");
	system("pause");
	return 0;
}

這種方式,第二個(gè)應(yīng)該LoadLibraryW(L"DllTest.dll")不會去匹配加載了,如下:

在這里插入圖片描述

那么我們按照這種方式加載呢?

int main(int args, char* argv[])
{
	LoadLibraryW(L"DllTest.dll");
	LoadLibraryW(L"C:\\DllTest.dll");
	system("pause");
	return 0;
}

按照規(guī)則來說,應(yīng)該是可以加載的,如下:

[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-aVEtDGkf-1577506406764)(assets/images/2019-12-28-12-05-34.png)]

這個(gè)中情況在wow64 情況下,有時(shí)候會踩到坑,例如,對于wowo64路徑,如果直接使用C:\Windows\system32\kernel32.dll路徑加載,那么LdrpFindLoadedDllByName一定會失敗,因?yàn)镕ullDllName為wow64使用的值:C:\Windows\syswow64\kernel32.dll.

3.2 死鎖

我們知道,在調(diào)用Dllmain的時(shí)候,會占用鎖RtlEnterCriticalSection(&LdrpLoaderLock),所有凡是在DllMain中調(diào)用可能獲取RtlEnterCriticalSection(&LdrpLoaderLock)的操作,就可能會導(dǎo)致死鎖,例如:

  1. 直接調(diào)用LoadLibrary(Ex)。
  2. 調(diào)用CoInitializeEx(在CoInitializeEx底層會調(diào)用LoadLibraryEx)。
  3. 調(diào)用CreateThread(因?yàn)镃reateThread會繼續(xù)調(diào)用DllMain,稍有不慎可能就會死鎖)。
  4. 調(diào)用GetModuleFileName、GetModuleHandle 等(底層獲取RtlEnterCriticalSection(&LdrpLoaderLock)鎖)。

到此這篇關(guān)于LoadLibrary深入案例詳解的文章就介紹到這了,更多相關(guān)LoadLibrary詳解內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • VisualStudio2019配置OpenCV4.5.0的方法示例

    VisualStudio2019配置OpenCV4.5.0的方法示例

    這篇文章主要介紹了VisualStudio2019配置OpenCV4.5.0的方法示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-03-03
  • C++11中移動(dòng)構(gòu)造函數(shù)案例代碼

    C++11中移動(dòng)構(gòu)造函數(shù)案例代碼

    C++11 標(biāo)準(zhǔn)中為了滿足用戶使用左值初始化同類對象時(shí)也通過移動(dòng)構(gòu)造函數(shù)完成的需求,新引入了 std::move() 函數(shù),它可以將左值強(qiáng)制轉(zhuǎn)換成對應(yīng)的右值,由此便可以使用移動(dòng)構(gòu)造函數(shù),對C++11移動(dòng)構(gòu)造函數(shù)相關(guān)知識感興趣的朋友一起看看吧
    2023-01-01
  • C++利用鏈表實(shí)現(xiàn)圖書信息管理系統(tǒng)

    C++利用鏈表實(shí)現(xiàn)圖書信息管理系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了C++利用鏈表實(shí)現(xiàn)圖書信息管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-11-11
  • Qt自定義Widget實(shí)現(xiàn)互斥效果詳解

    Qt自定義Widget實(shí)現(xiàn)互斥效果詳解

    在使用Qt時(shí),可能會遇到這種問題:多個(gè)控件互斥,類似于QRadiButton控件,但又不是單純的QRadioButton控件,互斥的可能是一個(gè)窗口,也可能是幾個(gè)按鈕,等等多種情況。本文將介紹利用Qt自定義Widget實(shí)現(xiàn)的互斥效果,需要的可以參考一下
    2022-01-01
  • C++?Primer學(xué)習(xí)記錄之變量

    C++?Primer學(xué)習(xí)記錄之變量

    這篇文章主要為大家介紹了C++Primer之變量,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-01-01
  • 冒泡排序的三種實(shí)現(xiàn)方法

    冒泡排序的三種實(shí)現(xiàn)方法

    本篇文章是對冒泡排序的三種實(shí)現(xiàn)方法進(jìn)行了詳細(xì)的介紹,需要的朋友可以過來參考下。希望對大家有所幫助
    2013-10-10
  • C++優(yōu)先隊(duì)列的使用小結(jié)

    C++優(yōu)先隊(duì)列的使用小結(jié)

    普通的隊(duì)列是一種先進(jìn)先出的數(shù)據(jù)結(jié)構(gòu),元素在隊(duì)列尾追加,而從隊(duì)列頭刪除,在優(yōu)先隊(duì)列中,元素被賦予優(yōu)先級,本文主要介紹了C++優(yōu)先隊(duì)列的使用,感興趣的可以了解一下
    2023-11-11
  • C++中main函數(shù)怎樣調(diào)用類內(nèi)函數(shù)

    C++中main函數(shù)怎樣調(diào)用類內(nèi)函數(shù)

    這篇文章主要介紹了C++中main函數(shù)怎樣調(diào)用類內(nèi)函數(shù)問題,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-08-08
  • C語言 位運(yùn)算詳解及示例代碼

    C語言 位運(yùn)算詳解及示例代碼

    本文主要介紹C語言 位運(yùn)算的基礎(chǔ)知識,這里整理了相關(guān)資料及示例代碼,有興趣的小伙伴可以參考下
    2016-08-08
  • C++ STL_vector 迭代器失效問題的解決方法

    C++ STL_vector 迭代器失效問題的解決方法

    迭代器的主要作用就是讓算法能夠不用關(guān)心底層數(shù)據(jù)結(jié)構(gòu),其底層實(shí)際就是一個(gè)指針,或者是對指針進(jìn)行了封裝,迭代器失效,實(shí)際就是迭代器底層對應(yīng)指針?biāo)赶虻目臻g被銷毀了,對迭代器失效我們了解了,那么現(xiàn)在我們就分析,在vector中哪些操作會導(dǎo)致迭代器失效
    2023-08-08

最新評論