前不久,我被分配了一批带有地理定位功能的智能手表,这些手表在一次试用后被闲置了起来。我决定给它们找点用处,于是开始了逆向工程智能手表的旅程。
在这篇文章中,我将分享逆向工程的过程,首先强调对手表表面和电路的一些初步观察,接着详细介绍我如何重新编程智能手表的部分,以及如何修补固件以重新使用它的最终步骤。
初步观察交付的手表非常简单,仅附有一页操作说明,说明如何充电和使用。每个盒子只有一块手表和一个充电器。没有任何README文档、网站或开发门户。手表本身只有一个触控传感器,可以点亮屏幕并查看心率、调试信息和不同表盘上的时间。
表盘样式
调试信息提供了识别手表所需的有用信息。我发现同一型号的手表的调试表盘上显示的IP地址是一样的。我最初的猜测是,它应该是这些手表用来通信的服务器的IP地址。
给手表充电
这块手表具有IP67的防水等级,也就是说它是防水的,所以我无法不损坏手表就打开它。出于探索的精神,我用了一把钳子拆解了一块手表,拆解它来确定其内部构造。这并不容易,但经过一个小时的仔细撬和切,我终于拆下了手表的外壳。
拆开设备
看看里面
里面只有一块PCB,带有一块LIPO电池。外壳里有两个天线,分别连接到板上的u.FL连接器。
拆解PCB这款智能手表由支持蓝牙的nRF52832芯片驱动,并且还有两个主要的集成电路:一个是支持WiFi的ESP8285,另一个是来自SIMCOM的蜂窝模块。最初我对WiFi控制器的存在感到困惑,因为没有迹象表明手表具备WiFi功能。然而,我后来发现它是用于城市三角定位的。由于GPS在城市环境中的精度一般较差,结合WiFi接入点和蜂窝数据与历史GPS数据,可以大致确定手表的位置。从布局来看,nRF52832是设备的主要集成电路,并使用WiFi芯片扫描附近的WiFi接入点(AP)。nRF52832还通过UART与SIMCOM模块通信,并发出命令以与移动网络通信。因此,我专注于寻找nRF52832上的任何UART或编程引脚,因为它是主要集成电路,这些连接通常用于与微控制器交互。
在这块板子上,可以看到不少暴露在外的UART和编程引脚。我发现一个非常有趣的线路是,PCB右侧圆形的金色触点除了连接到手表电池充电所需的引脚外,还连接到nRF52832 IC上的SWDIO和SWCLK引脚。它们是SWDIO和SWCLK,这两个引脚是用于芯片编程的JTAG接口。手表正面盖子的底部也装有弹簧触点(pogo pins),这些触点在手表闭合时会接触到这些焊盘。
这些弹簧销暴露在表盘正面的镀铜触点上,这意味着主芯片的编程针也暴露在外。暴露编程针并不常见,因为通常设备的固件会在工厂烧录到PCB上,并且之后可以使用WiFi或蓝牙更新。通常情况下,不需要暴露编程针。这个特性后来证明非常有用,因为它意味着这样我就能在不打开手表的情况下访问固件。如果需要打开手表,我重新利用这块手表的计划就会落空,就像我之前尝试打开手表一样,这并不是我近期打算重新组装的手表。
更有趣的是,在充电适配器上,连接到手表表面SWCLK和SWDIO接点的弹簧针被直接连接到了microUSB插口的D+和D-针脚上。D+和D-针脚一般用于数据传输。这意味着我不需要构建一个自定义的编程平台来重新编程手表:我只需将microUSB线缆剪开,暴露出编程针脚,然后连接到任何通过充电器连接的手表上。真是太方便了。
联系人
接上电缆
我通过充电器将手表连接到我的JLink调试器之后,首先注意到的是手表通过JLink RTT查看器产生了调试输出。成功了!虽然能看到调试输出非常有用,但由于RTT模块没有配置输入,所以无法向手表发送指令。不过,这些输出信息确认了我之前对手表内部连接方式的猜想。
从nRF52832 IC发送至SIMCOM模块的UART数据
在通过JLink尝试了几种发送命令的试探性操作后,于是我决定研究一下固件。在连接好JLink后,我能够通过使用nrfjprog
命令和--readcode
及--readram
标志导出固件。
原始固件备份
固件没有被设置为读写保护,所以我能够获取完整的固件备份。此时,重新利用这块手表有两种方式:1)我可以完全重新编程它,通过向设备刷入新的固件,或者2)我可以修改固件中的IP地址和端口,使手表的数据发送到我自己的服务器。由于完全重新编程手表可能非常复杂,我决定尝试修改固件。
启动Ghidra我需要同时卸载RAM和闪存的内容,以便,在Ghidra中得到函数和变量的正确映射。由于这是来自Cortex M0+设备的裸机程序,我需要将固件代码反编译为ARM小端格式,Ghidra能够生成基本可读的伪代码。
我的目标是在代码中找到IP地址的位置,我相信它应该存储在代码的某个地方。手表通过蜂窝网络将数据发送到一个服务器上,该服务器的地址在手表的调试界面上显示。如果我能更改IP地址,就能将手表发送的数据转移到我控制的服务器上。
不过,我最初尝试找到一个与我在手表上看到的IP地址匹配的硬编码的字符串,却未成功。
我的下一个尝试是找到在RTT查看器中观察到的调试输出中最接近的字符串片段。在这个情况下,这个命令是指由nRF52832发送给SIMCOM模块的串行命令AT+CIPOPEN=0
,该命令指示模块打开与指定IP地址的连接。通过找到这个字符串,因为它涉及手表需要连接的服务器的IP地址,我就能确定存储服务器IP地址的内存位置。
在内存中找到了字符串
我更成功地找到了一个格式字符串,它看起来像一个IP地址的格式,并且在一个疑似sprintf
函数的函数中被调用。
引用的字符串在 sprintf
函数中被引用,该函数还引用了四个其他变量。
冲刺功能部分引用了存储在DAT_20000887
中的数组,这与nRF52832数据表中的数据RAM部分相对应,如下面的内存映射所示。
nRF52832内存布局图
在内存中查看RAM的位置,我发现有两个函数在该位置写入数据。第一个位置引起了我的注意,因为它存放了硬编码的IP地址。第二个位置则允许通过无线方式更新IP地址。
跳到第一个函数后,我发现了这些硬编码的十六进制值,这些值被写入到数组中,作为主函数的一部分,而这些变量对应的正是我在手表上观察到的IP地址。
内存中用于打补丁的位置
我需要修改的固件中的六个字节是用于IP地址和端口的位置。
6字节用于补丁,4字节IP,2字节端口。
出现的一个小问题是这样的,原来的端口号是38899,但是它被保存为两个字节:0x0c和0x68。编译后的程序使用了movn
指令,在硬编码值放入RAM之前对其执行了布尔not
操作。但实际上,可以修补该指令来移除not
操作,但由于我尽量减少需要改动的字节数,因此在将所需的端口号转换为相应的十六进制值时,我添加了一个额外的操作。
有了这些知识,我能够编写一个简单的脚本,用任何IP地址和端口来修补固件中的内容。该脚本还会更新每个修补行的校验和,来确保每个修补行的校验和与固件的预期格式相匹配。一旦修补完成,我使用Nordic Semiconductor的编程实用程序将固件刷入手表。
固件更新脚本
固件更新
我很高兴地说这成功了!真是太好了!通过更新固件来与服务器通信,我们成功地从手机中获取并处理了数据。
更新固件
逆向工程部分至此结束。虽然我对逆向工程ARM设备起初了解不多,但我对设备的开发方式和功能已有所了解,这大大帮助了我明确目标。
一个有趣的发现是编程接口是如何通过USB端口暴露的,这在其他手表上是未曾见过的。此外,固件缺少读写保护也相当少见,因为大多数其他物联网设备一旦投入生产就会强制执行固件保护,以防止固件被轻易克隆。
然而,这真的很有趣,能够再次启用和使用那些原本可能被长期存放在仓库里或直接丢弃的电子产品,为这个项目增添了更多的意义。
共同学习,写下你的评论
评论加载中...
作者其他优质文章