关于系统搜索某个DLL的路径

引言

我们可以使用LoadLibrary()LoadLibraryEx()来显式地加载某个dll,在我们未提供dll全路径或调用SetDefaultDllDirectories和 AddDllDirectory两个API对DLL路径进行设置时,系统依然会尝试着在某些目录下寻找我们想要的DLL文件,在寻找DLL时,Windows系统会按一定的顺序在不同的目录下查找.


Windows查找DLL的顺序

在以前的Windows版本(Windows xp sp2之前),系统在寻找某个DLL时,会按照以下顺序搜索:

  1. 应用程序EXE所在的路径。
  2. 当前目录(可通过SetCurrentDirectory设置,GetCurrentDirectory获取)
  3. 系统目录(通常是C:\Windows\System32,WOW64程序也会重定向到C:\Windows\SysWOW64目录,可以通过GetSystemDirectory获取)
  4. 16位系统目录(通常是,C:\Windows\System)
  5. Windows目录(通常是,C:\Windows,可以通过GetWindowsDirectory获取)
  6. PATH环境变量指定的目录

由于很多恶意程序利用这一机制,在程序的当前目录放置与程序欲加载的DLL同名的假DLL文件,导致程序加载了错误的DLL(这一过程又叫DLL劫持),在Windows xp sp2之后,WIndows系统默认开启了一种名为安全DLL搜索模式(SafeDllSearchMode)的机制,该机制将使系统按如下模式搜索DLL:

  1. 应用程序EXE所在的路径。
  2. 系统目录。
  3. 16位系统目录
  4. Windows目录
  5. 当前目录
  6. PATH环境变量指定的目录

HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager目录下的键值SafeDllSearchMode的值设置为0时将关闭该机制,win7win10没找到该键值但验证下来的确开启了该机制

验证SafeDllSearchMode

我们可以验证这一机制,具体做法为:

  • 编写一个dll
  • 在dllmain中输出调用GetModuleFileName输出当前dll所在目录
  • 将该dll在上面提到的所有路径中都放置一个dll的副本
  • 使用我们的程序加载该dll(仅传入dll名字)
  • 这样输出的值就是系统搜索dll的第一个路径
  • 删除该路径下的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这一项(第一个输出结果是多余的,操作失误了一下):
关于系统搜索某个DLL的路径_第1张图片

可以看见,SafeDllSearchMode的确在起作用

DLL重定向

将一个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重定向有不少可说的,这里暂且放下

你可能感兴趣的:(关于系统搜索某个DLL的路径)