如何检测使用 LoadLibraryEx 加载的模块

Posted

技术标签:

【中文标题】如何检测使用 LoadLibraryEx 加载的模块【英文标题】:How do I detect a module loaded using LoadLibraryEx 【发布时间】:2019-07-05 15:54:50 【问题描述】:

我需要使用诸如GetModuleHandle 或GetModuleFileName 之类的Windows 函数来确定是否在执行我的代码的同一进程中加载​​了特定的dll。

我正在寻找的一个模块是 System.Windows.Forms.dll,但即使它在进程中加载​​...(在这里你可以使用 Process Explorer 看到它)

GetModuleHandle 还是找不到!

    HMODULE modHandle = GetModuleHandle(L"System.Windows.Forms.dll");

GetLastError() 返回 ERROR_MOD_NOT_FOUND

如果函数成功,返回值是指定模块的句柄。 如果函数失败,返回值为NULL。

我认为这可能与 CLR 加载这些 dll 的方式有关。我在LoadLibraryEx 上看到一条注释,如果使用了 LOAD_LIBRARY_AS_DATAFILE 标志,那么:

如果使用此值,系统会将文件映射到调用 进程的虚拟地址空间,就好像它是一个数据文件一样。没有什么是 done 执行或准备执行映射文件。因此,你 不能调用 GetModuleFileName、GetModuleHandle 或 GetProcAddress 与此 DLL。

也许这是我的问题,但不管是什么原因 - 有没有人知道在使用本机/c++ 代码的进程中找到托管 DotNet dll 的方法?

谢谢!

编辑: 根据来自 Castorix 在 cmets 中的建议,我尝试使用 EnumProcessModules:

    HMODULE modules[100];
    void* hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, 0, GetCurrentProcessId());
    if (hProcess)
    
        DWORD bytesNeeded;
        BOOL rc = EnumProcessModules(hProcess, modules, sizeof(modules), &bytesNeeded);
        if (rc)
        
            int count = (int)(bytesNeeded / sizeof(HMODULE));
            for (int i = 0; i < count; i++)
            
                wchar_t moduleName[260];
                GetModuleFileName(modules[i], moduleName, 260);
            
        
    
    CloseHandle(hProcess);

这段代码找到了很多模块,但没有找到 System.Windows.Forms.dll

【问题讨论】:

向我们展示您的代码。 使用我的 DLL 注入器,我在目标进程中使用 EnumProcessModules (+ GetModuleInformation) @JesperJuhl 'if (GetModuleHandle(L"System.Windows.Forms.dll") != 0) ..." 但即使存在该 dll 也会返回 0 @PaulMacGuiheen 请不要在评论中发布代码。 编辑您的问题并在其中包含代码。 将其设为minimal reproducible example。 @Castorix 我读到了这个函数,但是文档说它是用于其他进程的,而我对我的代码正在执行的同一个进程感兴趣 【参考方案1】:

好的,这是一个回答的尝试(或者真的只是一个太长的评论,抱歉)。

就我个人而言,我从未在“进程资源管理器”窗格中看到托管的 .NET DLL,但可能没有仔细/经常查看。但是,我可以(并且总是可以)看到的是 NGENed 图像 (*.ni.dll)。

还要注意这里存在System.Data.dll,它不是 NGENed,而是混合模式程序集,包含本机代码和托管代码。

因此可以得出结论,您在这里只能看到 NGEN 和混合模式“程序集”,因为它们仍然由 LoadLibraryLoadLibraryEx 加载。

还请注意我的评论,我将其复制在这里以便于访问:

我认为 CLR 不使用 LoadLibrary,这可以解释为什么你 使用您描述的 API 无法“看到”它们。实际上, CLR 4 Does Not Use LoadLibrary to Load Assemblies 是相关的博客条目。您可以随时查看来源 (CoreCLR,但不重要),特别是关于它是如何完成的。一世 没有真正好的地方,但你可以开始 here 然后离开它。请改用 ICorDebug 接口。

以下是上面链接的博客条目中的一些相关引述:

您可能会问自己:……谁在乎?好吧,首先它很好 要知道。我没有注意到上面的公共服务公告。 然而,这是一个实现细节——CLR 程序集甚至不是 保证用文件实现,更不用说里面的DLL文件了 使用 LoadLibrary Win32 API 加载的特定格式。

