Mac 上的微信,一截图主窗口就是一片空白。第一反应是"是不是有人往 /Applications 里写了什么东西"。查下来根因在微信自己身上:它给自己的窗口调了 setSharingType:,让系统截图和录屏只能拍到空白。

这篇记怎么排除"系统/App 被篡改",怎么在二进制里定位到那个开关,为什么这个版本里找不到关它的地方,以及不改客户端能怎么正常截图。


一、TL;DR

  • 现象:用系统截图(⇧⌘4/⇧⌘5)或第三方工具截微信主窗口,出来是空白。
  • 排除了被篡改:微信.app 是 Mac App Store 正版,codesign --verify --deep --strict 和 spctl 都通过,扩展属性正常,系统里没有配置描述文件、没有 MDM 管控。
  • 根因:微信的 wechat.dylib 调了 macOS 的 -[NSWindow setSharingType:]。窗口被标记后,系统截图、第三方截图、录屏都只能拍到空白。这是应用层的隐私功能,和"往系统目录写东西"无关。
  • 它只隐藏主窗口。独立聊天窗口、从主窗口打开的图片/视频/文件窗口、以及微信自带截图(⌃⌘A,勾了"保留当前窗口")都能正常截。
  • 这个版本里设置界面找不到对应开关,大概率是服务端灰度下发、默认开着但没放出 UI。
  • 要彻底去掉只能改客户端(注入 dylib hook 掉 setSharingType:),代价不小、有账号风险,我没做。

二、现象与第一个假设

截微信主窗口,图是空白的;截别的 App、截桌面都正常。用户最初怀疑是系统 /Applications 被写了某种 flag。先按这个方向把"系统/App 被动过手脚"彻底排除掉。

三、排除:App 和系统都没被改

App 本身没被改过。

A="/Applications/微信.app"
codesign -dv --verbose=2 "$A"
codesign --verify --deep --strict "$A"   # 通过
spctl -a -vv "$A"                         # 通过

Mac App Store 正版 4.1.13,开发者是 Tencent Mobile International。只要包里有一个文件被改动或新增,签名就会失效——它没失效。

扩展属性正常。 xattr 只有 App Store 的元数据和系统的 com.apple.macl,没有异常的 xattr。

系统层面没有管控。 没装任何配置描述文件,也没有 /Library/Managed Preferences——不是 MDM 下发的截屏限制。

结论:挡住截图的东西不在系统里,在微信自己身上。

四、定位:在二进制里找那个开关

在 App 的 Mach-O 里搜防截屏相关的 API:

A="/Applications/微信.app"
strings -a "$A/Contents/Resources/wechat.dylib" \
  | grep -nE "setSharingType|sharingType|ScreenCaptureKit|CGWindowListCreateImage"

命中 setSharingType:。再把 dylib 里相关的界面文案导出来看,中英文都有:

Added the option to hide the Weixin window for screenshots by default. When it is enabled, Weixin’s main window won’t appear during screen sharing, screen mirroring, or screenshots via other apps.

中文版对应的是"演示屏幕时不显示微信主窗口"(有的版本叫"演示屏幕时隐藏微信主窗口")。

五、根因

-[NSWindow setSharingType:] 是 macOS 提供的接口,把窗口的共享类型设成 NSWindowSharingNone 后,系统截图、其他截图工具、录屏对这个窗口都只能拿到空白。微信把主窗口标成了这个状态。

这是一个可开可关的应用层设置(文案是 “When it is enabled…” 的写法),可能是更新后默认开启的。关键特征:

  • 只隐藏主窗口。 从主窗口打开的图片、视频、文件是独立窗口,截它们正常;设置窗口本身也不是主窗口,所以设置页能截出来。
  • 这个版本的设置界面里找不到对应开关。腾讯常把新功能先灰度推给一部分用户,很可能功能默认开着、但 UI 里还没给开关。

怎么确认是它在起作用:截屏/录屏时如果弹出"其他应用正在录制屏幕,已对其他应用隐藏微信主窗口",就是它。

六、不改客户端的解法

日常截图其实绕得过去,这些都不受影响:

  1. 微信自带截图 ⌃⌘A:截主窗口也正常。它的截图选项里有"保留当前窗口",勾上即可。
  2. 把聊天拖成独立窗口:双击会话或右键"在独立窗口中打开",再用任何工具截这个独立窗口。(推断独立窗口不算主窗口,没逐一实测每种工具,但机制上成立。)
  3. 图片/视频/文件单独打开再截:这些是独立窗口,系统截图和录屏都正常。
  4. 用手机微信截:没这个限制。

真正绕不过去的只有一种场景:用 OBS 录屏或开会共享时,要把微信主窗口本身录进去。

七、改客户端这条路(描述,没做)

如果一定要去掉主窗口的这个限制,macOS 不允许别的进程去改微信窗口的属性,所以只能把代码注进微信自己的进程:

  1. 复制一份 微信.app,原版留着当退路;
  2. 写一个小 dylib,hook -[NSWindow setSharingType:],不管传什么值都强制改成允许截图;
  3. 把 dylib 加进副本的加载命令,ad-hoc 重签副本。

代价要先说清楚,所以我没有做:

  • 重签之后 App Store 签名和 Keychain 访问权限就没了,大概率要重新登录,本地聊天记录的数据库也可能读不出来;
  • 微信里有客户端完整性和截屏状态的上报模块,改过的客户端有被检测到、账号受限的风险;
  • 每次微信更新都会把改动覆盖掉,得重做。

八、可复用的判断

  1. 截图空白先分清是系统还是 App。 App 被改过 → codesign --verify --deep --strict 会失败;系统管控 → 查配置描述文件和 /Library/Managed Preferences。两样都干净,就往 App 自己身上找。
  2. setSharingType: 是 macOS 防截屏的标准手段,银行、IM 这类 App 常用。在 Mach-O 里 strings | grep setSharingType 基本能确认。
  3. 这类窗口级防截屏通常只作用于主窗口,拖成独立窗口、或用 App 自带截图往往能绕过,不必动客户端。