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

LoadLibrary深入案例詳解

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

LoadLibrary流程分析

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

那么,到底為什么DllMain中容易導(dǎo)致死鎖呢?下面我們來分析一下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 : 這個標(biāo)志用于告訴系統(tǒng)將DLL映射到調(diào)用進(jìn)程的地址空間中,但是不調(diào)用DllMain并且不加載依賴Dll(只映射自己本身)。
  2. LOAD_LIBRARY_AS_DATAFILE : 這個標(biāo)志與DONT_RESOLVE_DLL_REFERENCES標(biāo)志相類似,因?yàn)橄到y(tǒng)只是將DLL映射到進(jìn)程的地址空間中,就像它是數(shù)據(jù)文件一樣。系統(tǒng)并不花費(fèi)額外的時間來準(zhǔn)備執(zhí)行文件中的任何代碼。
  3. LOAD_LIBRARY_SEARCH_USER_DIRS : 搜索路徑的使用使用AddDllDirectory和SetDllDirectory設(shè)置的路徑(保護(hù)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. 進(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

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

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

這里有一個默認(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);
}

這個函數(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的時候,會獲取鎖LdrpLoaderLock;然而加載的時候又會調(diào)用LdrpRunInitializeRoutines進(jìn)入DllMain。

2.4 LdrpFindOrMapDll

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

LIST_ENTRY LdrpHashTable[LDR_HASH_TABLE_ENTRIES];

其實(shí)每個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

一個進(jìn)程是否可以加載相同名字的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)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-aVEtDGkf-1577506406764)(assets/images/2019-12-28-12-05-34.png)]

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

3.2 死鎖

我們知道,在調(diào)用Dllmain的時候,會占用鎖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)文章

最新評論

开封市| 奉化市| 武陟县| 合江县| 延川县| 桑日县| 出国| 晋中市| 平顺县| 朔州市| 景谷| 伊金霍洛旗| 清水河县| 荣成市| 馆陶县| 荔波县| 岳阳县| 宜昌市| 章丘市| 中西区| 民县| 灵璧县| 酉阳| 合水县| 阳西县| 德清县| 姚安县| 邓州市| 城步| 清丰县| 博罗县| 新和县| 潍坊市| 辉县市| 綦江县| 略阳县| 古交市| 瓦房店市| 民权县| 韶山市| 永兴县|