打开网易新闻 查看精彩图片

当智能家居没有想象中那么智能时,一位网络安全研究员决定亲自“动手升级”。他选择“黑”进家中的联网洗衣机,通过抓包分析、逆向工程乃至暴力破解,成功解密其通信协议,并接入 Discord 通知系统,让这台原本“半智能”的家电真正具备了互动能力。从识别 API 接口到写出状态推送脚本,这不仅是一次技术实验,也是一场对现有智能家居生态的“轻松反击”。

原文链接:https://nexy.blog/2025/07/27/how-i-hacked-my-washing-machine/-the-notification-script

作者 | nexy7574 责编 | 苏宓

出品 | CSDN(ID:CSDNnews)

这两天,我和一个朋友闲来无事,就动手“黑”进了家里的洗衣机。一方面是图个乐子,另一方面,也确实有点实用价值。

打开网易新闻 查看精彩图片

背景介绍

简单交代下背景:我最近和几个朋友合租,搬到了大学附近的一所新家里。房子是带家具的,电器也都配齐了,包括一台洗衣机。按照典型的租房配置,这台洗衣机是那种很便宜的“智能”型号,就自带了个 Wi-Fi 连接和一个可以用来远程控制的手机 App,市面上很常见。

重点来了——合租这套房子里一半的人是学网络安全的,所以我们一开始一脸嫌弃:洗衣机上网?搞笑的吧。

然后……我们还是让洗衣机先连上了网看看情况。

意外的是,这洗衣机居然真的能在洗衣完成后给 App 发通知。虽然 App 还有什么“自清洁”之类的功能,但基本没啥用(应用本身就是一堆廉价的 HTML 页面塞进 WebView 里)。

说实话,有洗衣完成的通知确实挺方便的,尤其是像我这种人,懒得买新衣服,一套衣服要轮流洗着穿,就更需要知道它什么时候洗完了。

不过,问题也来了:这洗衣机一次只能连一个人的账号。要是别人也想接收通知,就得先把它从你的 App 上解绑,然后其他人再重新配网连上……整个流程相当麻烦。

打开网易新闻 查看精彩图片

洗衣机 App 的主界面,这个 App 叫做 “Hoover Wizard”

洗衣完成通知

门铃的故事

顺便提一下,我们住的这栋房子是一座年头很久的维多利亚式老房子,墙厚、地板厚、门也厚。住在一楼靠近门口的只有一个人,其他人根本听不到有人敲门或门铃响。幸运的是,这个门铃自带一个可以插在别处的接收器,按下门铃时会通过无线信号触发接收器响铃。它使用的是 433MHz 的频率,这类设备中很常见。

我朋友买了个便宜的信号接收器,可以监听这种 433MHz 的信号,然后把门铃按下的消息推送到我们的 Discord 服务器。以后我们还打算加个摄像头,看清到底是谁按的门铃。是不是还挺酷的?

打开网易新闻 查看精彩图片

门铃响了,Discord 群里会推送通知

当然,那个门铃接收器还是会响的,这个通知更多是像一种备用方案。

正是看到这个门铃项目取得了意想不到的成功,我们才灵机一动:那洗衣机是不是也能整一个类似的通知系统?于是……我们开始谋划了。

打开网易新闻 查看精彩图片

计划

在开始这个项目之前,我已经有过一些逆向工程和破解手机应用的经验。比如我曾试着逆向我们大学的考勤 app,当时觉得挺有意思,不过后来因为变得越来越费劲,就没再继续。但正是这个经历让我想到:可以先看看洗衣机的 App 连的是哪儿,然后从中推导出它的 API。这本该是个很简单的操作:在手机上安装一个自签 TLS 证书,再用像 tcpdump 和 mitmproxy 这样的工具抓取 DNS 请求和其他网络流量。

但我们很快发现,大多数网络通信其实都是洗衣机本身在进行。这让我灵机一动:那干脆直接抓取洗衣机的流量,反向分析它的行为。于是我找出一台支持 OpenWRT 的旧路由器,把它刷机后配置成监听 2.4GHz(因为洗衣机只支持这个频段),通过 5GHz 连接到外网,中间再接上我的笔记本电脑(用以太网连着)。

路由器配置好之后,我设置了一个临时 SSID,把洗衣机连上去,记录下它的 IP 地址,然后用 tcpdump 开始抓包。

不幸的是,那些抓包数据后来都没保存下来。

打开网易新闻 查看精彩图片

我的洗衣机在和谁“说话”?

