Reading view

There are new articles available, click to refresh the page.

逆向拼多多上的「关灯神器」,实现蓝牙遥控开关灯

依稀记得以前在某个友链博主那边看到过一篇文章,讲的是因为他们寝室所有人都懒得下床关灯,所以就用树莓派和舵机做了个远程遥控关灯的小玩意儿,当时我就感叹,果然懒才是第一生产力。

自从今年初开始出来租房住,突然就感觉睡前关灯变得好麻烦好麻烦。我的房间里是有好几盏灯的,床头的开关只能控制其中的两盏,剩下的开关在另一个地方,另外还有一个总开关位于进门的门厅处。于是我就陷入了两难之境:

  • 不用总开关:每天睡前把灯一一关掉,第二天回家又得一一开回来;
  • 直接用总开关:开关离床太远,关完灯要摸黑上床,早上起来又得先过去开灯。

不爽,太不爽了!现在都讲究智能家居,我这他喵的是智障家居啊……

作为租房一族,咱们也没法对灯啊开关啥的做电气改造(不然直接换个智能开关就完事儿了),只能使用一些「非侵入式」的方案。首先想到的就是上面提到的开发板 + 舵机,搜了一下似乎已经烂大街了,有不少成熟的方案(ESP8266 居多)。

不过我还是低估了我的懒癌,连动手都不想动了,于是直接去万能的某宝搜索「关灯神器」:

light-switch-products

(为什么不是某宝?别问,问就是消费降级)

哎呀,没想到还真有现成的,竟然还能红外 + 手机遥控,不错哦!

入手「关灯神器」

所谓关灯神器,其实也是一个能接收红外和蓝牙信号的主板,加上一个舵机来控制开关。

product-unboxing

我买的这款是 🐻 卡通款,还带了个小夜灯功能,聊胜于无吧。内置锂电池供电,可以通过 micro USB 接口充电。开模挺精准,普通 86 型墙壁开关完美适配,通过无痕胶和滑槽安装,可以卸下来充电,总体还是挺满意的。

然而,这玩意最操蛋的其实是软件部分……除了附带的红外遥控器,如果想要用手机遥控它开关灯,竟然只能用微信小程序!

微信小程序……小程序……程序……序……

讲道理,我第一次知道微信小程序是还有提供蓝牙能力的,而且还真有人用,我和小伙伴们表示都孤陋寡闻,惊了个呆。

但是这我 TMD 就很不爽了,关个灯我还要打开微信,还得用你的小程序?

rnm

作为一个合格的折腾星人,自然不能如此任人宰割。不就是一个蓝牙设备嘛,小程序能遥控,我难道就不能遥控了?

逆向「关灯神器」小程序

这里主要用到的是 wxappUnpacker 这个工具对小程序解包、反混淆。以 Android 手机为例,小程序的包文件位于:

/data/data/com.tencent.mm/MicroMsg/{hash}/appbrand/pkg/xxxxx.wxapkg

这个目录一般需要 root 权限才能访问,但不巧的是哥已经不折腾 Magisk/Xposed 好多年,手上已经没有 root 过的机器了……不过天无绝人之路,我想起来 MIUI 有自带一个应用数据备份功能,可以备份 App 的 /data 目录。

这玩意儿备份出来的东西其实就是标准的 Android 备份格式 (.ab) 前面加了个自己的文件头,去掉头就可以吃了(划掉),用 Hex Editor 删掉文件头部 414E44 以前的部分,就可以直接当做 .ab 文件处理了。

miui-backup-hex-editor

(谢谢你,雷军!金凡!)

我这里用的是 android-backup-extractor,完整流程如下:

# MIUI 的备份目录adb pull /sdcard/MIUI/backup/AllBackup/20220501_010000/ ./# 去掉 .bak 文件的头部后另存为 .ab 文件java -jar ./abe.jar unpack '微信(com.tencent.mm).ab' mm.tar# 小程序位于 apps/com.tencent.mm/r/MicroMsg/{hash}/appbrand/pkg/*.wxapkgtar xvf mm.tar

目录下可能会有很多小程序的 .wxapkg 包,这里就只能按照时间一个一个试过去了……拿到正确的小程序包以后,使用 wxappUnpacker 解包:

./bingo.sh xxx.wxapkg

解包出来呢,大概就是这样的:

wxapkg-extracted

接下来就是在源码里找控制逻辑和通信值了,看看有没有加密什么的。不得不说,小程序这种前端技术做的东西,确实和裸奔没什么区别,真的能叫做逆向吗……标题党实锤了(作为一个前端仔,看到这些东西就像回家了一样)

随便看了一圈,发现这家制造商的业务线是真的广,光看里面内置的设备类型就有:风扇、茶吧机、干衣机、夜灯、颈椎按摩仪、腰部按摩器、足部按摩器、足浴器、水暖毯、灭蚊器、加湿器、电暖器、按摩椅,感觉像是专门给人生产贴牌智能硬件的,然后遥控模块和小程序用的都是同一套,十分强大。

下面贴几块处理过的关键代码:

// 遥控按钮的入口<i-btn  hover  bindtap="remoteIR"  icon="icon-power"  id="0"  label="大灯"  type="round-big"></i-btn>// 按钮事件处理function remoteIR(e) {  var id = e.currentTarget.id;  // cmd = "01" + "807F" + "12"  // 每种产品都有不同配置,前两个都是固定的,最后的 "12" 代表开关大灯,"08" 为氛围灯  // 还有 "01" 定时十分钟,"03" 定时三十分钟,以及氛围灯亮度等等  var cmd = config.irType + config.irAddr + config.irCMD[id].value;  this.sendCMD("3201", cmd);  this.vibrateLong();}function sendCMD(e, B) {  // format2Byte 函数的作用其实就是补零到 4 位,比如 6 -> 0006  // s = "fe010006320101807F12";  var s = "fe01" + format2Byte(((e.length + B.length) / 2).toString(16)) + e + B;  sendData(s);}

下面的 sendData 也就是实际调用微信小程序 SDK 蓝牙能力的地方:

function sendData(n) {  // ArrayBuffer(10) = FE 01 00 06   32 01 01 80   7F 12  var t = new Uint8Array(    n.match(/[\da-f]{2}/gi).map(function (n) {      return parseInt(n, 16);    })  ).buffer;  wx.writeBLECharacteristicValue({    // 蓝牙设备 ID    deviceId: this.globalData.deviceInfo.deviceId,    // 对应的服务 UUID    serviceId: this.globalData.deviceInfo.serviceId,    // 可写入的特征值 UUID    characteristicId: this.globalData.deviceInfo.writeCharacteristicsId,    // 写入值    value: t,    success: function (n) {},    fail: function (n) {},  });}

简单来说,就是通过 BLE (Bluetooth Low Energy, 蓝牙低功耗) 协议连接开关设备,通过读写对应 Characteristic 的值与其通信,实现设备的控制(如开关灯)。

手动连接设备发送开关灯指令

好了,所有需要的数值现在都已经到手了,下面就尝试跳过微信小程序,手动连接设备发送指令,看看能不能正常操作吧。

这里我用到的是 BLE-调试工具 这个 Android 应用,打开后扫描蓝牙设备,找到并连接「关灯神器」。如果不知道具体是哪个设备,就选看起来比较可疑的。

然后在设备的 Service 中,找到带有 WRITE 属性的特征值 (Characteristic),就是我们用来通信的特征值了。点旁边的写入按钮,把上面逆向出的值填进去……

android-ble-test

见证奇迹的时刻,灯关上了!再次写入同样的值,灯又打开了!

欧耶✌️

还有其他的指令值也可以试一试,比如最后两位改成 08 就是开关氛围灯,等等。

写一个 Android App

想要让这个开关更“智能”,单靠手动操作手机遥控肯定是不够看的。因为手头没有开发板(听说现在树莓派都被炒上天了,不懂),所以还是让闲置的手机发挥余热吧。

好在之前学的那点 Android 开发还没有全忘光,基于 Android-BLE 这个库(其实上面我们用来测试的 App 就是这个库的 demo)和小程序里扒出来的控制逻辑糊了一个遥控 App 出来(代码放在 GitHub):

ble-light-switch

可以看到界面非常简约,不过比什么微信小程序可好用多了。幸福感 UP!

等以后有时间的话,再捣鼓捣鼓接入一下 Home Assistant,加几个自动化,不用动手直接喊 Siri 关灯,岂不美哉?(dreaming)

demo

参考链接

自己动手打造智能物联网设备

自从前段时间研究了智能家居设备之后,我便迷上了物联网和开源硬件。玩遍了市面上各种常见的物联网产品,总觉得各有不足,于是我突发奇想:干脆自己做一个!

对于我而言,现在市面上的物联网产品最大的问题在于太过封闭,不同厂商的产品都得用自家的 App 才能使用,而且大多数用户体验实在不敢恭维。虽然通过我前一篇博文介绍的方法将它们接入到 HomeAssistant 和 HomeKit 之后使用自由度会高得多,但依然无法满足我自己编写家居智能控制程序的要求。

当我问 Siri 客厅的室温是多少的时候,数据是这样传输的:米家温湿度传感器读取温度数据、通过 ZigBee 信号发送给米家智能网关、通过 Wi-Fi 发送到路由器,路由器再传给树莓派、HA 存储数据、Homebridge 读取和广播数据、手机上的 HomeKit 再通过 Wi-Fi 读取数据……可想而知,数据传输的环节越多,稳定性和数据时效性就越低。比如我想做一个根据电视画面的亮度来调整房间灯光亮度的设备,当电视在播放夜间画面(亮度较低)的时候调暗房间灯光来减轻屏幕反光的影响,反之则调亮灯光方便我吃东西。这时候就需要以百毫秒级的速率来读取光线传感器的数据,一般的商业产品很难满足这种需求。

此外,价格也是很重要的因素:一个硬件成本不超过十元的智能插座零售价高达两百多元;即使选用相对便宜的米家系列传感器和 Sonoff 开关,要实现我心目中真正的智能家居——家中所有电器全部智能化、每个角落都有人体感应器也将是一笔非常可观的开支。

初识 Arduino

于是我开始深入了解开源硬件方面的知识来打造完全合乎自己要求的物联网设备,这时候我发现了 Arduino 这个开源电子原型平台。它本质上是一个单片机,有丰富的针脚接口用于连接各类传感器、伺服器和继电器等等。在电脑上用 Arduino IDE 编写代码后,可以很方便地写入到微控制器上执行。更重要的是由于 Arduino 的软硬件都是完全开源的,让我能以很低的成本获取所需的软件和硬件。

Arduino 有很多种版本,加之以开源的 PCB 图为基础自行生产的第三方产品可谓数不胜数,我选择的是一个可以和我的第三代 Raspberry Pi(树莓派)结合使用的版本——因为 Arduino 本身只是个单片机,并不能像树莓派之类基于 ARM 架构的微型电脑一样连接网络和存储大量数据。虽然市面上 Arduino 也有能实现相关功能的硬件模块,但我依然认为搭配熟悉的树莓派更简单好用。

这个 Arduino 通过串口与树莓派通讯,同时封装了树莓派上的所有 GPIO 针脚,所以需要将它用 USB 线连接到树莓派,然后再将整个 Arduino 都插在它上面。

接入传感器

市面上有非常多的传感器可供选择,且售价大多不过二三十元。Arduino 支持接入模拟和数字两种信号的传感器,我第一个接入的温湿度传感器 DHT11 属于后者,所以要用杜邦线将它插到数字针脚上。

插好之后就可以开始写代码了。Arduino 主要用 C++ 来编程,这是一种我从未接触过的编程语言,还好我学过 Objective-C,所以还算是能读懂;配合万能的 Google、完善的官方文档和传感器厂商提供的实例代码,在开发过程中基本没有遇到什么困难。

虽然厂商提供了已经封装好的代码库,只需调用即可直接读取到传感器数据,不过我对从硬件电路到软件数据的传输和转换过程很感兴趣,于是一探究竟,才知道 Arduino 通过数字接口读取到的是 DHT11 传感器在一段时间内通过电压变化来传输的二进制数值:

按照官方数据表的说明,高电平输出 26 微秒左右表示 0,输出 70 微秒则表示 1,用逻辑分析器即可看到比较直观的效果,这里我偷个懒在网上找了一张已经标注好的图:

这里可以看到传感器总共输出了 40 位的数据,其中前 16 位是湿度,紧接着的 16 位是温度,最后 8 位则用于校验数据有效性,若为温湿度数值之和即为有效;温湿度的 16 位数据中只有前 8 位是有效数据,后 8 位是奇偶校验位,这里全部为 0,可直接忽略。

按照上述规则来解析,图片中的二进制湿度数据为 00011110,温度为 00011001,将它们转换成十进制即可得到最终结果:30% 相对湿度和 25 摄氏度。再计算一下 00011110 + 00011001 = 00110111,即最后 8 个奇偶校验位的数值,证明数据是有效的。

当然实际使用时并不需要自己计算这些,只要引入官方提供的代码库,简单调用一下即可获取传感器数值并通过串口输出,非常方便。

这里要吐槽一下 Arduino IDE 的代码编辑器,功能简陋到基本就是个带语法高亮的记事本,然而它的流畅性和视觉效果甚至还不如记事本……让我不得不在 Sublime Text 里写代码再复制过来编译。

接下来试试光线传感器,它传输的是模拟信号,所以要插在模拟接口的针脚上。软件方面就简单多了,调用 Arduino 内置的 analogRead() 方法即可获取亮度数据。

将数据传送到树莓派

现在已经可以用 Arduino 读取传感器的数据,接下来就要将数据传送到树莓派来做进一步处理。实际上之前所写的代码已经可以让 Arduino 把数据通过串口输出到树莓派上了,所以真正需要做的只是在树莓派上写个程序来读取串口输入的数据,我是用 Python 写的,只需六行代码。

测试成功后我又在 Arduino 上接入了六七种传感器,读取数据的方法都大同小异,这里就不再展开;不过随着数据量的增加,需要对数据进行封装才好解析。这里我选用了 ArduinoJson 库将数据转换为 JSON 格式输出,这样在树莓派上用 Python 读取就方便多了。

在树莓派上用 Python 读取到传感器数据之后,就可以自己写个程序通过我前一篇文章提到过的 HomeAssistant API 来自动控制家里的其他电器了。至此我的智能物联网终端已经初步完成,当然我还会继续研究如何实现更多新奇有趣的用法,包括如何进一步脱离网络传输,完全在本地直接控制电器等等,相信没有做不到,只有想不到。在学习 Arduino 传感器的过程中我还顺便了解到了很多关于电机、伺服器、继电器、ZigBee 通讯协议和无线充电的相关知识,让我很感兴趣,说不定哪天我会一时兴起,给它装上轮子和机械手臂,做成 AI 机器人管家之类的东西,哈哈。

❌