最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

LoadLibrary深入案例詳解

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

LoadLibrary流程分析

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

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

1. 使用

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

1.1 聲明

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

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

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

默認情況下,LoadLibrary和LoadLibrary按照如下目錄搜索:

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

1.2 SetDllDirectoryW

這個函數(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分析

下面來分析一下這個函數(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價值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;
}

這里有一個默認的加載路徑為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);
}

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

獲取加載鎖RtlEnterCriticalSection(&LdrpLoaderLock);嘗試加載dll: LdrpFindOrMapDll。處理導入表信息。運行回調(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的時候,會獲取鎖LdrpLoaderLock;然而加載的時候又會調(diào)用LdrpRunInitializeRoutines進入DllMain。

2.4 LdrpFindOrMapDll

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

LIST_ENTRY LdrpHashTable[LDR_HASH_TABLE_ENTRIES];

其實每個LdrpHashTable都是通過一個_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

對于每個加載的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"   //全路徑匹配這個
   +0x02c BaseDllName      : _UNICODE_STRING "kernel32.dll"   //Dll名稱匹配這個
   +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

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

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

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

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

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

這種方式,第二個應(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)存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-aVEtDGkf-1577506406764)(assets/images/2019-12-28-12-05-34.png)]

這個中情況在wow64 情況下,有時候會踩到坑,例如,對于wowo64路徑,如果直接使用C:\Windows\system32\kernel32.dll路徑加載,那么LdrpFindLoadedDllByName一定會失敗,因為FullDllName為wow64使用的值:C:\Windows\syswow64\kernel32.dll.

3.2 死鎖

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

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

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

相關(guān)文章

  • C語言實現(xiàn)簡單回聲服務(wù)器

    C語言實現(xiàn)簡單回聲服務(wù)器

    這篇文章主要為大家詳細介紹了C語言實現(xiàn)簡單回聲服務(wù)器,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • C++中Digraphs、Trigraphs和Tokens的深入講解

    C++中Digraphs、Trigraphs和Tokens的深入講解

    這篇文章主要給大家介紹了關(guān)于C++中Digraphs、Trigraphs和Tokens的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家學習或者使用C++具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2018-09-09
  • C語言繪制簡單時鐘小程序

    C語言繪制簡單時鐘小程序

    這篇文章主要為大家詳細介紹了C語言繪制簡單時鐘小程序,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-06-06
  • C語言實現(xiàn)磁盤映射

    C語言實現(xiàn)磁盤映射

    磁盤映射技術(shù)通過將文件映射到內(nèi)存中,提高了文件操作的效率,本文就來介紹一下C語言實現(xiàn)磁盤映射,感興趣的可以了解一下
    2024-09-09
  • rapidjson解析json代碼實例以及常見的json core dump問題

    rapidjson解析json代碼實例以及常見的json core dump問題

    今天小編就為大家分享一篇關(guān)于rapidjson解析json代碼實例以及常見的json core dump問題,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2019-04-04
  • 詳解C語言中雙指針算法的使用

    詳解C語言中雙指針算法的使用

    雙指針,指的是在遍歷對象的過程中,不是普通的使用單個指針進行訪問,而是使用兩個相同方向(快慢指針)或者相反方向(對撞指針)的指針進行掃描,從而達到相應(yīng)的目的。本文將通過示例帶大家深入了解雙指針算法的使用
    2022-08-08
  • C++ ofstream和ifstream詳細用法

    C++ ofstream和ifstream詳細用法

    ofstream是從內(nèi)存到硬盤,ifstream是從硬盤到內(nèi)存,其實所謂的流緩沖就是內(nèi)存空間,本文小編就為大家詳細介紹C++ ofstream和ifstream用法,需要的朋友可以參考下面文章的具體內(nèi)容
    2021-09-09
  • cocos2d-x學習筆記之CCLayer、CCLayerColor、CCLayerGradient、CCLayerMultiplex場景層介紹

    cocos2d-x學習筆記之CCLayer、CCLayerColor、CCLayerGradient、CCLayerMu

    這篇文章主要介紹了cocos2d-x學習筆記之CCLayer、CCLayerColor、CCLayerGradient、CCLayerMultiplex場景層介紹,需要的朋友可以參考下
    2014-09-09
  • Visual C++中Tab View的多種實現(xiàn)方法

    Visual C++中Tab View的多種實現(xiàn)方法

    這篇文章主要介紹了Visual C++中Tab View的多種實現(xiàn)方法,包括了CTabCtrl控件、CSheetCtrl標簽選擇窗口以及靜態(tài)分割窗口等實現(xiàn)Tab View的方法,需要的朋友可以參考下
    2014-10-10
  • C++實現(xiàn)日期類(Date類)的方法

    C++實現(xiàn)日期類(Date類)的方法

    下面小編就為大家?guī)硪黄狢++實現(xiàn)日期類(Date類)的方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-01-01

最新評論

抚州市| 那曲县| 青川县| 正定县| 望都县| 类乌齐县| 阿拉善左旗| 灵山县| 淄博市| 南昌县| 讷河市| 固原市| 确山县| 平定县| 信宜市| 澄城县| 黔西| 昌江| 北流市| 旌德县| 堆龙德庆县| 兴和县| 沭阳县| 乐清市| 汝南县| 灵川县| 白朗县| 小金县| 白朗县| 台东县| 漳平市| 江安县| 奉贤区| 泾源县| 柳林县| 乐清市| 宝坻区| 德保县| 佛冈县| 神农架林区| 海兴县|