一个模块可以被卸载,那么我如何确定它是否还在内存中呢?我有一个从 GetModuleHandle 获得的句柄。当我尝试对其调用 GetHandleInformation 时,我看到错误 0xc0000008 -“指定了无效的 HANDLE”。这发生在它可能被卸载之前。
3 回答
术语“句柄”在这里有点过分——Win32 API 中许多不同类别的对象被称为“句柄”。
GetHandleInformation 用于内核对象的句柄——文件、注册表项、互斥体等。
GetModuleHandle 返回的 HMODULE 由加载程序使用,它不是实际的内核对象,因此 GetHandleInformation 失败。但是,您在 GetHandleInformation 中获得的两个标志对 HMODULE 都没有意义。
如果你想检查 HMODULE 是否仍然加载在内存中,你可以调用 GetModuleHandle - 这个 API 应该足够快,可以多次调用。但是,GetModuleHandle 的结果在它返回的那一刻可能是无效的——另一个线程可能调用了 FreeLibrary。最好确保 DLL 确实保持加载状态。您可以通过自己调用 LoadLibrary 或调用 GetModuleHandleEx 来执行此操作,这将增加 DLL 的引用计数。
两种解决方案:
1
在 HMODULE 上调用 GetModuleFileName()。如果加载了模块,您将获得一个有效的文件名。如果未加载,您将无法获得有效的文件名。确保在调用 GetModuleFileName() 之前将返回的文件名数组的第一个字节设置为 '\0' 或检查返回值。如果您在调用之前设置第一个字节,您可以有效地忽略返回值并将零长度字符串视为“未加载”信号。
TCHAR szModName[MAX_PATH + 1];
szModName[0] = _T('\0');
GetModuleFileName(hMod, szModName, MAX_PATH);
// zero length string if not loaded, valid DLL name if still loaded
2
调用 VirtualQuery() 传递 HMODULE 作为要查询的地址。作为一个实验,在加载的库和您知道要释放的库上执行此操作。你会发现它们对于返回的 MEMORY_BASIC_INFORMATION 有非常不同的结果。我留给你制定一个合适的算法来确定两者之间的差异。
警告
当然,另一个线程可能会在您运行这些测试中的任何一个时卸载库的警告适用。根据我的经验,这不太可能发生,但这在很大程度上取决于您在做什么、为什么要这样做以及何时执行程序的执行路径。小心使用。
这是非常简单的 API。
PIMAGE_NT_HEADERS (NTAPI* _RtlImageNtHeader)
示例程序:
(PVOID)typedef PIMAGE_NT_HEADERS (NTAPI *RTLIMAGENTHEADER)(PVOID);
RTLIMAGENTHEADER RtlImageNtHeader;
HMODULE hDll = GetModuleHandle("ntdll.dll");
HMODULE hDllTmp = LoadLibrary("ws2_32.dll");
RtlImageNtHeader = (RTLIMAGENTHEADER)GetProcAddress(hDll,"RtlImageNtHeader");
struct _IMAGE_NT_HEADERS *r,*r2;
r= RtlImageNtHeader(hDllTmp);
FreeLibrary(hDllTmp);
r2= RtlImageNtHeader(hDllTmp);
//r = NULL
//r2 = return ws2_32 PE Header address