如何避免窗口被游戏反作弊误杀以及替代方案"

admin 2026-10-18 12:18:52 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

cover_image

如何避免窗口被游戏反作弊误杀以及替代方案

KsaNL
KsaNL

看雪学苑

2026年7月16日 17:59
上海

在小说阅读器读本章

去阅读

同步透明覆盖窗口与任务栏状态

核心问题

任务栏可能:

  • 被全屏应用遮挡/隐藏
  • 自动隐藏
  • 位置/大小变化
  • 虚拟桌面切换

方案:监听任务栏状态同步

class TaskbarOverlay {
    HWND m_hOverlay = NULL;
    HWND m_hTray = NULL;

// 检测任务栏是否可见
bool IsTaskbarVisible() {
if (!m_hTray || !IsWindowVisible(m_hTray))
return false;

// 检查是否被全屏窗口遮挡
        APPBARDATA abd = { sizeof(abd) };
        UINT state = (UINT)SHAppBarMessage(ABM_GETSTATE, &abd);

// ABS_AUTOHIDE 时可能隐藏了
if (state & ABS_AUTOHIDE) {
            RECT rc;
            GetWindowRect(m_hTray, &rc);
// 任务栏缩到1像素说明已隐藏
if ((rc.bottom - rc.top) <= 2 || (rc.right - rc.left) <= 2)
return false;
        }

// 检查是否有全屏窗口遮挡
return !IsFullscreenAppRunning();
    }

bool IsFullscreenAppRunning() {
// 方法1: 用 Shell API
        APPBARDATA abd = { sizeof(abd) };
// ABM_GETSTATE 不能直接检测全屏
// 用前台窗口判断
        HWND hFg = GetForegroundWindow();
if (!hFg || hFg == m_hTray) return false;

        RECT rcWnd, rcMonitor;
        GetWindowRect(hFg, &rcWnd);

        HMONITOR hMon = MonitorFromWindow(hFg, MONITOR_DEFAULTTONEAREST);
        MONITORINFO mi = { sizeof(mi) };
        GetMonitorInfoW(hMon, &mi);
        rcMonitor = mi.rcMonitor;

// 窗口覆盖整个屏幕 = 全屏
return (rcWnd.left <= rcMonitor.left &&
                rcWnd.top <= rcMonitor.top &&
                rcWnd.right >= rcMonitor.right &&
                rcWnd.bottom >= rcMonitor.bottom);
    }

void SyncOverlay() {
if (IsTaskbarVisible()) {
            RECT rc;
            GetWindowRect(m_hTray, &rc);
            SetWindowPos(m_hOverlay, HWND_TOPMOST,
                rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top,
                SWP_SHOWWINDOW | SWP_NOACTIVATE);
        } else {
            ShowWindow(m_hOverlay, SW_HIDE);
        }
    }
};

更好的方案:Hook Shell 事件

注册 Shell Hook 消息

