文章总结: 文章介绍了一种从Windows内存转储文件中提取USB摄像头图像的方法,通过xperf跟踪ETW事件获取帧缓冲区地址,触发蓝屏生成内存转储,再用Windbg提取图像数据。这种方法比从USB抓包数据提取更简单,无需繁琐的数据包拼接操作,可成功提取USB颜色相机的JPG图像和红外相机的YUV图像。
综合评分: 91
文章分类: 二进制安全,逆向分析,安全工具,IoT安全,漏洞分析
从 Windows 转储文件中提取 USB 摄像头图像
原创
MyStackTrace
MyStackTrace
2025年11月16日 23:14
北京
前面我写过一篇从 USB 抓包数据中提取 USB 摄像头图像的文章,主要是通过 Python 去解析 Wireshark + USBPcap 抓到的 USB 数据包,按照 UVC 协议中规定的 UVC Payload 格式解析,提取并且拼接出 USB 摄像头的图像。由于 USB 摄像头的每一帧图像是被拆成一个一个的 USB 数据包进行传输的,我们需要进行大量的拼接操作,这种方式还是挺麻烦的,还得写代码,这篇文章我们来介绍另外一种更简单的提取图像的方式,可以一次性的把 USB 摄像头的图像提取出来,不用做繁琐的拼接,而且不用写代码。
这个方法的原理其实很简单,比把大象放到冰箱里还简单,一共就两步:
1、找到 Windows 存放 USB 摄像头图像的缓冲区。
2、把 USB 摄像头图像缓冲区中的数据保存到文件中。
我们先来解决第一步,前面我在使用 DTrace 截取 USB 摄像头图像数据一文中提到过 Windows 的 USB 摄像头驱动(USBVideo.sys)提供了一个 ETW (Event Tracing for Windows) Provider:Microsoft-Windows-USBVideo,我们可以通过 EtwExplorer 工具查看这个 ETW Provider 的各种信息。
这个 ETW Provider 提供了很多用来监控 USB 摄像头行为的事件,比如 USB 摄像头启动事件 (StartUSBVideoDevice),停止事件 (StopUSBVideoDevice),填充帧缓冲区开始事件 (USBVideo_FillUserBufferStart),填充帧缓冲区结束事件 (USBVideo_FillUserBufferStop)。其实 Windows 的 USB 摄像头驱动也是像我们之前在 Python 脚本中做的那样,把 USB 数据包中的 UVC Payload 中的图像数据一一提取出来,并且在图像帧缓冲区中进行拼接,而这个 USBVideo_FillUserBufferStop 事件就是 USB 摄像头驱动拼接完一帧图像之后产生的事件,并且这个事件带了两个字段,分别是表示图像帧缓冲区地址的 pBuf 和表示该缓冲区中图像数据字节数的 ulBytesCopied。
所以我们跟踪 USBVideo_FillUserBufferStop 事件就可以获取到 USB 摄像头驱动中存放图像的帧缓冲区地址及其中图像数据的长度,而跟踪这个事件可以借助微软提供的 Windows Performance Analyzer 工具中 xperf 命令。
第一步解决了,我们有了找到 USB 摄像头存放图像的帧缓冲区的方法,现在我们来看看如何解决第二步,把找到的帧缓冲区中的数据存储到文件中。由于我们找到的帧缓冲区位于 Windows 内核态,我们在用户态很难直接访问到内核态的内存,前面我使用过 DTrace 读取过这个缓冲区,但是使用 DTrace 需要我们通过 bcdedit 命令来修改 Windows 的 Boot Configuration Data (BCD) 把 DTrace 打开,这就需要我们首先关闭 Windows 系统的 PatchGuard、BitLocker、Secure Boot 等安全功能,这一系列操作很麻烦,这里我们来用一种简单粗暴,而且每个人都能做到的办法,就是在 USB 摄像头启动之后触发一次 Windows 蓝屏,蓝屏故障会让 Windows 把当前内存中的数据转储到 C:\Windows\MEMORY.DMP 文件中,这其中当然也包含了 USB 摄像头帧缓冲区中的数据,然后我们使用 Windbg 根据第一步中找到的帧缓冲区地址从 MEMORY.DMP 文件中提取出帧缓冲区中的图像。
现在还有一个问题,就是我们如何触发一次 Windows 蓝屏呢?这个其实很简单,杀掉 Windows 系统中的关键性服务就行了 (Stop code: CRITICAL_PROCESS_DIED),一条 cmd 命令即可:
taskkill /F /IM svchost.exe
好了,所有问题都解决了,我们现在开始动手。
一、使用 xperf 跟踪 USB 摄像头驱动中的 ETW 事件
先使用 xperf -start 命令启动一个 log session 对 USB 摄像头驱动提供的 ETW 事件 (ETW Provider 为 Microsoft-Windows-USBVideo) 进行跟踪,然后启动 USB 摄像头(我是打开了 Windows Hello 的人脸注册程序,这样可以同时启动我电脑上的 USB 颜色相机和红外相机),等个几秒之后,使用 xperf -stop 命令停止这个 log session,最后使用 xperf -merge 命令生成 .etl 文件,在这期间 USB 摄像头驱动生成的 ETW 事件都会被存储在这个 .etl 文件中。
二、触发 Windows 蓝屏,转储内存
保持第一步中打开的 USB 摄像头继续处于打开状态,切勿关闭摄像头 ,因为重启摄像头之后,帧缓冲区的地址就会发生变化,就不是前面获取到的 .etl 中记录的地址了。然后使用 Windows 自带的 taskkill 命令杀掉系统服务。
在上面的命令执行过程中,Windows 就会蓝屏,在 Windows 11 25H2 上微软把蓝屏改成黑屏了😂。
在 Windows 蓝屏/黑屏重启之后,我们会在 C:\Windows 目录下找到转储文件 MEMORY.DMP,其中肯定就包含了我们所需要的 USB 摄像头帧缓冲区中的数据。
三、使用 Windbg 从 MEMORY.DMP 中提取 USB 摄像头图像
首先使用 Windows Performance Analyzer 打开第一步中生成的 .etl 文件,找到 USBVideo_FillUserBufferStop 事件,然后我们就可以看到一个个 USB 摄像头帧缓冲区的地址以及每个缓冲区中存储的图像数据字节数。
然后使用 Windbg 打开 C:\Windows\MEMORY.DMP 转储文件。我们先使用 .db 命令来看看第一个缓冲区中的内容,前面四个字节是 “ff d8 ff e0″,这是 jpg 图像的 magic number,说明这个缓冲区中存放的是 jpg 格式的图像,所以这个缓冲区是属于 USB 颜色相机的,然后我们使用 .writemem 命令根据这个帧缓冲区的地址和长度把帧缓冲区中的数据存储到一个 .jpg 文件中。
我们可以直接打开这个 .jpg 文件,图片如下,确实是一幅正常的图像。
第二个和第三个缓冲区的长度都是 230400 = 640 x 360,这刚好是我那个红外相机输出的分辨率,它的输出格式为 8 位的亮度值,也就是只有 YUV 分量中的 Y 分量,我们使用 .writemem 命令把这两个缓冲区的内容保存成 .yuv 格式的文件。
对于 .yuv 格式我们可以使用 YUView-Win 工具查看,设置好分辨率和 YUV 格式之后,红外图像就可以显示出来了,效果如下,这两张图片的内容不太一样,根据微软对 UVC 协议的扩展协议 (Microsoft extensions to USB Video Class 1.5 specification) 中对红外相机的规定来推测,第一张应该是红外图像中的黑暗帧,第二张应该是红外图像中的明亮帧。
其实我们也可以不使用 Windbg 来解析 Windows 转储文件,通过 Python 包 pykd 也可以从转储文件中提取图像文件,不过这就需要我们自己写 Python 程序了。
最后总结一下,这篇文章中使用的两个重要工具 Windows Performance Analyzer 和 Windbg 都是微软的工具,使用它们也不需要对 Windows 进行特殊设置(比如关闭 Secure Boot,打开测试模式等),我们可以直接在正式产品上使用这种方式去提取 USB 摄像头输出的图像。
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:MyStackTrace MyStackTrace《从 Windows 转储文件中提取 USB 摄像头图像》