但是,有几个工具和场景已经开始依赖 事实上,CLR 使用 LoadLibrary 加载程序集。例如, 直到 CLR 4,如果您想知道加载了哪些 .NET 程序集 在您的过程中,一个相当可靠的启发式方法是启动 Sysinternals Process Explorer 并查看给定的 DLLs 视图 过程。这不适用于 CLR 4,您可以在此处看到:

坦率地说,我不知道 Process Explorer 如何在您的情况下设法显示程序集(不是 NGENed 和非混合模式) - 您正在观看 CLR2 进程。但是,请注意 PE 不仅使用 Win32 API。它还使用 WMI,并且可能还直接使用 CLR 来获取更多信息。例如,“进程属性/.NET 程序集”和“进程属性/.NET 性能”选项卡很可能分别使用 ICorDebug/ICorProfile 和性能计数器/ETW。

您可能还需要使用这些接口中的一个,或者一般来自unmanaged Debugging API 或unmanaged API 的其他接口。

不管是什么,我不认为EnumProcessModules等会因为上述原因让你到达那里。

【讨论】:

有趣的是你应该提到原生图像 :) 我需要检测 DotNet 程序集的原因是 ngen.exe 没有运行(因为我不会进入),所以原生图像是不在过程中。我在使用您提到的本机 CLR 接口方面取得了很大进展,并将很快使用相关代码进行更新。感谢您的帮助!【参考方案2】:

补充上述答案并提供相关代码;无法使用像 EnumProcessModules 这样的本机函数来检测非 ngen 的 DotNet dll,而我必须使用 C++ 接口连接到 CLR。​​

这里有更多信息:https://blogs.msdn.microsoft.com/calvin_hsia/2013/12/05/use-reflection-from-native-c-code-to-run-managed-code/ 与这个特定问题最相关的代码是:

    HRESULT GetAssemblyFromAppDomain(_AppDomain* pAppDomain, LPCWSTR wszAssemblyName, _Deref_out_opt_ _Assembly **ppAssembly)
    
      *ppAssembly = NULL;
      // get the assemblies into a safearray
      SAFEARRAY *pAssemblyArray = NULL;
      HRESULT hr = pAppDomain->GetAssemblies(&pAssemblyArray);
      if (FAILED(hr)) 
      
        return hr;
      
      // put the safearray into a smart ptr, so it gets released
      CComSafeArray<IUnknown*>    csaAssemblies;
      csaAssemblies.Attach(pAssemblyArray);

      size_t cchAssemblyName = wcslen(wszAssemblyName);

      long cAssemblies = csaAssemblies.GetCount();
      for (long i=0; i<cAssemblies; i++)
      
        CComPtr<_Assembly> spAssembly;
        spAssembly = csaAssemblies[i];
        if (spAssembly == NULL) 
          continue;
        CComBSTR cbstrAssemblyFullName;
        hr = spAssembly->get_FullName(&cbstrAssemblyFullName);
        if (FAILED(hr)) 
          continue;
        // is it the one we want?
        if (cbstrAssemblyFullName != NULL && 
          _wcsnicmp(cbstrAssemblyFullName, 
          wszAssemblyName, 
          cchAssemblyName) == 0)
        
          *ppAssembly = spAssembly.Detach();
          hr = S_OK;
          break;
        
      
      if (*ppAssembly == 0)
      
        hr = E_FAIL;
      
      return hr;
    

这里有一些关于 CLR 接口的信息:

ICLRMetaHost ICLRRuntimeInfo ICorRuntimeHost _AppDomain _Assembly

【讨论】:

以上是关于如何检测使用 LoadLibraryEx 加载的模块的主要内容,如果未能解决你的问题,请参考以下文章

win32ctypes.pywin32.pywintypes.error: (2, ‘LoadLibraryEx‘, ‘系统找不到指定的文件。‘)

LoadLibraryEx() 中更改的搜索路径 (LOAD_WITH_ALTERED_SEARCH_PATH) 是啥

IIS7.5 HTTP 错误 500 调用loadlibraryex失败的解决方法

在不执行 dllmain 函数的情况下加载 Dll

加载C:\Program Files\Common Files\\RunTime\0701\Intel32\ctor.dll时出错找不到指定的模

从DllMain调用LoadLibrary