在跟踪了一段时间的流量之后,我注意到四件事:

  1. 洗衣机非常喜欢……和自己对话?我怀疑设计它网络模块的人可能不知道 loopback 接口的存在,因为它发出的大量流量居然都是从自己发到自己 IP 地址的。我觉得这没什么价值,就忽略了。

  2. 它每秒都会向 255.255.255.255 广播一些数据,也不清楚为什么,同样没管。

  3. 它偶尔会连接 simplyfimgmt.candy-hoover.com。这是一个指向某个 Heroku DNS 域名的 CNAME,我觉得有点奇怪,但也没从中抓到什么有用信息。倒是注意到 URL 中有个参数 &encrypted=1,挺引人注意的,尤其是它居然是通过 HTTP 连接的,而不是 HTTPS。

  4. 洗衣机会直接和手机 App 通信,App 通过 HTTP 向端口 80 发请求。

这四点中,只有最后一点比较有意思,于是我决定深入研究。我猜 simplyfimgmt.candy-hoover.com 这个域名可能和通知服务有关,但我从中也没能拿到什么干货。这时我朋友说了一句——我们要不就直接……轮询洗衣机?

这句话说出来真是让人哭笑不得。轮询洗衣机?谁会想到要这么干啊。

打开网易新闻 查看精彩图片

逆向洗衣机的 API

于是,拦截洗衣机通知的想法基本被我放弃了——除非愿意投入大量额外精力。我转而开始研究 App 的行为:比如打开 App 时它会做什么、尝试和洗衣机交互时发生了什么等等。

看起来这个 App 只会访问两个接口:

  1. /http-read.json?encrypted=1

  2. /http-write.json?encrypted=1

可能还有其他,但这两个最显眼,也被 App 频繁调用。

http-write.json 似乎是用来向洗衣机发送命令的,比如启动自清洁模式什么的。但我们并不想主动控制洗衣机,而是希望洗衣机主动向我们“说话”,所以这个接口暂时搁置。

接下来就是 http-read.json,看起来是用来读取洗衣机状态的——但它是加密的!你没看错,那个 ?encrypted=1 参数确实起了作用。

我用浏览器直接打开这个接口,结果出现了一个非常“离谱”的画面:

HTTP/1.1 200 OK  
Content-Type: text/html
45D3EBF9E63886CD240D412B44973ECBCD96ADFF263D56E293C48B9F0C884D00...

没错,洗衣机返回的是加密数据……以十六进制编码……还标注为 HTML 内容类型。这简直让人抓狂——现在我得设法解密这堆数据了。

破解加密方式

你可能会想:“不能直接把 encrypted=0 设上吗?”

我也试过,但结果要么还是返回加密数据,要么直接是 400 错误请求。具体为啥这样我也懒得深究。

三月份的时候,我和朋友去兰卡斯特大学参加了 Hackademia 2025 网络安全会议。虽然现在已经记不清大会上的很多细节,但我还记得我们参加过一个由 Interrupt Labs 组织的 IoT 摄像头破解工作坊。我当时学得并不好——太偏底层了,很多概念都不太熟。但我从中记住了一些关键点,后来确实派上了用场:

  • 很多物联网设备的安全性都很差;

  • 固件通常是公开可下载的;

  • 重用密钥和凭据的情况非常普遍;

  • 很多厂商会用自定义的、非常简单的“加密”方式,比如直接对数据做 XOR 运算。

这让我想到两个可能的办法来获取洗衣机的 XOR 密钥:

  1. 拆开洗衣机,读出固件,然后再装回去(太折腾);

  2. 直接暴力破解这个密钥(简单)。

但问题是,当时我手边只有一台笔记本电脑,用这种设备在一无所知的前提下暴力破解密钥并不理想。再说了,为了读一串密钥去拆一整台洗衣机,也太不理智了。于是我决定用 CyberChef 来尝试暴力破解。

打开网易新闻 查看精彩图片

CyberChef 正在处理来自洗衣机加密响应的 XOR 爆破

但这个过程真的非常艰难。我们完全不知道解密后的内容应该长什么样,也不知道密钥的长度。我这台轻薄本有 8 核 16 线程,尝试破解长度 ≥3 的密钥就已经慢得不行了。

