原标题: 一次超出能力范围的尝试

最近家里的智能家居被修复了(喊人修了一下午)

于是想把控制智能家居的接口扒出来,自己封装成wx小程序

应用类型: 智能家居控制

特征: 支持米家第三方控制, 局域网直连(无视账户绑定), APP控制.

网关通信数据获取

抓包

首先想到的是抓包, 直接扒出接口.

应用的用户鉴权是走的HTTPS协议, 可以直接扒出来.

用户登录, 注册, 找回密码, 查询网关绑定(改绑), 用户信息, 修改密码, 操作日志. 这些都有接口(没错就是没有用户登出).

从查询绑定接口可以获得网关ID, 网关Code(类似校验码); 从操作日志可以获得各个智能家居元件的ID.

服务器是杭州阿里云, 鉴权接口是Java写的.(1/26)

但是关键的控制数据走的是TLSv1.2.

而且包裹的不是HTTP协议, 而是其他数据, 尝试了Fiddler/Wireshark/Charles均扒不出内容无奈放弃.

借此熟悉了TCP三次握手/TLS四次握手/中间人攻击, 也算没有白折腾一趟吧.(1/28)

米家

随后想了一下能不能通过米家曲线救国.

然后跑去米家官网查阅了第三方接入的文档 小米IoT开发者平台文档

发现是通过OAuth进行交互的, 那就没我啥事了…(1/28)

这几天没事又看了一遍米家文档(顺便熟悉OAuth)(2/21)

局域网

那最后只有通过局域网方式解决了, 不行的话只能放弃, 用他开发的APP.

使用tcpdump抓取的APP局域网数据:

1
2
3
4
5
6
7
8
C -> 255.255.255.255:8818 574d05383838383838 //UDP广播五次
S -> C 574d0500c0a800670000000000000000000000000000000000158d00020365e8 //UDP回复 8-16是IP地址 -16是网关ID\r\n//TCP三次握手 (直到程序退出或其他原因断线)
C <-> S:8018
C -> S 085f0100006801
S -> C 085f05000068019a5399bc //类似于FTP的Hello.(1/30)
//随后是传输所有设备数据/区域分类/情景模式.
//如果客户端已经有缓存, 就只传输设备开关状态.(2/1)
//控制数据已经可以复现请求了, 但是是无序的(最少我没找到规律).(2/2)

好消息是成功获取到了局域网下APP请求网关的控制数据, 并且网关没有校验流程(直接TCP连上就可以发送控制数据, 没有任何校验), 且控制数据看来长期不会变.

坏消息是每个设备的控制数据是无序的, 只能挨个请求并记录.

已经在着手编写PC的模拟请求了, 使用的是E(复现和记录控制数据用).(2/3)

着手编写基于PHP的模拟请求.

为啥不直接内网穿透+DDNS呢?

  1. 首先当然是没钱, 家里的路由器过老, 没有各自服务商的DDNS服务, 只有向日葵的DDNS.(流量少还不支持自定义域名)

  2. 自己有云服务器可以整frp穿透.

  3. wx小程序不支持TCP Socket, 所以需要”第三方”才能传递数据到网关.

  4. 直接暴露网关的TCP端口是非常不安全的, 所以我准备wx小程序封装->云主机反向代理->内网穿透->内网PHP来实现.

网关通讯程序编写

E

自带的数据报(UDP广播)组件不支持显示对方IP.

随后折腾了HP-Socket, 尝试了”UDPCast””UDPServer””UDPClient”+各种参数组合后仍然拿不到对方IP.

最后突发奇想提取了网关UDP回应里第二段字符才发现这是十六进制的IPv4地址!!!

随后就是tcpdump抓取APP请求->Wireshark跟踪TCP流->E复现并记录数据, 记录了家里所有设备的控制数据.(2/5)

PHP

最开始想的是HTTPS请求, 然后在翻PHP TCP/UDP通信相关文档时发现了WebSocket也许更适合这个项目.(wx小程序也支持WebSocket)

主要实现WebSocket的框架有俩, 一个是Swoole, 一个是Workerman. Swoole几乎没有对Win的支持, 放弃.(内网机器是Win兼任的)

Workerman实现WebSocket的方法异常简单, 直接照抄代码在对应的接收事件里写自己的代码即可.

我在WebSocket传输数据是使用JSON的, 共三个操作(UDP/TCP/HEATBEAT).

但是实际编写代码时还是遇到一些困难:

socket_recv() 在接收TCP/UDP数据时必须要收到才会返回, 收不到就阻塞线程. 定位问题就费了老大功夫(因为WebSocket不会因为这个断连).

最后找到解决方案:

1
2
socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, Array("sec"=>2, "usec"=>0)); //RECV超时两秒就返回
socket_set_option($socket, SOL_SOCKET, SO_SNDTIMEO, Array("sec"=>2, "usec"=>0)); //SEND超时两秒就返回

因为TCP/UDP的返回数据是十六进制的, 刚开始我是直接 json_encode() 就走WebSocket了.

然后在传输有些十六进制数据的时候 json_encode() 会FALSE, 返回空数据.

解决方法非常简单: (我还在 pack() 上纠结了许久)

hex2bin()/bin2hex() 俩兄弟用一下就不会出现这些问题了

PHP端实现通过WebSocket与网关通信(UDP/TCP).(2/7)

PS. 推荐一个WebSocket测试的网址(EasySwoole-WebSocket在线测试工具)带cookie记忆和心跳包发送功能, 我WebSocket测试全程用的这个.

在部署的是使用小皮面板(XP.CN), 全身上下都是坑(比如重启Apache覆盖httpd.conf配置), 都是解决的就不说了. 完成了frp穿透, 实现wss访问.(2/8)

wx小程序编写

几乎没接触过, 这个真的要容我折腾几天.

写了个demo, 实现了设备列表交互和ws通信

放弃, 这个小程序的功能分类和我脆弱的CSS能力几乎过不了审核.(2/9)

控制网页的编写

我倾向于使用Vue来写网页.(虽然我只会写单页, 路由和Cli都不会)

虽然基础薄弱, 也就当练手的项目吧. 网页相比wx小程序限制更小, 也可以用第三方组件库.弥补我脆弱的CSS基础

网页会部署在内网服务器, 走frp(HTTPS)穿透来实现外网访问.(主要还是内网使用, 穿透只是为了以防万一)

更新日志

  1. 页面完成.(2/11)

  2. Vue.js 复习.(2/12)(春节快乐!)

  3. 完成demo.(2/13)

  4. 修改页面, 对小屏(手机)页面更加友好.(2/15)

  5. 完善错误处理流程, 按钮点击流程.(2/17)(过年这几天出去玩了)

项目总览

下面回顾一下我为了方便控制家里的智能家居都做了什么事吧

使用网页进行交互.

HTML/CSS 使用第三方组件 www.layui.com

JS 使用Vue.js

Vue.js

提前使用第三方组件搭建出家里的布局, 绑定 @click + 传递参数 传递点击的具体区域.

使用Json存储家中不同区域的设备, 根据 @click 传递的参数再用 v-for 显示设备按钮, 以供点击.

使用WebSocket与后端进行通信, 做了错误处理和消息返回.

后端

后端使用PHP, Workerman实现Websocket通信, 使用PHP自带的TCP通讯函数实现与网关的TCP通讯.

服务器在家中内网, 使用frp穿透实现远程控制, 但是大多数操作都在内网完成, 穿透仅作为备用手段.

服务器使用定时任务上报状态到我的云主机, 云主机也使用定时任务检测家中服务器是否上报.

如无上报就通过 Server酱->Bark 推送消息到我的手机提醒.

控制智能设备的网关品牌为HBANG, 其他具体的情况我在上文已经写出了.(是我这的本地品牌哦)

总结

这次尝试让我拓展了许多知识: TCP/TLS握手挥手过程, 如何在电脑和手机上进行抓包, PHP中TCP通讯相关函数, WX小程序编写(萌新), Vue编写页面实战.

从开始到结束跨度半个多月(1/27 - 2/17), 上一次有这个热情还是在折腾MC服务器的时候.

感谢父母的鼓励和神奇的百度和支持.

如果没有意外的话, 这次的尝试就到此为止了, 我已经实现了我想要的功能, 也获得不少新的姿势.

2021 / 12 / 17 —-Cnzw

真难

Wireshark使用入门 - Cocowool - 博客园

wireshark抓包新手使用教程 - jack_Meng - 博客园

tcpdump Binary Downloads (32 Bit) | Android tcpdump

tcpdump for Android 移动端抓包

Android实时抓包:tcpdump+nc+wireshark

使用wireshark 动态显示Android应用中的联网数据_51CTO博客_Android wireshark

TCP 握手和挥手图解(有限状态机)_tlcp 连接-CSDN博客

坑-1:

adb版本过旧导致的安卓设备offline

Downloads - ADB Shell

坑-2:

WOL唤醒(Wake On Lan), 不能用半吊子的工具唤醒.

用正经的…害得我BIOS/网卡设置折腾了半天(最后是2006年的一个26K的exe唤醒的).

稍后会尝试frpc的WOL唤醒.成功是成功了, 但是不打算用.(没有第二台设备搭载frp实现WOL唤醒)

坑-3:

Vue配合axios使用时json数据保存失败的情况.

详情 -> vuejs/core · Discussions

最后也没找到解决方案, 只能换用 vue-resource.

/data/local/tcpdump -i any -p -s 0 -w /sdcard/capture.pcap\r\nadb pull /sdcard/capture.pcap capture.pcap