卸载注入的DLL

我有一个使用SetWindowsHookEx注入其他进程的DLL。 在DLL内部,我通过调用GetModuleHandleEx增加模块的引用计数器,以便控制何时卸载模块。

此时,来自这两个API调用的模块引用计数“应该是2”。 当调用进程关闭时,它调用UnhookWindowsHookEx ,将引用计数递减为UnhookWindowsHookEx有一个线程等待几件事,其中一个是调用SetWindowsHookEx的进程的句柄。 当进程消失时,DLL会进行一些清理,终止所有线程,清理内存并处理,然后调用FreeLibraryAndExitThread 。 这减少了计数器并且DLL被卸载。

这是我的问题。 有几个进程,特别是那些没有UI的进程,DLL永远不会被卸载。 我很自信,我已经清理了一切。 我知道我的线程没有运行。

首先,如果您有任何疑难解答提示来帮助发现原因,那将会有所帮助。 否则,我正在考虑使用像NtQueryInformationProcess这样的API来获取模块地址并确认模块处理计数实际上为零,然后调用CreateRemoteThread来调用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("  =======================================================rn");
    str.append("  Module Name:             ").append(Mod32.szModule).append("rn");
    str.append("  =======================================================rnrn");
    str.append("  Module Path:             ").append(Mod32.szExePath).append("rn");
    str.append("  Process ID:              ").append(std::to_string(Mod32.th32ProcessID).c_str()).append("rn");
    str.append("  Load Count (Global):     ").append(std::to_string(static_cast<int>(Mod32.GlblcntUsage != 0xFFFF ? Mod32.GlblcntUsage : -1)).c_str()).append("rn");
    str.append("  Load Count (Process):    ").append(std::to_string(static_cast<int>(Mod32.ProccntUsage != 0xFFFF ? Mod32.ProccntUsage : -1)).c_str()).append("rn");
    str.append("  Base Address:            0x").append(to_hex_string(reinterpret_cast<std::size_t>(Mod32.modBaseAddr), std::hex).c_str()).append("rn");
    str.append("  Base Size:               0x").append(to_hex_string(Mod32.modBaseSize, std::hex).c_str()).append("rnrn");
    str.append("  =======================================================rn");
    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,它完全符合这一点。


我找到了问题的原因和解决方案。 我真的觉得很愚蠢,因为错过了它,并且一直在挣扎。

正如我在原始问题中提到的那样,有问题的进程没有UI。 原来他们确实有一个消息泵运行。 在我们调用UnhookWindowsHookEx来触发卸载后,没有UI的情况下发送消息到这些进程是没有问题的。 (事实上​​,我相信MSDN确实声明窗口消息在调用UnhookWindowsHookEx时不会发送到进程。)

在注入过程调用UnhookWindowsHookEx之后,通过向所有进程广播WM_NULL,消息泵在注入的进程中唤醒,并且DLL引用计数递减。 当注入的DLL最终调用FreeLibraryAndExitThread时,DLL立即被卸载。

这只是解决方案的一部分。 如果注入进程被终止或崩溃,则不会广播消息,因此DLL不会从没有UI的进程中卸载。 正如我之前提到的,我有一个运行在等待注入进程句柄的DLL中的线程。 当注入过程结束时,DLL被发送信号,然后调用PostThreadMessage将WM_NULL发送给进程中的每个线程。 然后,在调用FreeLibraryAndExitThread之前,它会继续等待直到DLL引用计数递减,然后再继续并清理。 因此,几乎立即从所有进程,UI或无UI卸载DLL。

链接地址: http://www.djcxy.com/p/60317.html

上一篇: Unloading an Injected DLL

下一篇: elegant method to inject a dll to processes BEFORE they start