就在我们陷入僵局时,我朋友在查资料时发现,有人已经干过这事了!他找到了一个项目 MelvinGr/CandySimplyFi-tool(https://github.com/MelvinGr/CandySimplyFi-tool)。这是个用 C++ 写的小程序,里面已经内置了若干已知的明文特征,可以在几秒钟内完成密钥爆破。我立刻在笔记本上编译并运行了这个工具,果然成功解密了数据!

接下来我把得到的密钥输入到 CyberChef 中,也成功解密了洗衣机的响应数据。

用找到的密钥在 CyberChef 中成功解密洗衣机返回的数据

幸运的是,后续每次请求返回的数据格式也都是一致的——也就是说,我们还获得了一个稳定的数据结构!我后来借用了 ofalvai/home-assistant-candy (https://github.com/ofalvai/home-assistant-candy)这个项目的一些组件,省去了自己反复试探数据含义的麻烦。

接下来,才是最好玩的部分——动手“调戏”洗衣机,看看它会发生什么变化。

我们一开始只是简单地开机,然后记录状态的变化。接着启动洗衣程序、让它运行一会儿、中途暂停、再重新开始,直到整个流程结束。

在多次测试之后,我们大致摸清了状态字段的变化规律。不过在这个过程中,也发现了一个很大的问题——洗衣机里的 Wi-Fi 芯片如果 30 秒内没有收到请求,就会自动进入休眠状态。除非它自己要连接远程服务器发送 ping,否则你就会发现它时不时就停止响应,非常烦人。

我的解决方案是每隔三秒请求一次接口,保持它在线。

我们观察到以下这些字段会发生变化:

  • Pr:当程序选择旋钮被转动,或者洗衣机开机时,这个值会变。很明显,它代表当前选择的洗衣程序。

  • PrPh:在洗衣过程中会变化,应该是代表当前所处的洗涤阶段。

  • Temp:当程序变更或者手动设置温度时会变化。

  • SpinSp:当设定脱水转速时会变化。

  • RemTime:洗涤过程中频繁变化,表示剩余时间(单位是分钟)。

有趣的是,RemTime 值通常都还挺准的,但有时候会莫名其妙地跳到 10。具体原因我至今也不清楚,猜测是洗衣机在不确定剩余时间时会默认设为 10。在我后面写的通知脚本里,干脆就忽略了 10 这个值——因为我的洗衣机根本不可能在 10 分钟内洗完,9 分钟或 11 分钟都可以,但 10 是假的。

还有一点挺烦的:SpinSp 显示的不是当前真实转速,而是程序设定值。也就是说,就算我的洗衣机实际在以 1495 RPM 旋转,它也只会告诉我“程序设置的是 1500”,太敷衍了。

打开网易新闻 查看精彩图片

写个通知脚本

到这一步,我已经掌握了以下几样东西:

  • 洗衣机的 API 数据结构;

  • 解密数据所需的密钥;

  • 轮询洗衣机的能力;

  • 解密洗衣机返回数据的能力;

  • 读取洗衣机状态的能力。

我接下来要做的,就是写个脚本,定时轮询洗衣机,并把信息通过 Discord 的 webhook 发出去。

实现细节我就不展开了,源代码在这里你可以自己看看:

https://git.nexy7574.co.uk/nex/washing-machine-bot

脚本的主逻辑循环大致如下:

  1. 轮询洗衣机;

  2. 如果状态没变,睡眠 3 秒,再来一次;

  3. 如果状态有变,尝试更新上次发送的 webhook 消息;

  4. 如果找不到之前的消息,那就新建一条;

  5. 等 3 秒,再重复以上过程。

后续我打算加一些功能,比如让用户可以订阅某次洗衣提醒,或者统计一些有趣的使用数据啥的。但目前这个 bot 已经能满足我的需求了。

Discord 中显示当前洗衣机运行状态的消息

接下来干点啥?
打开网易新闻 查看精彩图片
接下来干点啥?

当然不止这些。目前我们已经把门铃接入 Discord,洗衣机状态也实时更新,但就像我朋友说的:“我们还需要更多智能家居操作。”

问题来了,洗衣机是家里唯一一台接入 Wi-Fi 的“智能”设备。那我怎么知道烘干机什么时候转完?或者洗碗机什么时候洗完?我怎么能还在 2025 年用遥控器开电视?这不科学!

所以,未来的计划是:既然有些设备本身“没有大脑”,那就给它们接上一个。

洗碗机:这个最简单,插个智能插座,用功率变化判断运行状态。

烘干机:稍微麻烦点,因为它耗电很大,一般的智能插座可能不支持,支持的大多也贵得离谱。我们可能得靠振动传感器来搞。

电视:用个廉价的红外发射器加个控制板,应该就能搞定遥控控制。

除此之外,我们还可能在前后窗户边装几个蹩脚的摄像头,名义上是“安防监控”,但实际上嘛……纯粹是图个乐。

2025 全球产品经理大会

8月15–16日·北京威斯汀酒店

互联网大厂&AI 创业公司产品人齐聚

12 大专题,趋势洞察 × 实战拆解

扫码领取大会 PPT,抢占 AI 产品新红利