如何让窗口对屏幕捕获不可见,以及在线监考为何存在缺口
在受测 Windows 和 macOS 版本的受控实验室模拟中,公开的窗口捕获设置实现了 100% 规避率,未观察到捕获痕迹。未测试商用监考产品或 Linux。
一句话说明漏洞
Windows 和 macOS 提供文档化的窗口共享或捕获属性设置,使应用窗口在实体显示器上可见,同时被排除在遵循这些设置的捕获路径之外。本研究通过受控实验室模拟展示了由此产生的屏幕捕获监考缺口。
背景:监考如何工作
自 2020 年起在各地大学广泛部署的 WebRTC 远程监考系统,会在考试期间捕获学生屏幕。浏览器扩展或原生客户端请求 getDisplayMedia() 流,将其传至监考服务器,并标记打开窗口、可见应用、剪贴板活动等异常。其隐含安全假设是:
只要捕获你的屏幕,就能看到屏幕上的一切。
这一假设在 Windows 和 macOS 上并不成立。
Windows:SetWindowDisplayAffinity
Microsoft 在 Win32 SDK 中将此 API 文档化为数字版权管理机制,原本用于防止捕获受保护的视频内容,例如 Netflix 桌面应用中的视频。函数签名很简单:
BOOL SetWindowDisplayAffinity(HWND hWnd, DWORD dwAffinity);传入 WDA_EXCLUDEFROMCAPTURE 标志,会要求 Windows 将目标窗口排除在兼容的屏幕捕获路径之外,包括:
BitBlt/PrintWindow(旧版 GDI)IDXGIOutputDuplication(桌面复制 API)Windows.Graphics.Capture(现代 WinRT)- WebRTC 的
getDisplayMedia()捕获链路
窗口继续在实体显示器上正常绘制。用户能看到,监考视频流却看不到。
不需要管理员权限。 API 作用于调用进程拥有的窗口,完全属于正常用户空间操作,无须修改内核、绕过驱动签名或提权。
概念验证(Win32 C)
#include <windows.h>
int main() {
HWND hwnd = CreateWindowEx(/* ... window params ... */);
// Single API call, window now invisible to all screen capture
SetWindowDisplayAffinity(hwnd, WDA_EXCLUDEFROMCAPTURE);
ShowWindow(hwnd, SW_SHOW);
// Window is visible on physical display
// Window is absent from all capture streams
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}macOS:NSWindow.SharingType.none
Apple 的对应机制位于 AppKit。每个 NSWindow 实例都有 sharingType 属性,自 macOS 10.6 (2009) 起就在 AppKit 参考文档中公开:
import AppKit
let window = NSWindow(
contentRect: NSRect(x: 100, y: 100, width: 800, height: 600),
styleMask: [.titled, .closable, .resizable],
backing: .buffered,
defer: false
)
// Single property assignment, window excluded from all capture
window.sharingType = .none
window.makeKeyAndOrderFront(nil)把 sharingType 设为 .none,请求遵循窗口共享标志的捕获路径排除该窗口。相关捕获接口包括:
CGWindowListCreateImage(旧版 Core Graphics 捕获)ScreenCaptureKit(SCStreamConfiguration,Apple 的现代捕获框架)AVCaptureScreenInput(AV Foundation 屏幕录制)- macOS 上 WebRTC 的
getDisplayMedia()
在 macOS 26.3.1 上测试的原生捕获仍受影响。 尽管 Apple 在 macOS 15 中公开说明了 ScreenCaptureKit 的变化,研究仍通过原生工具观察到 .none 导致的窗口排除。它没有测试 macOS 26 上的浏览器捕获,也没有确认所有 ScreenCaptureKit 客户端的行为。
测试结果
| 平台 | 实验室捕获结果 | 观察到的捕获痕迹 |
|---|---|---|
| Windows 10 | 窗口被排除 | 无 |
| Windows 11 | 窗口被排除 | 无 |
| macOS 14 | 窗口被排除 | 无 |
| macOS 26.3.1 | 原生捕获中被排除;未测试浏览器捕获 | 无 |
| Linux (X11) | 未测试 | 未测试 |
| Linux (Wayland) | 未测试 | 未测试 |
结果针对受控实验室中的模拟监考,而非商用产品。macOS 26 评估使用 CGWindowListCreateImage 和 screencapture 工具,而非浏览器 getDisplayMedia() 调用。未测试 Linux 的 X11 或 Wayland。行为研究包含 n = 8 名知情同意的研究团队成员;p = 0.41 并不能证明行为检测不可能。结果反映截至 2026 年三月受测软件版本的状态,后续版本可能不同。
为何在结构上难以修复
该漏洞源于两项合理的操作系统功能之间的设计冲突:
- DRM / 内容保护:媒体公司需要防止未经授权地录制受保护内容。
WDA_EXCLUDEFROMCAPTURE和NSWindow.SharingType.none正是为此而设。
- 考试诚信:监考系统假定屏幕捕获可以完整呈现用户桌面。
这两项要求从根本上不相容。支持 (1) 的操作系统,包括在设计上支持它的 Windows 和 macOS,无法同时在屏幕捕获层保证 (2)。
缓解方案各有取舍,不能提供普遍的考试诚信保证:
- 原生枚举窗口的捕获排除标志,并监测窗口焦点,可提供额外信号,但受论文所述限制约束
- 采用带硬件证明的环境凭据(TPM、Secure Enclave),而非分析屏幕内容
- 推动操作系统层面的捕获完整性 API,并评估残余风险,而非假定仅凭捕获即可证明诚信
负责任披露时间线
| 日期 | 行动 |
|---|---|
| 2026 年一月 | 在 Windows 10 和 macOS 14 的受控实验室中发现并验证问题 |
| 2026 年二月 | 将原生捕获测试扩展至 macOS 26.3.1;通知 Microsoft |
| 2026 年三月 | 通知 Apple 并收到回应 |
| 2026 年四月 | Microsoft 回应并给出安全边界分类 |
| 2026 年五月 | 在收到操作系统厂商回应后公开;发布预印本(CC BY 4.0) |
厂商回复确认其了解操作系统层面的机制。核心困难在于问题出在文档化的系统功能,而非厂商代码错误;缺少操作系统层面的配合,单个厂商能够修复的范围有限。
结论
受测 Windows 和 macOS 捕获路径可省略用户仍能看到的应用窗口,不需要提权,也未观察到捕获痕迹。这说明内容保护功能与“捕获像素完整代表实体显示器”的假设之间存在冲突。研究在受测实验室范围内证明了这一缺口,并未证明所有商用监考产品或所有捕获 API 都能被绕过。
项目页面提供完整的 13 页论文链接。完整概念验证源码未公开发布;经过验证的安全研究人员和监考厂商可依据负责任使用协议申请获取。上方说明性 API 示例仍保留在文章中。