You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何防止C++编写的DLL被重复注入以避免程序崩溃?

检测C++注入DLL是否已存在于目标进程中

当重复注入DLL导致目标进程崩溃时,你可以通过以下两种常用方法实现DLL自检测,避免重复加载:

方法一:使用全局互斥量(推荐,可靠)

利用Windows内核对象的唯一性,在DLL加载时创建一个唯一命名的互斥量,若创建失败且错误码为ERROR_ALREADY_EXISTS,则说明DLL已注入当前进程。

代码示例

#include <windows.h>
#include <string>

// 定义唯一互斥量名称,建议使用GUID或包含DLL专属标识避免冲突
#define MUTEX_NAME L"Global\\MyInjectedDLL_5F4B3A2C-1D6E-7G8H-9I0J-KL1M2N3O4P5Q"

BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        // 禁用线程通知,减少不必要的回调
        DisableThreadLibraryCalls(hModule);

        // 创建互斥量,Global前缀确保跨用户会话生效(按需选择)
        HANDLE hMutex = CreateMutexW(NULL, TRUE, MUTEX_NAME);
        if (!hMutex)
        {
            MessageBoxW(NULL, L"互斥量创建失败", L"错误", MB_ICONERROR);
            return FALSE;
        }

        // 检查互斥量是否已存在
        if (GetLastError() == ERROR_ALREADY_EXISTS)
        {
            MessageBoxW(NULL, L"DLL已注入当前进程,无需重复操作", L"提示", MB_ICONINFORMATION);
            CloseHandle(hMutex);
            return FALSE; // 返回FALSE阻止DLL加载
        }
        break;

    case DLL_PROCESS_DETACH:
        // 进程卸载DLL时释放互斥量
        HANDLE hMutex = OpenMutexW(MUTEX_ALL_ACCESS, FALSE, MUTEX_NAME);
        if (hMutex)
        {
            CloseHandle(hMutex);
        }
        break;

    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
        break;
    }
    return TRUE;
}

方法二:枚举进程模块检查

通过枚举当前进程已加载的模块,对比模块文件名判断自身是否已注入。

代码示例

#include <windows.h>
#include <psapi.h>
#include <string>

#pragma comment(lib, "psapi.lib")

BOOL IsDllAlreadyLoaded(HMODULE hCurrentModule)
{
    HMODULE hModules[1024];
    DWORD cbNeeded;
    if (!EnumProcessModules(GetCurrentProcess(), hModules, sizeof(hModules), &cbNeeded))
        return FALSE;

    // 获取当前DLL的文件名(仅取文件名,忽略路径)
    WCHAR szCurrentDll[MAX_PATH];
    GetModuleFileNameW(hCurrentModule, szCurrentDll, MAX_PATH);
    std::wstring strCurrentName = szCurrentDll;
    size_t slashPos = strCurrentName.find_last_of(L"\\/");
    if (slashPos != std::wstring::npos)
        strCurrentName = strCurrentName.substr(slashPos + 1);

    // 遍历所有加载的模块
    DWORD moduleCount = cbNeeded / sizeof(HMODULE);
    for (DWORD i = 0; i < moduleCount; i++)
    {
        if (hModules[i] == hCurrentModule)
            continue; // 跳过当前正在加载的实例

        WCHAR szModule[MAX_PATH];
        if (GetModuleFileNameW(hModules[i], szModule, MAX_PATH))
        {
            std::wstring strModuleName = szModule;
            slashPos = strModuleName.find_last_of(L"\\/");
            if (slashPos != std::wstring::npos)
                strModuleName = strModuleName.substr(slashPos + 1);

            // 不区分大小写对比文件名
            if (_wcsicmp(strCurrentName.c_str(), strModuleName.c_str()) == 0)
                return TRUE;
        }
    }
    return FALSE;
}

BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        DisableThreadLibraryCalls(hModule);

        if (IsDllAlreadyLoaded(hModule))
        {
            MessageBoxW(NULL, L"DLL已注入当前进程", L"提示", MB_ICONINFORMATION);
            return FALSE;
        }
        break;

    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

注意事项

  • 互斥量命名必须唯一,推荐使用GUID生成,避免与其他程序冲突;若无需跨用户会话,可去掉Global\\前缀。
  • 若目标进程无UI线程,弹窗可能无法显示,此时可直接返回FALSE退出DLL加载,无需弹窗。
  • 枚举模块的方法存在轻微性能开销,适合对跨会话检测无需求的场景。

内容的提问来源于stack exchange,提问作者NoobDev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 20:25:13