8月份的时候买了个除湿器,这东西可以联网,用他们自己的APP进行控制。当然也可以直接按实体按钮控制,说好听点叫没网也能用😂
我一直不喜欢用装这种APP,那么既然如此,就要搞一波这个德业智能APP了。
思路
- 先拿到除湿器的IP地址, 先nmap一波看看,也许有什么隐藏的接口
- 在路由器上用tcpdump抓这个IP地址的包,又回到了我搞小米摄像头的时候(这个也许以后会写文章讲讲我都做了哪些奇葩的事情)
- 在iOS上抓包,看看都发了什么请求控制开启和关闭,也许是简单的http
除湿器IP
在路由器的DHCP分配记录中可以看到我有一个叫 MiCO 的设备,IP地址是192.168.7.119
。全部TCP 65535端口都扫了一波,发现只开了一个53端口。难道这东西是DNS?咱也不知道,因为所有的DNS请求都会被拦截到路由器,因此也没有进行进一步测试。
路由器抓包
有点懒,还要先搞tcpdump,再加上好像不太会用tcpdump,嗯先不管这个
iOS抓包
在iOS上抓包,有很多选择,比较简单的用stream,在APP里设置一下就可以解密HTTPS了。不过缺点是stream只能抓https和http的,其他协议看不到
抓包之后可以分享har文件,其实就是个json。抓包结果中有这样几个关键信息:
- https://api.deye.com.cn/v3/enduser/userInfo/
Authorization JWT xxxx
看来是Json Web Token做的认证- 接口包含
userInfo, device_list, refresh_token, auth, mqttinfo
等。
随手拿浏览器打开这个api看了一下,令人痴呆惊呆的事情发生了
这竟然是个Django WebApp,并且还开启了DEBUG模式,这……😓
简单整理了下有这么几个接口比较管用
登录接口
https://api.deye.com.cn/v3/enduser/login/
body里提交 loginname
、password
、appid
(任意字符串就可以)、extend
(json string),POST方法,会在返回的数据中拿到token
和clientid
{ "meta": { "code": 0, "message": "signup ok." }, "data": { "token": "xxxxx", "clientid": "123456" } }
这个TOKEN
就是后续的JWT TOKEN
注意这个登录是单点登录的!但是TOKEN一直有效就是了。
获取设备列表
https://api.deye.com.cn/v3/enduser/deviceList/?app=new
GET请求,携带JWT TOKEN,会返回当前帐号绑定的所有设备
{ "meta": { "code": 0, "message": "device list by user." }, "data": [ { "online": true, "product_icon": "https://deye-cloud.oss-cn-shanghai.aliyuncs.com/profile/1597889707825.png", "product_id": "c2c34567df", "product_name": "DYD-E12A3", "device_id": "609", "mac": "04xxxx", "payload": "1411xxxxxhiddenxxxx", "role": 1, "gatewaytype": 0, "device_name": "不再潮湿", "product_type": "dehumidifier", "is_combo": false, "protocol_version": "wifi_V2.4" } ] }
比较关键的信息有product_id,product_name,device_id
等。Payload
还不知道是个啥看起来像是一段hex
MQTT info
https://api.deye.com.cn/v3/enduser/mqttInfo/
同样是GET请求,会返回如下数据
{ "meta": { "code": 0, "message": "mqtt info" }, "data": { "loginname": "ff123/123abc", "clientid": "app_123abc", "endpoint": "ff123", "password": "123456", "mqtthost": "ff123.mqtt.iot.gz.baidubce.com", "mqttport": 1883, "sslport": 1884 } }
看来用户名和密码都有了,并且用的是MQTT协议,这是个啥?
MQTT协议
国内有一家做MQTT协议的公司,叫EMQ,文档写的非常不错!
简单的来说,MQTT主要就是一种适合物联网等处理能力比较差、内存比较小、网络环境复杂不可靠的终端设备用的协议。
这东西简单的说有点像消息队列,属于publish/subscribe的模式,但是设计非常简单。盗个图是这样的
publisher向news这个topic发送消息,所有订阅了news的用户都会收到消息。
当然了每个用户既可以是publisher也可以是subscriber。
这个消息,图片中的some msg就是payload,格式一般有json,plaintext,base64和hex
那既然如此,肯定是我的iPhone发了MQTT协议的数据,看来只能继续抓包了
Wireshark抓包iOS
想要抓到iOS全部通信的数据,那最好的办法是用Wireshark了。当然我这里不是说在iOS上装Wireshark,想啥呢,App Store可没有Wireshark。
具体来说其实很简单,让iPhone的全部流量走你的电脑,电脑抓包就可以了。Windows本本的话,只能想办法开个热点,然后抓无线网卡了,这个操作过于简单,我就不说了。
macOS的话,这样也行,但是还有一种更方便的、开发iOS APP时用到的办法🤔
获取iPhone UDID
把iPhone用线连接到电脑,打开Apple Music,就可以看到UDID,一大长串,别认错了不是序列号。iPhone记得解锁。
创建虚拟网卡
这一步需要rvictl
这个命令,如果你不开发iOS APP,那么大概率这个命令是找不到的,因为这个命令包含在Xcode之中。
当然这并不意味着我们要为了这么一点小事安装10G+的Xcode。这就像你爸爸想要捶你并不需要拿出鸡毛掸子,他只要拎起拳头就够了。同理,我们只要Xcode的一小部分就可以了。当然了,整个Xcode还是要下载的,爸爸毕竟还要从箱子里拿出来鸡毛掸子吓唬吓唬你嘛。
在Apple官网下载适合你的版本的Xcode之后,解压缩,然后进到Xcode.app中(右键-显示包内容),找到这两个文件:
Xcode.app/Contents/Resources/Packages/MobileDevice.pkg Xcode.app/Contents/Resources/Packages/MobileDeviceDevelopment.pkg
安装这两个就可以了。
如果你不想下载10G+的Xcode的话,那么也可以用我的,Catalina 10.15.7,Xcode 12.4
https://www.dropbox.com/sh/4sdl3zyx1m0sw08/AADNcp8WdFEBtNHdUjkiIQmIa?dl=0
安装完成后
sudo rvictl -s UDID
即可看到success
想要停止可以用sudo rvictl -x UDID
如果失败,可能需要先运行
sudo launchctl load -w /Library/Apple/System/Library/LaunchDaemons/com.apple.rpmuxd.plist
之后会多出来一个rvi0
的网卡,Wireshark打开,开始吧!
分析MQTT协议
首先我们要在wireshark的设置中开启MQTT协议,这样Wireshark会解析出来,更方便我们看。然后需要过滤一下
mqtt and ip.addr==192.168.7.116
connect
就像在教科书中说的一样,德业智能先是连接到了MQTT服务器
- Cliend ID: 其实是任意一个随机尽量不可能重复的字符串就可以了,德业智能这里是login接口中的clientid和时间戳的拼接
- username可以从mqttinfo接口中获得,格式其实是
endpoint/clientid
- 密码从接口中获得
- host当然也是接口中获得了,看起来是百度云哦
subscribe
随后德业智能订阅了两条消息,分别为
status/hex
endpoint/product_id/device_id/status/hex
online/json
endpoint/product_id/device_id/online/json
{ "type": "online", "data": { "online": true } }
看起来是德业智能在获取当前除湿器的状态,然后APP动态显示按钮类别
publish
publish证明德业智能向某个topic发布了消息,那么这非常有可能是在控制除湿器,这就需要一点点尝试了
publish online/json
德业智能向online/json
发布了一条消息,内容是一串十六进制
7b2274797065223a226f6e6c696e65222c2264617461223a7b226f6e6c696e65223a747275657d7d
随便找个网站转换一下,发现竟然是如下结果
尝试在MQTTX中连接到这台设备的MQTT服务器,然后以hex格式发布消息到topic,除湿器没有响应
publish command/hex
从名字上来看,这个非常像在控制除湿器
而且message是16进制的0001
,会让人联想是不是0001
是开,0000
是关?
在MQTTX中设置一波,发送,除湿器并没有开……
再往下看,还有一串神奇的数字 080203403c0000000000
,MQTTX中尝试一下,奇迹出现了,除湿器开了。🧐
再抓个包,就会发现080202403c0000000000
是关机代码。
尽管俺也不知道这神秘代码是什么,但是这就像是伏拉夫经常说的“我爱中国”,“咱们中国真是太棒了”一样,直接拿去用就能过上心想事成的生活。
Python实现
已经知道了神秘代码和topic,那么接下来封装一下就可以了。使用paho.mqtt.python
server.publish("endpoint/product_id/device_id/command/hex", "080203403c0000000000")
嗯??没开啊,咋回事?完全没效果。网络不好吗,再试一次,还是如此
莫非财富密码神秘代码失灵了?打开MQTTX订阅这个topic,发现好像不对,怎么是那么一长串数字……
仔细想一下……
噢要hex格式,可是这个库并没有额外参数设置payload的格式啊
不慌不慌,经过我的摸着石头过河、然后搬起石头砸自己的脚的艰苦探索的经历,完全不用考虑struct.pack
什么的,直接发送这样的消息就可以啦
codecs.decode(b"080203403c0000000000","hex") # 当然你也可以选择使用base64 base64.b64decode("CAICQDwAAAAAAA==")
封装
那么要素已经齐全了,接下来就是上click、requests等,写个setup.py,做成console application,直接pip install deye
然后就可以啦!包含登录功能,登录之后会把信息写到 ~/.deye.dbm
中
可以看下面这个视频
后续
- 这个Python package的bug很多!我知道!因为我不太会用click
- 我并没有什么打算添加其他设备的想法,也不想添加更多的command。因为既没有时间、也没有钱购入更多德业的设备,不过接口已经写好了就是……按照原本的想法,是想把这个接入米家的,可是一想这个工作量++
- 花了好几个小时,写完了,把不需要的文件丢到回收站,清空。发现写好的文章也被我顺带着删了……🤬想嘴自己
代码开源在这里 https://github.com/BennyThink/deye