文章总结: 本文介绍了Windows系统中截屏与反截屏的基本原理和技术实现。截屏主要有两种方式:传统的GDI方法使用BitBlt函数复制屏幕内容到内存,但无法捕获GPU加速内容;现代的DesktopDuplicationAPI可直接复制整个桌面图像,包括硬件加速内容,性能更高。反截屏主要通过SetWindowDisplayAffinity函数设置窗口显示亲和度,使窗口在截屏时显示为黑色或空白,能有效防止大多数标准截屏工具。文章提供了基于GDI的截屏代码示例和反截屏的实现方法,展示了实际运行效果。
综合评分: 85
文章分类: 应用安全,终端安全,代码审计,二进制安全,安全工具
截屏与反截屏
原创
MyStackTrace
MyStackTrace
2025年11月13日 00:16
上海
最近有个朋友问我截屏和反截屏的原理,回想着已经很久没有更新 Windows 编程的文章了,借着这个机会写一篇吧。截屏和反截屏的基本原理其实很简单,就是几个 Windows API 配合使用。
截屏的基本原理
截屏的本质都是获取当前显示在屏幕上的图像数据,这些数据存储在一个叫做图形缓冲区的特殊内存区域中。截屏操作,实际上就是读取这个图形缓冲区的内容,并将其保存为一张图片文件(如 PNG、JPG)或复制到剪贴板中。Windows 提供了两种主要的技术来访问图形缓冲区,它们分别对应着不同的时代和技术架构。
一、GDI – 传统、基础的方式
GDI 是 Windows 最经典的图形接口,早期的截屏方法主要依赖于 GDI 函数。其基本原理是先创建一个与屏幕设备上下文兼容的内存设备上下文,然后调用 BitBlt 函数,这个函数会将屏幕设备上下文(代表整个屏幕或某个窗口)的像素数据传输到内存设备上下文中,最后将这个内存中的位图数据保存下来。这种截图方式的优点是简单、稳定、兼容性好,缺点是无法捕获由 GPU 直接渲染的内容,例如使用 DirectX、OpenGL 加速的游戏或视频。BitBlt捕获这类窗口通常会得到黑屏或空白区域,因为它只复制了 GDI 管理的界面层,而无法获取 GPU 直接写入缓冲区的覆盖层。
二、Desktop Duplication API – 现代、高效的方式
这是 Windows 8/8.1 及以上版本引入的 DirectX 家族的一部分,是当前推荐和主流的截屏技术。Desktop Duplication 截屏技术的基本原理其实就在它的名字里,它允许应用程序直接“复制”整个桌面的图像数据,这种方式可以高效地获取包括硬件加速内容(游戏、视频)在内的所有画面,可以实现高性能的屏幕录制。它的优点是可以完美捕获所有类型的画面(GDI、DirectX、OpenGL),性能高,延迟低,适合游戏截图和录屏,它的缺点是编程相对复杂,需要 DirectX 知识,且只支持较新的 Windows 版本。
现在的截图工具(如 Windows 11 的截图工具、Snipaste、OBS、游戏录屏软件)都优先使用 Desktop Duplication API,在 Desktop Duplication API 无法使用的情况下 (比如你的显卡只有基本的显示功能,不支持 Direct X) 会回退到基于 GDI 的传统截屏方式。
下面我们来看个简单的例子程序,演示一下如何使用基于 GDI 的截屏方式。首先使用 GetDC(NULL) 获取整个屏幕的设备上下文(Device Context),然后创建一个与屏幕 DC 兼容的内存设备上下文和一个与屏幕DC 兼容的位图,尺寸与屏幕分辨率相同,接着将新创建的位图选入内存DC 中,最后使用 BitBlt 函数从屏幕 DC 复制整个屏幕区域到内存 DC,完成将屏幕内容复制到内存位图中的操作。如果我们需要把截屏内容保存到文件中,就把内存位图中的内容转成对应格式的图片保存成文件即可。
在这个例子中我把图片保存成 BMP 格式的文件,保存 BMP 图片的函数如下。该函数通过 GetDIBits 获取位图的实际像素数据,将DDB(设备相关位图)转换为DIB(设备无关位图),然后依次把位图文件头 (BITMAPFILEHEADER) 和位图信息头 (BITMAPINFOHEADER) 以及位图像素数据写入文件,我们就得到了一张 BMP 格式的图片。
在命令行中运行这个程序,截图如下,它把它自己运行时的场景给截了下来。
反截屏的基本原理
在 Windows 上反截屏最简单最常用的手段就是使用函数 SetWindowDisplayAffinity 设置窗口显示的亲和度,做法很简单,只需要下面这行代码就可以了。
SetWindowDisplayAffinity(hWnd, WDA_MONITOR | WDA_EXCLUDEFROMCAPTURE);
这行代码告诉 Windows 系统该窗口(句柄 hWnd 表示的窗口)内容不应该被捕获,截屏时该窗口区域会显示为黑色或空白,而且这种方法可以用来反制大多数标准截屏工具,包括上面介绍的基于 GDI 的截屏方式和基于 Desktop Duplication API 的截屏方式。这个 API 防不了内核级的截屏工具,更防不了对着屏幕直接拍照,但是能够防住绝大多数截屏或者录屏工具在大多数场景下已经够用了。
下面我们写个简单的窗口程序,并且使用这个 API 设置这个窗口的显示亲和度,让这个窗口在截屏工具下隐形。下面的代码创建并显示了一个窗口,我们使用 SetWindowDisplayAffinity 设置了这个窗口的显示亲和度为 WDA_MONITOR | WDA_EXCLUDEFROMCAPTURE。
运行这个程序,效果如下,因为无法截图了,我只能用手机对着电脑屏幕拍下来了。
下面是当使用 Snipaste 进行截图时的情况,此时目标窗口中的内容已经看不到了,只能显示出来这个窗口的位置和边框的长度了。
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:MyStackTrace MyStackTrace《截屏与反截屏》