首页
课程
问答
CTF
社区
招聘
峰会
发现
排行榜
知识库
工具下载
看雪20年
看雪商城
证书查询
登录
注册
首页
社区
课程
招聘
发现
问答
CTF
排行榜
知识库
工具下载
峰会
看雪商城
证书查询
社区
编程技术
发新帖
4
1
[原创]SetWindowDisplayAffinity:原理与检测(DWM篇)
发表于: 2026-9-8 17:27
390
[原创]SetWindowDisplayAffinity:原理与检测(DWM篇)
hawkeye_Leo
2026-9-8 17:27
390
**窗口隐藏与反截屏系列 · 第 1 期** 工具:IDA · CE · Hawkeye Community · Cursor MCP --- ## 目录 1. [背景与系列规划](#1-背景与系列规划) 2. [win32k 内核链](#2-win32k-内核链) 3. [DWM 侧线索](#3-dwm-侧线索) 4. [HasProtectedContent 在做什么](#4-hasprotectedcontent-在做什么) 5. [内存验证](#5-内存验证) 6. [检测思路(有法有解)](#6-检测思路有法有解) 7. [开源实现](#7-开源实现) --- ## 1. 背景与系列规划 `SetWindowDisplayAffinity`(常见用法:`WDA_EXCLUDEFROMCAPTURE`)是一种广为人知的反截屏隐私手段。基于此我们打算使用三期来围绕这个话题展开探索。 本系列共三篇,本篇为第一期: - **第 1 期(本文)**:`SetWindowDisplayAffinity` 原理与检测 - 第 2 期:Visual 层隐藏原理与检测 - 第 3 期:基于 BitBlt 的「超级截屏」方案 --- ## 2. win32k 内核链 用户态调用进入内核后,落脚在 `win32kfull!NtUserSetWindowDisplayAffinity`,核心逻辑在 `SetDisplayAffinity`。简化调用链如下: ``` NtUserSetWindowDisplayAffinity └─ SetDisplayAffinity ├─ ComposeWindowIfNeeded ├─ InternalSetProp / InternalRemoveProp("SysDispAffinity") └─ ChangeWindowTreeProtection └─ ProtectWindowBitmap ├─ ChangeWindowBitmapOwner └─ GreProtectSpriteContent └─ DwmAsyncUpdateSprite → dwm.exe ``` `DwmAsyncUpdateSprite` 最终发送了一个 LPC 消息到 `dwm.exe` 进程。在整个过程中,除了对 tagWND 中 `SysDispAffinity` 属性有所涉及,其他相关改动较少。另外,不同 Windows 版本中属性编号时常变动,因此,基于内核去检测窗口的隐藏,在稳定性和兼容性上会差一些。 经过对 `dwmcore.dll` 的研究,发现截屏路径与直接显示路径是不同的,分别对应 `CCaptureRenderTarget` 与 `CDDisplayRenderTarget`。结合更多观察与分析,可以给出判定:在反截屏方面,DWM 进程负责完整逻辑,内核方面作用有限。 --- ## 3. DWM 侧线索 窗口是否「截屏隐藏」,从机制上应体现在 DWM `CCaptureRenderTarget` 的合成输出上。`dwm.exe` 加载多个模块;但嫌疑最大的应该是 `dwm.exe`、`dwmcore.dll`、`dwmredir.dll`、`uDWM.dll`(下文先在这几个模块里搜线索)——毕竟名字里都带 `dwm`。 在 Hawkeye Community 中先用 `!probe -pid:<dwm_pid>` attach `dwm.exe`,再对下列四个模块联合搜索 `protect` 关键字: ``` !probe -find:protect -mod:dwmcore,dwmredir,udwm,dwm ```  *图 1. `!probe -find:protect` 命中大量保护相关符号;下文聚焦 `CWindowContext`。* 其中嫌疑最大的是 `dwmredir` 中与窗口内容相关的两个函数: - `CWindowContext::HasProtectedContent` - `CWindowContext::NotifyProtectionChange` 命名已经说明用途:判断窗口内容是否处于保护状态,以及在保护状态变化时通知合成管线。DWM 内还有大量 `IsProtected` / `SetHardwareProtection` 等符号,值得继续挖掘。 --- ## 4. HasProtectedContent 在做什么 反汇编 `dwmredir!CWindowContext::HasProtectedContent` 非常直接:  *图 2. 读取 `[rcx+0x114]`,`SHR 6` 后 `AND 1` — 即检测该 DWORD 的 **bit 6(0x40)**。* 我们可以大胆的猜测:`CWindowContext` 对象在偏移 `+0x114` 处有一个标志字段,bit 6 表示内容是否受保护。 另外我们还可以猜测,每个窗口应该都会对应一个CWindowContext对象。且此对象中应该存在CWindowContext虚表指针。基于此猜测,我们就有了搜索目标。即,基于虚表指针来反推CWindowContext对象的结构。 定位 `CWindowContext` 实例时,注意该类有**两张 vftable**。应使用 `IDwmWindow` 接口对应的那张(本机示例如下):  *图 3. `!probe -find:CWindowContext,vftable -mod:dwmredir` — 定位 `IDwmWindow` vftable。*  *图 4. vftable 地址在 `dwmredir.dll` 内,可用于在 DWM 堆上扫描对象。* --- ## 5. 内存验证 实验:对某窗口调用 `SetWindowDisplayAffinity(WDA_EXCLUDEFROMCAPTURE)`,在 CE 中对 `dwm.exe` 进程搜索该窗口 HWND 的数值,即可找到对应的 `CWindowContext` 对象。(搜索过程和验证过程省略)  *图 5. 对象布局验证:前 8 字节为 vftable;`+0x114` 处 `0xC7` 的 bit6(`0x40`)为保护位;可交叉验证 HWND 与 owning PID。* | 观察项 | 示例 | 说明 | |--------|------|------| | vftable | `0x00007FFAF582A000` | `CWindowContext`(`IDwmWindow`)虚表指针 | | 保护标志 | `+0x114`,bit 6 = 1 | 与 `HasProtectedContent` 汇编一致 | | 窗口 kind | `0x1` / `0x2` / `0x4` | DWM 内部类型枚举(大体猜测):`0x1` 控制台类、`0x2` GUI 窗口(无边框 / 自定义标题栏)、`0x4` GUI 窗口 | | HWND / PID | — | 窗口的句柄和窗口所属进程 PID | 再次确认:`CWindowContext` 对象前 8 字节即为 vftable。不同 Windows 版本中具体偏移会有变动,但 **vftable + 保护位 + HWND/PID** 这一组特征在多版本上相对稳定,可据此设计检测方案。 --- ## 6. 检测思路(有法有解) 基于以上分析,窗口截屏排除相关的合成逻辑主要在 DWM 进程内完成,与内核路径关系不大,因此扫描 `dwm.exe` 是较可行的检测面。 可操作的方案与上文 CE 手工验证类似,但把布局发现自动化: 1. 从 `dwmredir.dll` 符号(PDB)解析 `IDwmWindow` vtable 地址。 2. 扫描 `dwm.exe` 可读写的内存区域,查找等于该 vtable 的指针。 3. **校准**阶段:对已知窗口开启反截屏 — 鹰眼在初始化时对自身主窗口调用 `SetWindowDisplayAffinity` — 再按 vtable、保护位 bit 6、窗口 kind、HWND、所属 PID 锁定匹配对象。 4. 记录 flags / kind / HWND / PID 相对对象起始的字节偏移,供后续扫描复用。 绝对偏移(例如本机样例中标志 DWORD 在 `+0x114`)会随 Windows 版本变化;以 vtable 为锚点、校准一次即可保持工具可移植。Community 实现中,扫描时 window kind 仅匹配 `0x2` 与 `0x4`,尽管内存 dump 里也可能出现 `0x1`。 --- ## 7. 开源实现 本文所述检测思路已在 **Hawkeye Community** 落地并开源,对应命令 `!hidden_windows`(需先 `-init`)与测试命令 `!hidden_windows_sim`。Community 版以 `dwmredir.dll` 中 `CWindowContext` / `IDwmWindow` 的 vtable 为锚点,经一次校准得到 flags / kind / HWND / PID 的相对偏移后扫描 `dwm.exe` 内存。 - 源码仓库:<mark class="encrypted">9dcK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6Z5j5i4N6C8k6i4W2W2i4K6u0V1e0r3g2G2i4K6u0r3K9r3q4%4K9$3g2&6k6g2)9J5k6r3y4G2L8h3#2#2L8X3W2@1P5b7`.`.</mark> - 预编译包:<mark class="encrypted">374K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6Z5j5i4N6C8k6i4W2W2i4K6u0V1e0r3g2G2i4K6u0r3K9r3q4%4K9$3g2&6k6g2)9J5k6r3y4G2L8h3#2#2L8X3W2@1P5g2)9J5c8Y4u0W2L8r3g2S2M7$3g2K6i4K6u0r3L8r3q4@1k6i4y4@1</mark> **系列下一篇**:隐藏Visual — 更加自由和隐蔽的反截屏方案。 ---
传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!!
收藏
・
4
点赞
・
1
打赏
分享
分享到微信
分享到QQ
分享到微博
赞赏记录
参与人
雪币
留言
时间
我的小拇指啊
为你点赞!
2026-9-14 14:52
查看更多
赞赏
×
1 雪花
5 雪花
10 雪花
20 雪花
50 雪花
80 雪花
100 雪花
150 雪花
200 雪花
支付方式:
微信支付
赞赏留言:
快捷留言
感谢分享~
精品文章~
原创内容~
精彩转帖~
助人为乐~
感谢分享~
最新回复
(
2
)
火火火火炎
雪 币:
524
活跃值:
(120)
能力值:
( LV2,RANK:10 )
在线值:
发帖
4
回帖
11
粉丝
1
关注
私信
火火火火炎
2
楼
求成熟dwm的dll交流
2026-9-9 11:28
0
TkBinary
雪 币:
7307
活跃值:
(13379)
能力值:
(RANK:385 )
在线值:
发帖
26
回帖
533
粉丝
250
关注
私信
TkBinary
5
3
楼
拜读一下
2026-9-9 13:52
0
游客
登录
|
注册
方可回帖
回帖
表情
雪币赚取及消费
高级回复
返回
hawkeye_Leo
4
发帖
0
回帖
10
RANK
关注
私信
他的文章
[原创]【开源】Hawkeye TFE:双缓冲 minifilter 文件透明加解密(GPL-3.0)
276
[原创] BitBlt「超级截屏」方案(DWM篇)
526
[原创]Visual 层反截屏原理与 DWM 侧检测
551
[原创]SetWindowDisplayAffinity:原理与检测(DWM篇)
390
关于我们
联系我们
企业服务
看雪公众号
专注于PC、移动、智能设备安全研究及逆向工程的开发者社区
看原图
赞赏
×
雪币:
+
留言:
快捷留言
为你点赞!
返回
顶部