对智能手表进行逆向工程
视频教程在我主页简介或专栏里
目录:
初步观察
PCB 的逆向工程
引入 Ghidra
重新编程智能手表
前段时间,我收到一批具备地理定位功能的智能手表,这些手表在试用期结束后被搁置。我决心为它们找到新的用途,于是开始了我的智能手表逆向工程之旅!
在本文中,我将分享智能手表逆向工程的过程,首先展示一些关于手表外观和电路的初步观察,然后详细讲述如何重新编程智能手表,最后说明如何修补固件以实现其重新利用。
初步观察
这些手表以最基本的配置交付,包装内仅附有一页关于如何充电和使用的说明。每个盒子内包含一个充电器和一块手表。没有提供 README 文件、官方网站或开发者门户。手表本身仅在表面配备了一个电容传感器,用于开启屏幕,用户可通过屏幕查看心率、一些调试信息以及不同表盘样式下的时间。
调试信息为识别手表提供了有用的线索。我注意到,不同相同型号的手表上显示的调试界面中,IP 地址是相同的。我的初步猜测是,这个 IP 地址指的是这些手表所连接的服务器的地址。
这款手表具有 IP67 级防水性能,也就是说,它是防水的。因此,使用我有限的工具,在不破坏手表的情况下几乎无法进入内部。本着探索的精神,我拿了一把钳子对其中一块手表进行了拆解,试图弄清楚内部构造。这并不容易,但经过一个小时的小心撬动和切割,我终于成功拆除了手表的外壳。
内部是一块单一的 PCB 板以及一个锂聚合物电池(LIPO)。机壳内嵌有两个天线,通过 u.FL 连接器与电路板连接。
PCB 的逆向工程
这款智能手表的核心是一个支持蓝牙的 nRF52832 芯片,同时还有两个主要的集成电路:支持 WiFi 的 ESP8285 和来自 SIMCOM 的蜂窝通信芯片。一开始我对 WiFi 微控制器的存在感到疑惑,因为手表没有任何支持 WiFi 功能的明显迹象。然而,我后来发现它被用于城市环境中的定位辅助。由于 GPS 在城市环境中的精度通常较差,手表可以通过结合 WiFi 接入点(AP)、蜂窝数据和历史 GPS 数据来近似确定位置。
从布局上看,nRF52832 是设备的主控芯片,并使用 WiFi 芯片扫描本地的 WiFi 接入点(AP)。nRF52832 还通过 UART 与 SIMCOM 设备通信,并发出命令以接入移动网络。基于这一发现,我将精力集中在寻找 nRF52832 上的任何 UART 或暴露的编程引脚上,因为这些引脚通常用于与微控制器进行交互。
在这种情况下,电路板上暴露了相当多的 UART 和编程引脚。我发现一个非常有趣的迹象是,PCB 右侧的圆形金色触点除了用于为手表电池充电外,还与 nRF52832 芯片上的 SWDIO 和 SWCLK 引脚相连。SWDIO 和 SWCLK 是 JTAG 编程引脚,通常用于对芯片进行编程。此外,在手表前盖的底部有弹簧加载的电接触探针(pogo pin),当手表关闭时,这些探针会与这些触点接触。
这些 pogo pin 与手表正面铜镀层触点相连,这意味着主芯片的编程引脚暴露在设备表面。暴露编程引脚并不常见,因为通常设备的固件会在工厂中被写入 PCB,并随后通过 WiFi 或蓝牙进行更新。一般来说,没有必要暴露编程引脚。然而,这个特点在后续操作中非常有用,因为这意味着我不需要打开手表就可以访问固件。而如果必须打开手表,我的重用计划几乎就没有意义了——正如我之前尝试打开手表时所表现的那样:这可不是一件能很快重新组装好的事情。
更有趣的是,在充电适配器上,与手表正面 SWCLK 和 SWDIO 接触点相连的 pogo pin 竟然连接到了 microUSB 接口上的 D+ 和 D- 引脚。D+ 和 D- 引脚通常用于数据传输。这意味着我甚至不需要制作定制的编程平台来重新编程手表:只需要将一个 microUSB 电缆剪开,暴露出编程引脚,然后将其连接到充电器上的手表即可。非常棒。
通过充电器将手表连接到我的 JLink 调试器后,我首先注意到手表正在通过 JLink RTT 查看器输出调试信息。成功了!虽然能够观察到调试输出非常有用,但由于 RTT 模块没有配置输入,因此无法向手表发送命令。不过,这些输出验证了我之前关于手表内部连接方式的假设是正确的。
在通过 JLink 尝试发送命令进行一些探索性操作后,我决定查看固件。通过连接 JLink,我能够使用 nrfjprog
工具并结合 --readcode
和 --readram
参数来转储固件。
固件没有启用读写保护,因此我成功获取了完整的固件转储。此时,我有两个选择来重新利用这款手表:1)可以完全重新编程,通过向设备中刷入新的固件实现,或者 2)对固件中的 IP 地址和端口进行补丁修改,使手表将数据发送到我控制的服务器。由于重新编程手表可能是一个庞大的工程,我决定尝试对手表进行补丁修改。
引入 Ghidra
为了在 Ghidra 中正确映射函数和变量,我需要转储 RAM 和闪存。这是一个来自 Cortex M0+ 设备的裸机代码,我必须将固件反编译为 ARM 小端格式,Ghidra 成功生成了半可读的伪代码。
此时我的目标是调查 IP 地址的位置,我推测它存储在代码中的某处。手表通过蜂窝网络将数据发送到一个服务器,该服务器的地址显示在手表的调试界面上。如果我能够更新这个 IP 地址,就可以将手表发送的数据重定向到我控制的服务器。
然而,我最初尝试查找与手表界面上显示的 IP 地址相符的硬编码字符串未成功。
接下来,我尝试查找 RTT 查看器中调试输出中出现的最接近的字符串。在这种情况下,字符串是 AT+CIPOPEN=0
,这是从 nRF52832 发送到 SIMCOM 模块的串行命令,指示其打开与指定为参数的 IP 地址的连接。通过找到这个字符串,因为它使用了手表必须连接的服务器的 IP 地址,我就可以定位存储服务器 IP 地址的内存位置。
我更成功地找到了一个格式化字符串。这个匹配项的字符串模式与 IP 地址的格式相符。它在一个看起来非常像 sprintf
函数的函数中被调用。
sprintf
函数中调用的字符串引用,调用了另外四个变量。
sprintf
函数引用了一个存储在 DAT_20000887
的数组,DAT_20000887
对应于 nRF52832 数据手册中的数据 RAM 区域,如下图所示的内存映射所示。
进入内存中的 RAM 位置,我发现它在两个函数中被写入。只有第一个位置是有趣的,因为它存储了硬编码的 IP 地址。第二个位置则允许稍后通过无线更新 IP 地址。
跳到第一个函数,我在内存中找到了这些硬编码变量的十六进制值,它们作为主函数的一部分被写入数组,这些变量对应了我在手表上观察到的 IP 地址。
这些内存位置是固件中需要修补的六个字节的数据,用于 IP 地址和端口。
出现的一个小问题是,原始端口号是38899,它以两个字节保存:0x0c 和 0x68。编译后的程序使用了一个 movn
指令,该指令对硬编码的值应用布尔“非”操作,然后再将其放入 RAM 中。从技术上讲,这个指令可以被修补,以去掉 not
操作,但由于我想减少更改字节的数量,因此我在将所需的端口号转换为相应的十六进制值时,添加了一个额外的操作。
重新编程智能手表
有了这些知识后,我能够编写一个简单的脚本,修补固件,替换为任何我想要的 IP 地址和端口。该脚本还更新了每行修补后的校验和,以确保固件与预期格式匹配。修补完成后,我使用 Nordic Semiconductor 的 Programmer 工具程序将固件闪存到手表上。
我很高兴地说,这个方法成功了!通过更新固件以与服务器通信,我们能够从手机获取数据并进行处理。
一个有趣的收获是,编程接口暴露在USB端口上,这在其他智能手表中并没有见过。固件没有任何读写保护也很不常见,因为大多数其他物联网设备在投入生产后会强制执行固件保护,以防止固件被轻易克隆。
视频教程在我主页简介或专栏里
申明:本账号所分享内容仅用于网络安全技术讨论,切勿用于违法途径,所有渗透都需获取授权,违者后果自行承担,与本号及作者无关