我有一个使用 SetWindowsHookEx
注入(inject)其他进程的 DLL。在 DLL 中,我通过调用 GetModuleHandleEx
来增加模块的引用计数器,这样我就可以控制何时卸载模块。
此时,来自这两个 API 调用的模块引用计数“应该”为 2。当调用进程关闭时,它调用 UnhookWindowsHookEx
,将引用计数递减到 1。DLL 有一个线程等待一些事情,其中之一是调用 SetWindowsHookEx
的进程的句柄。当进程消失时,DLL 会进行一些清理、终止所有线程、清理内存和处理,然后调用 FreeLibraryAndExitThread
。这会减少计数器并卸载 DLL。
这是我的问题。有一些进程,尤其是那些没有 UI 的进程,其中 DLL 永远不会被卸载。我非常有信心我已经清理了一切。我知道我的线程都没有运行。
首先,如果您有任何故障排除提示来帮助找出原因,那将会很有帮助。否则,我正在考虑使用诸如 NtQueryInformationProcess
之类的 API 来获取模块地址并确认模块句柄计数实际上为零,然后调用 CreateRemoteThread
注入(inject)对 LdrUnloadDll
的调用以从进程内卸载模块地址。你对这种方法有什么想法?有人有任何示例代码吗?我很难找到如何获取模块句柄计数。
最佳答案
好的.. 开始.. 有很多方法可以从进程中获取模块信息。无证方式和“有证”方式。
结果(记录):
这是“记录”的方式..
#include <windows.h>
#include <TlHelp32.h>
#include <iostream>
#include <sstream>
int strcompare(const char* One, const char* Two, bool CaseSensitive)
{
#if defined _WIN32 || defined _WIN64
return CaseSensitive ? strcmp(One, Two) : _stricmp(One, Two);
#else
return CaseSensitive ? strcmp(One, Two) : strcasecmp(One, Two);
#endif
}
PROCESSENTRY32 GetProcessInfo(const char* ProcessName)
{
void* hSnap = nullptr;
PROCESSENTRY32 Proc32 = {0};
if ((hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)) == INVALID_HANDLE_VALUE)
return Proc32;
Proc32.dwSize = sizeof(PROCESSENTRY32);
while (Process32Next(hSnap, &Proc32))
{
if (!strcompare(ProcessName, Proc32.szExeFile, false))
{
CloseHandle(hSnap);
return Proc32;
}
}
CloseHandle(hSnap);
Proc32 = { 0 };
return Proc32;
}
MODULEENTRY32 GetModuleInfo(std::uint32_t ProcessID, const char* ModuleName)
{
void* hSnap = nullptr;
MODULEENTRY32 Mod32 = {0};
if ((hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, ProcessID)) == INVALID_HANDLE_VALUE)
return Mod32;
Mod32.dwSize = sizeof(MODULEENTRY32);
while (Module32Next(hSnap, &Mod32))
{
if (!strcompare(ModuleName, Mod32.szModule, false))
{
CloseHandle(hSnap);
return Mod32;
}
}
CloseHandle(hSnap);
Mod32 = {0};
return Mod32;
}
std::string ModuleInfoToString(MODULEENTRY32 Mod32)
{
auto to_hex_string = [](std::size_t val, std::ios_base &(*f)(std::ios_base&)) -> std::string
{
std::stringstream oss;
oss << std::hex << std::uppercase << val;
return oss.str();
};
std::string str;
str.append(" =======================================================\r\n");
str.append(" Module Name: ").append(Mod32.szModule).append("\r\n");
str.append(" =======================================================\r\n\r\n");
str.append(" Module Path: ").append(Mod32.szExePath).append("\r\n");
str.append(" Process ID: ").append(std::to_string(Mod32.th32ProcessID).c_str()).append("\r\n");
str.append(" Load Count (Global): ").append(std::to_string(static_cast<int>(Mod32.GlblcntUsage != 0xFFFF ? Mod32.GlblcntUsage : -1)).c_str()).append("\r\n");
str.append(" Load Count (Process): ").append(std::to_string(static_cast<int>(Mod32.ProccntUsage != 0xFFFF ? Mod32.ProccntUsage : -1)).c_str()).append("\r\n");
str.append(" Base Address: 0x").append(to_hex_string(reinterpret_cast<std::size_t>(Mod32.modBaseAddr), std::hex).c_str()).append("\r\n");
str.append(" Base Size: 0x").append(to_hex_string(Mod32.modBaseSize, std::hex).c_str()).append("\r\n\r\n");
str.append(" =======================================================\r\n");
return str;
}
int main()
{
PROCESSENTRY32 ProcessInfo = GetProcessInfo("notepad.exe");
MODULEENTRY32 ME = GetModuleInfo(ProcessInfo.th32ProcessID, "uxtheme.dll");
std::cout<<ModuleInfoToString(ME);
}
未记录的 API 的问题是我从来没有弄清楚为什么动态模块的负载计数总是“6”而静态模块的负载计数总是“-1”..因此,我不会发布它..
如果您只想要加载计数,最好不要使用未记录的 API。未记录的 API 的唯一优点是您可以使用它来“取消链接/隐藏”进程中的模块(就像病毒一样)。它会“取消链接/隐藏”它......而不是“卸载”它。这意味着您可以随时将其“重新链接”回流程的模块列表。
由于您只需要模块引用计数,因此我只包含了“文档化”的 API,它正是这样做的。
关于c++ - 卸载注入(inject)的 DLL,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/25577117/