// 注册 Shell Hook —— 收到前台窗口变化等通知
UINTWM_SHELLHOOK = RegisterWindowMessageW(L\"SHELLHOOK\");
RegisterShellHookWindow(m_hOverlay);

// WndProc 中处理
case WM_SHELLHOOK: // 即上面注册的消息
switch (wParam) {
case HSHELL_RUDEAPPACTIVATED:  // 全屏应用激活
        ShowWindow(m_hOverlay, SW_HIDE);
break;
case HSHELL_WINDOWACTIVATED:   // 普通窗口激活
        SyncOverlay();
break;
case HSHELL_REDRAW:
        SyncOverlay();
break;
    }
break;

监听 ABN\_FULLSCREENAPP

// 注册 AppBar 接收全屏通知
APPBARDATA abd = { sizeof(abd) };
abd.hWnd = m_hOverlay;
abd.uCallbackMessage = WM_USER + 100;
SHAppBarMessage(ABM_NEW, &abd);

// WndProc
case WM_USER + 100:
if (wParam == ABN_FULLSCREENAPP) {
if (lParam) {
// 全屏应用进入 —— 隐藏
            ShowWindow(m_hOverlay, SW_HIDE);
        } else {
// 全屏应用退出 —— 恢复
            SyncOverlay();
        }
    }
break;

最简方案:组合使用

static HWND g_hOverlay;
static HWND g_hTray;

LRESULT CALLBACK OverlayProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
static UINT WM_SHELLHOOK;

switch (msg) {
case WM_CREATE:
        WM_SHELLHOOK = RegisterWindowMessageW(L\"SHELLHOOK\");
        RegisterShellHookWindow(hwnd);

// 同时注册 AppBar 以获取全屏通知
        {
            APPBARDATA abd = { sizeof(abd), hwnd, WM_USER + 1 };
            SHAppBarMessage(ABM_NEW, &abd);
        }
// 定时器兜底(处理边界情况)
        SetTimer(hwnd, 1, 1000, NULL);
break;

case WM_TIMER:
        SyncPosition();
break;

case WM_USER + 1: // AppBar callback
if (wp == ABN_FULLSCREENAPP) {
            ShowWindow(hwnd, lp ? SW_HIDE : SW_SHOWNOACTIVATE);
if (!lp) SyncPosition();
        } else if (wp == ABN_POSCHANGED) {
            SyncPosition(); // 任务栏位置变了
        }
break;

case WM_DESTROY:
        DeregisterShellHookWindow(hwnd);
        {
            APPBARDATA abd = { sizeof(abd), hwnd };
            SHAppBarMessage(ABM_REMOVE, &abd);
        }
break;
    }

// Shell Hook 消息
if (msg == WM_SHELLHOOK) {
switch (wp) {
case HSHELL_RUDEAPPACTIVATED:
            ShowWindow(hwnd, SW_HIDE);
break;
case HSHELL_WINDOWACTIVATED:
            SyncPosition();
            ShowWindow(hwnd, SW_SHOWNOACTIVATE);
break;
        }
return 0;
    }

return DefWindowProcW(hwnd, msg, wp, lp);
}

void SyncPosition() {
    RECT rc;
    GetWindowRect(g_hTray, &rc);
    SetWindowPos(g_hOverlay, HWND_TOPMOST,
        rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top,
        SWP_NOACTIVATE | SWP_SHOWWINDOW);
}

总结

事件 检测方式 响应
全屏游戏进入 ABN_FULLSCREENAPP / HSHELL_RUDEAPPACTIVATED 隐藏 overlay
全屏游戏退出 ABN_FULLSCREENAPP(lp=0) / HSHELL_WINDOWACTIVATED 显示 + 同步位置
任务栏移动/调整大小 ABN_POSCHANGED / Timer 兜底 重新定位
任务栏自动隐藏 Timer 检查 GetWindowRect 跟随隐藏

ABN_FULLSCREENAPP 是最可靠的全屏检测,Windows Shell 自己就是用这个判断是否隐藏任务栏。

避免被游戏反作弊误杀的替代方案

为什么 SetWindowDisplayAffinity 会被误杀

反作弊引擎(EAC、BattlEye、Vanguard)会:

  • 枚举所有窗口,检测 WDA_EXCLUDEFROMCAPTURE
  • 认为这是作弊覆盖层(隐藏外挂 UI)
  • 直接封号或踢出

替代方案

方案 1:不隐藏,而是让窗口\"本来就不会被捕获\"

利用 WS_EX_NOREDIRECTIONBITMAP:

HWND hOverlay = CreateWindowExW(
    WS_EX_NOREDIRECTIONBITMAP | WS_EX_TOPMOST |
    WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW,
L\"Overlay\", NULL,
    WS_POPUP | WS_VISIBLE,
    x, y, w, h, NULL, NULL, hInst, NULL);

这个标志让窗口没有重定向表面(Redirection Surface):

  • BitBlt

/PrintWindow 捕获到的是空的
* 部分截图工具抓不到内容
* 但需要自己通过 DirectComposition 渲染

// 配合 DirectComposition 渲染
IDCompositionDevice* pDCompDevice = nullptr;
DCompositionCreateDevice(nullptr, IID_PPV_ARGS(&pDCompDevice));

IDCompositionTarget* pTarget = nullptr;
pDCompDevice->CreateTargetForHwnd(hOverlay, TRUE, &pTarget);

IDCompositionVisual* pVisual = nullptr;
pDCompDevice->CreateVisual(&pVisual);
pTarget->SetRoot(pVisual);

// 用 DXGI surface 或 D2D 绘制内容到 visual 上
// ...
pDCompDevice->Commit();

缺点: 不是所有截图方式都能规避,Windows.Graphics.Capture 仍可能抓到。

方案 2:根本不用窗口 —— 直接画到任务栏进程内

注入 explorer.exe,在内部创建 DirectComposition visual:

// 注入到 explorer.exe 后
// 获取任务栏的 IDCompositionTarget
// 在现有 visual tree 上添加自己的 visual
// 这样内容属于任务栏本身,不存在独立窗口

优点: 没有独立窗口,反作弊无从检测
缺点: 注入 explorer 本身风险高

方案 3:监听截图行为,临时销毁/重建窗口

不是隐藏,而是真正销毁,截图结束后重建:

// 全局监听
HWINEVENTHOOKhEventHook = SetWinEventHook(
    EVENT_SYSTEM_CAPTURESTART, EVENT_SYSTEM_CAPTUREEND,
    NULL, WinEventProc, 0, 0, WINEVENT_OUTOFCONTEXT);

// 更实用:监听已知截图进程
void MonitorThread() {
while (running) {
boolscreenshotActive =false;

// 检测常见截图工具进程
// SnippingTool, ScreenClippingHost, ShareX 等
HWNDhSnip = FindWindowW(L\"Microsoft-Windows-SnipperToolbar\", NULL);
if (hSnip && IsWindowVisible(hSnip))
            screenshotActive = true;

// 检测 PrintScreen 状态(剪贴板监听)
static DWORDlastSeq = GetClipboardSequenceNumber();
DWORDcurSeq = GetClipboardSequenceNumber();
if (curSeq != lastSeq) {
            lastSeq = curSeq;
// 剪贴板变化,可能是截图
        }

if (screenshotActive && g_hOverlay) {
            DestroyWindow(g_hOverlay);
            g_hOverlay = NULL;
        } else if (!screenshotActive && !g_hOverlay) {
            g_hOverlay = CreateOverlay();
        }

        Sleep(50);
    }
}

缺点: 无法覆盖所有截图方式,有延迟

方案 4:用普通窗口 + 全透明,截图中自然\"看不到\"

如果 overlay 只是少量文字/图标,让它截图时无害可见:

// 不做任何隐藏处理
// 让 overlay 在截图中也出现,但伪装成任务栏的一部分
// 视觉上与任务栏融为一体,截图里看到也无所谓

最安全的策略:如果不需要隐藏,就别隐藏。

最佳实践总结

场景 建议
不跑游戏/无反作弊 WDA_EXCLUDEFROMCAPTURE
有反作弊运行 不隐藏,让 overlay 与任务栏融为一体
需要绝对隐藏 WS_EX_NOREDIRECTIONBITMAP + DComp 渲染
最安全 注入 explorer,不创建独立窗口

现实建议: 如果 overlay 只是显示信息(时钟、监控等),让它截图中可见反而最安全。反作弊针对的是刻意隐藏的覆盖层,一个正常可见的窗口不会触发检测。

看雪ID:KsaNL

https://bbs.kanxue.com/user-home-969146.htm

\*本文为看雪论坛优秀文章,由 KsaNL 原创,转载请注明来自看雪社区

第十届安全开发者峰会【议题征集】-欢迎投稿

往期推荐

一道简单但不简约的堆题——CISCN2026初赛堆题robo分析

第二届软件系统安全赛决赛 StudentManagement 详解

APP整体加固原理

获取SYS加载方源头进程的方案-基于ETW技术

libmsaoaidsec.so 检测监测——绕过 Hook 脚本

球分享

球点赞

球在看

点击阅读原文查看更多


如何避免窗口被游戏反作弊误杀以及替代方案" 网络安全文章

如何避免窗口被游戏反作弊误杀以及替代方案"

本文探讨了Windows任务栏透明覆盖窗口的状态同步机制及规避游戏反作弊误杀的方案。因使用SetWindowDisplayAffinity隐藏窗口易被EAC等反作弊引擎判定为外挂,作者提出四种替代策略:使用无重定向表面标志结合DirectComposition渲染规避截图;注入explorer进程内部绘制以消除独立窗口特征;监听截图行为时临时销毁重建窗口;以及让覆盖层视觉融入任务栏不作隐藏。最终结论指出,反作弊主要针对刻意隐藏的覆盖层,若非必须,保持窗口可见是最安全的策略。
又撕微软!无补丁LegacyHive0day,低权限可读取管理员注册表数据" 网络安全文章

又撕微软!无补丁LegacyHive0day,低权限可读取管理员注册表数据"

安全研究员公开Windows零日漏洞LegacyHive的PoC代码,该漏洞影响所有已打补丁的桌面和服务器系统,允许低权限用户读取管理员注册表数据。漏洞源于用户配置文件服务,虽非远程代码执行,但适合内网提权。研究员与微软因漏洞披露流程产生冲突,已连续公开多个零日漏洞。建议企业主动监控异常注册表加载行为并限制本地用户权限。
一考双证!360智能体工程师(ADEA)认证" 网络安全文章

一考双证!360智能体工程师(ADEA)认证"

看雪与360联合推出ADEA智能体工程师认证课程,面向零基础学员,旨在培养从使用AI到构建AI的实战能力。课程涵盖大模型基础、智能体搭建、Prompt工程、MCP工具集成、RAG知识增强、工作流编排及360独家AI安全等8大模块,共48节系统课。学员通过考试可获得工信部NITE与360企业双认证。课程原价9800元,限时特惠3280元,线上班7月20日截止报名,7月25日考试,线下班在北京、成都、洛阳等地举办。该课程针对智能体开发岗位年增长率380%、人才缺口超500万的市场需求,为技术人提供系统化技能与
观点|谨防境外AI“后门”风险" 网络安全文章

观点|谨防境外AI“后门”风险"

工信部平台提示ClaudeCode存在隐蔽监控后门,可绕过授权向境外回传用户敏感数据,暴露出AI工具超级权限失控与地缘政治风险。建议立即排查卸载受影响版本并拦截异常外联,坚持最小权限与数据隔离原则在沙箱运行,加速国产工具替代,构建全链条防护体系。
评论:0   参与:  0