我们可以使用LoadLibrary()
或LoadLibraryEx()
来显式地加载某个dll,在我们未提供dll全路径或调用SetDefaultDllDirectories和 AddDllDirectory两个API对DLL路径进行设置时,系统依然会尝试着在某些目录下寻找我们想要的DLL文件,在寻找DLL时,Windows系统会按一定的顺序在不同的目录下查找.
在以前的Windows版本(Windows xp sp2之前),系统在寻找某个DLL时,会按照以下顺序搜索:
- 应用程序EXE所在的路径。
- 当前目录(可通过SetCurrentDirectory设置,GetCurrentDirectory获取)
- 系统目录(通常是C:\Windows\System32,WOW64程序也会重定向到C:\Windows\SysWOW64目录,可以通过GetSystemDirectory获取)
- 16位系统目录(通常是,C:\Windows\System)
- Windows目录(通常是,C:\Windows,可以通过GetWindowsDirectory获取)
- PATH环境变量指定的目录
由于很多恶意程序利用这一机制,在程序的当前目录放置与程序欲加载的DLL同名的假DLL文件,导致程序加载了错误的DLL(这一过程又叫DLL劫持),在Windows xp sp2之后,WIndows系统默认开启了一种名为安全DLL搜索模式(SafeDllSearchMode)的机制,该机制将使系统按如下模式搜索DLL:
- 应用程序EXE所在的路径。
- 系统目录。
- 16位系统目录
- Windows目录
- 当前目录
- PATH环境变量指定的目录
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager目录下的键值SafeDllSearchMode的值设置为0时将关闭该机制,win7win10没找到该键值但验证下来的确开启了该机制
我们可以验证这一机制,具体做法为:
GetModuleFileName
输出当前dll所在目录下面是DLL的代码:
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
{
char szDllPath[MAX_PATH] = { 0 };
GetModuleFileNameA(hModule, szDllPath, MAX_PATH);//输出dll所在路径
cout << "DLL PATH: " << szDllPath << endl;
}
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
exe的代码:
int main()
{
HMODULE hModuel = LoadLibraryA("mydll");
if (hModuel == NULL)
{
printf("LOADFAILED");
}
MessageBox(NULL, NULL, NULL, NULL);
return 0;
}
下面是输出结果(这里当前目录为G\Git\cmd,环境变量里也有G\Git\cmd这一项(第一个输出结果是多余的,操作失误了一下):
可以看见,SafeDllSearchMode的确在起作用
将一个DLL名称字符串解析成一个文件前,加载器LDR将利用Dll名称重定向来扩展或者改变DLL名字空间的某些部分
对于应用程序,规则有:
MinWin API集重定向
.LOCAL重定向:
在没有清单文件时(一般默认都有),将某个特定DLL基本名称的所有加载操作(就算指明了全路径)都重定向至EXE所在路径下的本地副本上,假设exe名称为Myapp.exe存放于Myapp\目录下,则重定向的路径为Myapp\Myapp.exe.local\MyLibrary.dll
在Win7WIn10中,欲开启该规则,需要将HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\Current Version\Image File Execution Options下添加一个dword键DevOverrideEnable,并设置值为1,重启电脑方可起效
Fusion(Side by side,并行程序集,SxS)
已知DLL重定向 (KnownDlls):
计算机\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs存放着一些(不带扩展名的)dll名字的键值,在加载这些DLL时,系统将直接去SYSTEM 32目录下加载这些DLL,然后才按上述路径搜索
在计算机\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager下有一键值叫ExcludeFromKnownDLLs,在其中加入指定dll则系统不会对其启用已知DLL重定向
DLL重定向一旦生效,系统将不会去按刚才说的搜索路径去搜索DLL,关于KnownDlls重定向有不少可说的,这里暂且放下