对 M6 智能健身手环进行逆向工程(上)
2021-08-02
来源:嘶吼专业版
概述
1、了解它的硬件
2、弄清楚如何与它交谈
3、转储其库存固件
4、让它运行自定义代码,最好利用它:
GPIO 引脚(用于输入和输出)
彩色显示
蓝牙低功耗 (BLE) 功能
硬件介绍
拆开塑料外壳,我们看到了一些有趣的东西:
Telink TLSR8232芯片系统(SoC)
0.96 英寸(160x80 像素)彩色显示屏
一个约 100 mAh 的微型锂电池和 USB 充电电路
振动电机
一个(最有可能的)假心率传感器
核心组件
印刷电路板全图
M6中的SoC是Telink TLSR8232(数据表),规格如下:
32位中央处理器:封闭式架构(通常称为tc32,类似于ARM9,关于它的介绍并不多)和24 MHz 时钟速度;
16kB 的 SRAM;
512kB 内部闪存;
用于低功耗模式的 32kHz 板载振荡器;
用于调试和编程的 SWS(Single Wire Slave)接口;
集成蓝牙低功耗 (BLE) 收发器;
低功耗运行(据称在深度睡眠中约为 2 uA);
幸运的是,就在几个月前,研究人员在其破解的小米温度计中看到了Telink芯片。此时,我用@atc1441 的替代固件重新刷了它。尽管它是不同的 SoC 模型,但这给了我一点希望和一个有价值的起点。
焊盘
焊盘 (Exposed-Pad),有些封装具有裸露焊盘,用来改善器件散热的焊盘。通常为非电气绝缘,可根据电连接要求将其接地或电源。
印刷电路板底部
印刷电路板底部
除了万用表、数据表,我什么也没有,我试图找到这些焊盘的连接位置。这就是我想出的:
单线(又名 SWire 或 SWS)接口
现在我们确定了手环的核心部件,如果你之前对 ESP32 进行过编程,你可能会依赖其引导加载程序并通过 UART 与它通信。如果你之前编程或调试过 ARM 微控制器,你可能使用了 SWD(串行线调试)协议。
在Telink-land 中,类似的接口称为Single Wire 或SWire。这就是应用程序如何加载到其闪存中、如何读取和写入内存以及如何在运行时调试的方式。
然而,当我们尝试了解有关此界面的更多信息时,真正的挑战才刚刚开始。在现实世界中,这些芯片是使用Telink官方的调试工具进行调试的。
研究人员在找到存储库 TlsrTools 时,其中有对 SWire 协议的两页描述。这似乎是一个旧版本的Telink数据表的一部分,已经被删除。
从 Pascal 到 Python
在编程/调试芯片时,通常有三个活动部分:
我们要编程的目标板
程序员硬件
与程序员通信的计算机软件
计算机软件的作用是向程序员硬件发送命令,并使其从目标板读写数据。由于研究人员手边没有Windows设备,所以我实现了一个基本的Python脚本,即 tlsr82-debugger-client.py,它作为计算机软件组件工作。
我们现在可以使用这个 Python 脚本和 STM32 来解密 M6 手环中隐藏的信息。设置如下:
基于 STM32 的替代编程设置
SWire 规范
顾名思义,单线用于在两个设备之间来回传输数据。在我们的例子中,STM32 编程器(主)和目标板(附属)。没有像 SPI 或 I2C 那样单独的时钟线。单线拓扑允许两个设备通信,但它们不能同时通信。换句话说,我们可以将 SWire 称为异步、半双工接口。
这意味着:
异步:由于没有共享时钟,两个设备必须以某种方式采用兼容的读写速度;
半双工:每个设备必须知道什么时候应该监听消息,什么时候允许传输消息。
为了实现协调,SWire 协议将责任归属于主盘和附属设备。主盘负责发起通信并管理数据传输之间的总线逻辑层。从盘负责在预期的时间发送数据。下面我将一些真实世界的例子放在一起,以便更清楚地说明这一点。
发送单个位
首先要注意的是位是如何在线路中编码的。每个位以五个时间单位传输:
要发送 0,请保持 1 个单元的低电压和 4 个单元的高电压;
要发送 1,保持低电压 4 个单位时间和高电压 1 个单位;
具体来说,这是我用逻辑分析仪捕获的真实 SWire 传输:
SWire 中 0 和 1 的示例
在上面的截图中,标记为 25 和 26 的标志之间有 8 位正在传输,但是很难解码。
发送单个字节
我们现在知道为了传输一个完整的字节,SWire 协议规定需要 9 位:
位 1:cmd 位。0 指定消息包含数据,1 指定消息是命令;
位 2-8:消息内容(8 位);
低级别的一个时间单位表示消息结束;
另外,让我们看一下 0xb0 字节的真实示例传输:
在 SWire 中发送一个字节的示例
发送最后一个低级别后,总线被释放并恢复到其自然高电压。换句话说,SWire 数据总线被拉高。
写入请求
如上所述,单个位和字节是如何在线路中编码的。接下来,我们来看看SWire协议是如何指定要写入特定地址的字节的。在该示例中,主盘希望将一个字节b写入从盘内存中的地址addr。
为此,主盘必须发送一个字节序列,每个字节都按照上一节所述进行编码:
START字节,这个值总是 0x5a;
目标addr的最高有效8位;
目标addr的最低有效8位;
RW_ID字节,最重要的位应该设置为0,用于写入操作;
字节值 b;
END字节,它的值始终为 0xff;
让我们看看下面的例子:
在 SWire 中写入数据的示例
在这个例子中,我们可以看到字节 0x05 被写入从盘的内存地址 0x0602。
SWire 协议的变体
值得注意的是,至少存在一个SWire协议的变体。在另一种变体中,主盘在 START 字节之后发送 3 个字节的 addr,而不是在我们的 SWire 协议中仅发送两个字节。例如,Telink 的 TLSR8251 SoC 中采用了 3 字节变体,用于我们上面提到的小米温度计。在 ATC_MiThermometer 存储库中基于 Python 的闪光器中,我们可以看到在从主盘到从盘的读/写请求中指定了 3 个字节的 addr。
写入多个字节
这是写入单个字节要花费的能力,幸运的是,该协议让我们一次写入多个数据字节。为此,主盘只需发送一个字节序列,而不是像上面示例中的单个字节。