如何检测使用 LoadLibraryEx 加载的模块
How do I detect a module loaded using LoadLibraryEx
我需要使用像 GetModuleHandle or GetModuleFileName 这样的 windows 函数来查明是否在执行我的代码的同一进程中加载了特定的 dll。
我正在寻找的一个模块是 System.Windows.Forms.dll,但即使它在进程中加载...(在这里您可以使用 Process Explorer 查看它)
GetModuleHandle 还是找不到!
HMODULE modHandle = GetModuleHandle(L"System.Windows.Forms.dll");
GetLastError() returns ERROR_MOD_NOT_FOUND
If the function succeeds, the return value is a handle to the specified module.
If the function fails, the return value is NULL.
我认为这可能与 CLR 加载这些 dll 的方式有关。我在 LoadLibraryEx 上看到一条注释,如果使用 LOAD_LIBRARY_AS_DATAFILE 标志,则:
If this value is used, the system maps the file into the calling
process's virtual address space as if it were a data file. Nothing is
done to execute or prepare to execute the mapped file. Therefore, you
cannot call functions like GetModuleFileName, GetModuleHandle or
GetProcAddress with this DLL.
也许这是我的问题,但不管是什么原因 - 有谁知道在使用本机/c++ 代码的进程中找到托管 DotNet dll 的方法吗?
谢谢!
编辑:
根据 Castorix 在评论中的建议,我尝试使用 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
好的,这是对答案的尝试(或者真的只是一个太长的评论,抱歉)。
就个人而言,我从未在 Process Explorer 窗格中看到托管的 .NET DLL,但可能没有认真/经常地查看。然而,我能(而且总是能)看到的是 NGENed 图像 (*.ni.dll
)。
另请注意此处存在 System.Data.dll
,它不是 NGENed,而是混合模式程序集,包含本机代码和托管代码。
所以可以得出结论,您在这里只能看到 NGENed 和混合模式 "assemblies",因为它们仍然由 LoadLibrary
或 LoadLibraryEx
加载。
另请注意我的评论,我将其复制在这里以便于访问:
I think the CLR does not use LoadLibrary, which would explain why you
cannot "see" them using the APIs you described. In fact,
CLR 4 Does Not Use LoadLibrary to Load Assemblies
is a blog entry that is relevant. You could always check the sources
(CoreCLR, but shouldn't matter), about how it is done in particular. I
have no really good place, but you could start
here
and then go from it. Use the ICorDebug interface instead.
以下是上面链接的博客条目中的一些相关引述:
You may be asking yourself: …who cares? Well, first of all it’s good
to know. I haven’t noticed a public service announcement to the above.
It is an implementation detail, however—CLR assemblies are not even
guaranteed to be implemented using files, not to mention DLL files in
a specific format that are loaded using the LoadLibrary Win32 API.
However, there are several tools and scenarios which have come to rely
on the fact the CLR loads assemblies using LoadLibrary. For example,
up to CLR 4, if you wanted to know which .NET assemblies were loaded
in your process, a fairly reliable heuristic would be to fire up
Sysinternals Process Explorer and look at the DLLs view of a given
process. This doesn’t work for CLR 4, as you can see here:
坦率地说,我不知道 Process Explorer 如何设法在您的案例中显示程序集(不是 NGENed 也不是混合模式)- appart 来自您正在观看 CLR2 进程。但是,请注意,PE 不仅使用 Win32 API。它还使用 WMI,并且可能还直接使用 CLR 来获取更多信息。例如,"Process Properties/.NET Assemblies" 和 "Process Properties/.NET Performance" 选项卡最有可能分别使用 ICorDebug
/ICorProfile
和性能 counters/ETW。
您可能还需要使用这些接口中的一个,或者通常 unmanaged Debugging API or the unmanaged API 中的其他接口。
无论如何,出于上述原因,我认为 EnumProcessModules
等不会让您到达那里。
补充上述答案并提供相关代码;不可能使用像 EnumProcessModules
这样的本机函数来检测 non-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 接口的信息:
我需要使用像 GetModuleHandle or GetModuleFileName 这样的 windows 函数来查明是否在执行我的代码的同一进程中加载了特定的 dll。
我正在寻找的一个模块是 System.Windows.Forms.dll,但即使它在进程中加载...
GetModuleHandle 还是找不到!
HMODULE modHandle = GetModuleHandle(L"System.Windows.Forms.dll");
GetLastError() returns ERROR_MOD_NOT_FOUND
If the function succeeds, the return value is a handle to the specified module. If the function fails, the return value is NULL.
我认为这可能与 CLR 加载这些 dll 的方式有关。我在 LoadLibraryEx 上看到一条注释,如果使用 LOAD_LIBRARY_AS_DATAFILE 标志,则:
If this value is used, the system maps the file into the calling process's virtual address space as if it were a data file. Nothing is done to execute or prepare to execute the mapped file. Therefore, you cannot call functions like GetModuleFileName, GetModuleHandle or GetProcAddress with this DLL.
也许这是我的问题,但不管是什么原因 - 有谁知道在使用本机/c++ 代码的进程中找到托管 DotNet dll 的方法吗?
谢谢!
编辑: 根据 Castorix 在评论中的建议,我尝试使用 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
好的,这是对答案的尝试(或者真的只是一个太长的评论,抱歉)。
就个人而言,我从未在 Process Explorer 窗格中看到托管的 .NET DLL,但可能没有认真/经常地查看。然而,我能(而且总是能)看到的是 NGENed 图像 (*.ni.dll
)。
另请注意此处存在 System.Data.dll
,它不是 NGENed,而是混合模式程序集,包含本机代码和托管代码。
所以可以得出结论,您在这里只能看到 NGENed 和混合模式 "assemblies",因为它们仍然由 LoadLibrary
或 LoadLibraryEx
加载。
另请注意我的评论,我将其复制在这里以便于访问:
I think the CLR does not use LoadLibrary, which would explain why you cannot "see" them using the APIs you described. In fact, CLR 4 Does Not Use LoadLibrary to Load Assemblies is a blog entry that is relevant. You could always check the sources (CoreCLR, but shouldn't matter), about how it is done in particular. I have no really good place, but you could start here and then go from it. Use the ICorDebug interface instead.
以下是上面链接的博客条目中的一些相关引述:
You may be asking yourself: …who cares? Well, first of all it’s good to know. I haven’t noticed a public service announcement to the above. It is an implementation detail, however—CLR assemblies are not even guaranteed to be implemented using files, not to mention DLL files in a specific format that are loaded using the LoadLibrary Win32 API.
However, there are several tools and scenarios which have come to rely on the fact the CLR loads assemblies using LoadLibrary. For example, up to CLR 4, if you wanted to know which .NET assemblies were loaded in your process, a fairly reliable heuristic would be to fire up Sysinternals Process Explorer and look at the DLLs view of a given process. This doesn’t work for CLR 4, as you can see here:
坦率地说,我不知道 Process Explorer 如何设法在您的案例中显示程序集(不是 NGENed 也不是混合模式)- appart 来自您正在观看 CLR2 进程。但是,请注意,PE 不仅使用 Win32 API。它还使用 WMI,并且可能还直接使用 CLR 来获取更多信息。例如,"Process Properties/.NET Assemblies" 和 "Process Properties/.NET Performance" 选项卡最有可能分别使用 ICorDebug
/ICorProfile
和性能 counters/ETW。
您可能还需要使用这些接口中的一个,或者通常 unmanaged Debugging API or the unmanaged API 中的其他接口。
无论如何,出于上述原因,我认为 EnumProcessModules
等不会让您到达那里。
补充上述答案并提供相关代码;不可能使用像 EnumProcessModules
这样的本机函数来检测 non-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 接口的信息: