Journal / 2026 / 工具教程

拆掉 Coway 空气净化器的云:抓包、假 CA 与蓝牙配网

Coway 净化器一断云就只剩按键。本文按 Scott 的复刻记录拆开整条链路:隔离 Wi-Fi 抓包、把明文下载的根证书换成自己的 CA、读出 MQTT 配置字段,再从 Android 蓝牙日志还原配网协议,并标出每一步真正会卡住的地方。

拆掉 Coway 空气净化器的云:抓包、假 CA 与蓝牙配网
封面 取自本文正文首图,点击可放大
目录 · 2641 字 / 6 分钟

一台空气净化器,最核心的功能是「转」和「不转」。Coway Airmega IconS 把这两件事的一部分交给了云:官方 App 慢、频繁要求改密码,Home Assistant 的集成只能假装自己是手机 App,轮询云服务再转发命令。云一坏,自动化就断。

2026 年 11 月 11 日,Scott 发了一篇长文,记录他怎么把这台设备从云里拽出来。起点是 2017 年那场山火——买净化器是为了活命,后来妻子用它当白噪音,于是它变成了每晚必须打开的东西。这个需求很具体:按下「晚安」,灯灭,风扇转到合适档位。云服务做不到稳定,那就自己接管。

这篇文章值得拆,不是因为它用了多新的工具,而是因为它把「抓包—伪造服务端—配网」这条链路走完了,还把失败点写得很清楚。下面按他自己的顺序复述,并补上复刻时容易忽略的检查项。

原来的集成差在哪

Home Assistant 的 Coway 集成思路没问题:模拟手机 App 的请求,轮询云端状态,命令也发到云端。问题在于整条链路仍然经过厂商的服务器。作者说这套东西一年总要坏一两次,原因可能是接口变更、证书轮换或者服务抖动;更长期的风险是,这个型号已经停售,云服务没有理由永远开着。

要去云,设备侧其实只有三件事要回答:

  • 启动时,它从哪里拿根证书?
  • 启动后,它连的是哪个 MQTT 服务?
  • 配网时,是谁把这些地址告诉它的?

把这三件事弄清楚,就不需要再跟厂商的云打交道。

第一步:把设备关进一个能看的网络

最直接的做法是让净化器连一个隔离 Wi-Fi,所有流量从自己的服务器出去:

  • 树莓派开一个独立 AP,只给净化器连。
  • 树莓派自己通过有线接到笔记本,笔记本再桥回家里 Wi-Fi。
  • nftables 把来自设备接口的 HTTP 与 HTTPS 重定向到本机的 mitmproxy,DNS 和 DHCP 放行,其余流量拒绝。

这里有两个实打实的坑。第一,树莓派的板载 PCB 天线信号弱,放在路由器房间时够不到净化器;作者把 Pi 搬进了净化器所在的房间。第二,单射频不能同时开 AP 又连家里 Wi-Fi,所以才有「有线接笔记本、笔记本无线桥接」这种看起来绕、实际省事的接法。

配网过程中 App 报了「setup failed」,但设备其实已经连上隔离网络并发出了请求。这是第一个容易误判的点:应用层报错不等于设备没流量,抓包列表里出现请求才是事实。

第二步:根证书居然走明文 HTTP

mitmproxy 里最先出现的请求很可疑:设备用明文 HTTP 去下载 Amazon 的根证书。作者的推断是,设备根本没有内置根证书,而是每次启动时先把这个公开证书拿回来,再用它校验后面的 HTTPS 连接。

如果推断成立,就可以在 mitmproxy 的响应钩子里,只对这台设备的这个 GET 请求,把返回的证书换成 mitmproxy 自己的 CA 证书。之后设备的 HTTPS 流量就出现在列表里了。

这里有一个很重要的边界,也是作者写明的失败点:mitmproxy 只显示「已经走到 HTTP 层」的连接。如果设备拒绝你伪造的 CA,TLS 握手会直接失败,列表里什么都看不到;看上去像「设备没发请求」,实际是「请求没走到能被记录的那一步」。排障时不要被空列表骗了。

顺带一提,作者原本想利用 2026 年 eMudhra 根证书事件,mitmproxy 甚至内置了对应的选项。但抓到明文证书下载之后,这一步完全不需要了:设备本来就愿意信任一个通过 HTTP 送来的 CA。这不是 eMudhra 的功劳,是设备自己的设计缺口。

第三步:MQTT 地址就在一个 JSON 里

继续抓,会看到一个 POST 请求,返回的是设备的连接配置。关键字段如下:

字段作用抓到的值
host / port设备要连的 MQTT 服务AWS IoT 端点,8883
caUrl根证书下载地址明文 HTTP 的 S3 地址,指向 AmazonRootCA1.pem
certSvrUrl证书签发接口cert.iotp.coway.com 下的 v1 路径
privateUrl / certUrl设备私钥与证书同一套证书服务
svcCode / countryCd设备型号与国家002AIR007 / US
macAddr / certToken设备身份与一次性令牌响应里下发

这张表的意义很直接:谁控制了这个响应,谁就决定了设备连哪个服务器、用哪对密钥。设备随后用 8883 端口跑 MQTT over TLS;用 tcpdump 在 Pi 上抓,能在按 App 按钮时看到服务端到设备的新消息。mitmproxy 不解码 MQTT,只能看原始字节流,但足够确认「按键—消息」的对应关系。

到这一步,已经可以自己写一个配置服务和一个 MQTT 服务,把云替换掉。不过还有个尾巴:设备仍然要用官方 App 配网,抓包环境也仍然要搭。

第四步:蓝牙配网把 App 也替掉

作者想连配网也自己做,于是换了侦察方式:不再一上来就反编译 APK,而是打开 Android 的蓝牙调试日志,用 adb 驱动手机走一遍配网,让 AI 从日志里重建协议。日志显示 Wi-Fi 配置是加密的,于是又把 App 拉下来反编译,把代码和日志对上。

还原出来的流程大致是:

  1. 通过蓝牙读特征值 FFA0,拿到设备身份,其中包括 MAC 地址。
  2. 发送 P001 前缀加一段 protobuf,command 1 里带一个 Base64 编码的随机 key。
  3. 把随机 key 和 MAC 地址的 12 位大写 ASCII 字符拼起来,得到 AES 密钥。
  4. 发送 command 2,内容是加密后的 Wi-Fi 配置 JSON,里面除了 ssid、密码、bssid,还有 caUrl、certSvrUrl、certToken、serial 等字段。

第 4 步是整个故事的转折:caUrl 和 certSvrUrl 这两个地址,是配网方自己填进去的。也就是说,只要自己跑一个服务,在配网时把这两个 URL 指向它,设备就会乖乖来拿证书、取配置、连 MQTT——完全不需要 MITM。

作者提到一个细节:解密所需的数据都在蓝牙日志里,所以可以在不看完整明文的情况下,用「同一份日志能否被自己的脚本正确解密」来验证实现。AI 把这套流程重写成脚本花了大约 20 分钟;他也直言,手工会是几小时到几天。文中还记录了一个小插曲:一个助手拒绝做这一步,另一个接了手。工具选择会影响流程,但结论不变。

复刻时的检查清单

  • 先确认设备是否内置根证书:清空状态后抓启动请求,看有没有明文下载 CA 的动作。
  • 配网时记下 caUrl 与 certSvrUrl。如果它们由配网方下发,后面的 MITM 就可以省掉。
  • 抓包列表为空时,先分清楚是「没请求」还是「TLS 没握上手」,两者的排查方向相反。
  • 蓝牙侧同时保留日志和反编译代码:日志给时序与字段,代码给算法与密钥拼接方式。
  • MQTT 侧先订阅、不改状态;确认主题与消息格式后,再下发控制命令。
  • 保留官方 App 与云路径作为回滚手段。固件升级后协议可能变,原来的脚本要能重新校准。

边界

  • 文中只验证了 IconS 这一台已停售的型号,其他 Wi-Fi 型号未必相同。
  • 「明文下载根证书」是这个设备的具体缺口,不是所有 IoT 设备的通病;不要把它当成通用结论。
  • 自建服务意味着自己维护证书、MQTT 和配网脚本,厂商不再兜底。
  • 作者的前提是控制自己的设备、自己的网络。换到别人的设备上,这就不是逆向,是入侵。

本文基于以下公开报道整理。

参考来源

本文由 AI 辅助整理,流程、字段与失败点来自原文的公开段落与配置示例;我们未在实机上复现,具体数值与结论均为原作者记录。

§

所属主题

Topics

主题只是把现有标签归并后的浏览方式,标签本身不变;全部标签 · 画廊

← 全部文章