WoKough
- 关注
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9

电子产品日新月异,不知不觉手上已积了一堆设(po)备(lan),与其吃灰不如折腾一下,以研促学,可能会遇见以前没接触到奇奇怪怪的知识点。
本篇是”手上有啥就搞啥“系列的第一篇,研究对象是小米手环3,侧重点是Cortex-M固件和BLE协议分析。小米手环已将出到7了,有人会问为什么不研究最新的,因为我没(hen)有(qiong) -.-。下文在0x00中介绍基础知识,0x01简单梳理前人经验成果,0x02中阐述固件逆向和协议分析过程,实现手环 BLE Hack,最后在0x03做个总结。
0x00 基础知识
0x00 - 00 BLE (Bluetooth Low Energe)
蓝牙1.0使用BR技术,2.0使用EDR技术,区别在于通信技术层面有所改进。蓝牙4.0时引进了诺基亚开发的一种名为Wibree的无线技术,称为BLE。仅支持BR/EDR的被淘汰掉了,只支持BLE的称为单模,同时支持经典蓝牙BR/EDR和低功耗蓝牙BLE的称为双模蓝牙。蓝牙5.0是BLE针对IoT进行的改进版本,更省电。
经典蓝牙ER/EDR没被淘汰是因为速度比BLE快的多,特点是需要配对,BLE优点在省电,延迟低,特点是数据包短,广播频道少,可以开发不配对的应用iBeacon。
蓝牙通信原理和socket不同,因此底层收发数据的通信协议也有较大的差别。BLE主要通过Attribute的形式共享信息。ATT协议主要是定义了Attribute(属性)这个数据结构。一个attribute由三个元素组成:
- 16 bit的句柄(唯一性,用于区分和查找不同的attribute)
- UUID(ATT本身不定义,留给GATT来定义)
- 一个定长的值value(配合UUID使用,由GATT来决定这个UUID的意义和数据)这有点像网络分层模型。
0x00 - 01 ARM Cortex-M
ARM Cortex内核系列提供非常广泛的具有可扩展性的性能选项,设计人员有机会在多种选项中选择最适合自身应用的内核,而非千篇一律的采用同一方案。Cortex系列组合大体上分为三种类别:
● Cortex-A — 面向性能密集型系统的应用处理器内核
● Cortex-R — 面向实时应用的高性能内核
● Cortex-M — 面向各类嵌入式应用的微控制器内核
其中 Cortex-M架构被广泛应用于IoT产品,医疗电子产品,工业控制产品和基带系统,M系列不能跑操作系统(只能跑 ucos2 ),偏向于控制方面,由于其固件十分精简提供的额外信息较少,固件逻辑非线性,使得安全分析难度很高,M系使用的是ARM的Thumb指令集。
0x01 站在巨人的肩上
由于小米手环3比较古老,研究资料还是很丰富的,主要在硬件拆解、手环unbrick、资源文件提取和美化和BLE认证协议解析方面,但在固件逆向分析方面的资料几乎没有,下简略介绍前人成果。
0x01 - 00 硬件拆解
拆解可以参考eWisetech的6步拆解法,这里不作赘述。
需要的是芯片信息以供进一步研究:
红色:Dialog-DA14681-蓝牙
绿色:STMicroelectronics-加速度传感器
青色:Gigadevice -GD25LQ32 –Flash芯片
深紫:Maxim
蓝色:NXP-PN80T- NFC控制芯片
橙色:TI-TPS61046-电源控制芯片
紫色:Azoteq-IQS266-触摸控制芯片
深绿:SGMICRO-Flash芯片
棕色:ON Semi-ESD7383-电源保护芯片(3颗)
由于需要对固件和BLE协议分析,所以重点关注的信息是Flash芯片和蓝牙芯片的型号。
0x01 - 01 固件提取
- APK中提取
下载小米运动App(小米手环3时还叫这个名字),利用apktool可解包(具体过程略),需要的固件包如下所示:
分别是:
Mili_wuhan.fw - 固件
Mili_wuhan.res - 资源文件
- 硬件接口提取
可以试用 FT2232 芯片组的编程器(FT2232 编程器)读写GD25LQ32 Flash固件。
下图为电路图,GD25LQ32的电源电压为 1.8V,该存储器的 SPI 接口容差为 3.3V。对GD25LQ32进行读写时,必须断开频段3的3.8V的电压,否则处理器会阻止通过 SPI 接口进行通信。
工具下载: Windows(v1.0.1-rc2) / Ubuntu(v0.9.9+r1954-1)
Flashrom/ FT2232H SPI Flash programmerwindows 驱动: **Zadig libusb-win32,**Linux 需要安装 "libusb-win32" 。
- 读取 GD25LQ32 内存:
flashrom -p ft2232_spi:type=2232H,port=A -c GD25LQ32 -r MB3_FullFlash_brick.bin
"MB3_FullFlash_brick.bin" 为输出文件.
- 擦除 GD25LQ32 内存:
flashrom -p ft2232_spi:type=2232H,port=A -c GD25LQ32 -E
- 写入 GD25LQ32 内存:
flashrom -p ft2232_spi:type=2232H,port=A -c GD25LQ32 -w MB3_FullFlash_unbrick.bin
"MB3_FullFlash_unbrick.bin" 为输入文件.
当然也可以直接在 此网站下载提取好的固件资源。
0x01 - 02 资源文件修改
通过上文可知,Mili_wuhan.res是手环的资源文件,是标准的res格式,可使用**AmazfitBipTools**解析,该工具将res文件解压缩并转换为png,
替换资源后再重新打包并将其转换为res文件。
0x01 - 03 认证协议
从APP控制端出发,认证协议如下:
- 向认证 UUID (00000009-0000-3512-2118-0009af100700) Descriptor 写入 \x01\x08请求,即发送认证请求头;
- 发送16字节密钥KEY(自己设置),并加上头,即 \x01\x08 + KEY;
- 手环会回复随机数 \x02\x08 + Random(尾部 16 bytes),在Characteristic读取获得;
- 使用第二部中的16字节密钥KEY加密随机数,方式是AES/ECB/NoPadding,发送给手环
( \x03\x08 + encrypt data)。
可参考 auth.py代码,也可参见 GadgetBridge项目,该项目构建了一个免费的手环 Android 端管理APP,以替代供应商的闭源(且远程云同步数据)的 APP,支持Pebble、小米手环、Liveview和HPlus等。
下文也会通过固件逆向方式分析找出认证过程。
0x02 自己动手做
0x02 - 00 固件逆向
固件逆向需要先收集一些信息,简单流程如下图:
手环这样的小设备应该是没有文件系统的,先用binwalk看下信息:
binwalk没有识别出任何信息,指令和架构也无法识别。
直接拖入IDA pro,架构选择ARM(可通过Flash芯片判断),如下图所示:
同样没有识别出任何信息 -.- 。
这就是使用到了0x00中的基础知识和0x01中的硬件信息,通过拆解可知芯片是 Gigadevice -GD25LQ32 –Flash芯片,使用ARM Cortex-M架构,在分析时需要注意的是Corex-M使用Thumb指令集,所以IDA没有识别出来(后来使用Ghidra可以识别,还是过于相信IDA了),需要按ALT+g,把里面T的值改为0x1。
函数和字符串会部分识别,当然并不全,下面就需要找到基地址。
找基址的套路还是比较多的,根据Datasheet、头结构、表结构、字符串等等,最后通过交叉引用验证,这里我们可以:
- 通过Cortex-M标准结构
熟悉Cortex-M架构的话,很容易从这里面发现一些关键的东西。比如红框里这两个地址,上面的那个是SP指针的初始位置,下面这个是加载基址,CPU从这个位置开始运行。
所以猜测运行基址是 0x8004000,rebase之后发现交叉引用也识别出来了。
- 懒人新宠—爆破
可以使用 Basefind2爆破加载地址,这是一个 python 脚本,只有一个py文件,感兴趣的小伙伴可以研究下原理(从字符串出发),一个命令搞定,有很好的辅助作用。
至少在这里是比较好用的,有点惊喜。
接下来就是恢复函数、字符串等信息,并祈祷有符号,可以写个IDApython脚本,可以看到脚本运行后函数基本恢复,字符串也识别出来,有明显的表结构与引用对应。如下图所示:
但是祈祷果然没用,没有符号,也没有找到类似vxworks那样函数字符串表 -.-。
面对这样的情况一般有几种方法,无论什么方式都要注意,在逆向时一定要明确目的,一般并不是要恢复所有源码,如果目的不聚焦,可能进入无尽循环…
- 根据字符串信息恢复关键函数
- 根据SVD或中断向量表恢复
- 使用bindiff与包含符号的固件比较
因为这里主要关注BLE,从硬件信息得知,蓝牙使用Dialog-DA14681芯片,之前笔者制作过nRF的开发,所以对这个芯片的开发函数不熟,所以选择从字符串出发分析,有兴趣的同学可以编译DA固件再用bindiff比较。
首先BLE最重要的是UUID,也最好识别,小米手环3的UUID如下代码描述:
BASE = "0000%s-0000-1000-8000-00805f9b34fb"
SERVICE_MIBAND3 = BASE % 'fec1'
SERVICE_ALERT = BASE % '1802'
SERVICE_ALERT_NOTIFICATION = BASE % '1811'
SERVICE_HEART_RATE = BASE % '180d'
SERVICE_DEVICE_INFO = BASE % '180a'
CHARACTERISTIC_HZ = "00000002-0000-3512-2118-0009af100700"
CHARACTERISTIC_SENSOR = "00000001-0000-3512-2118-0009af100700"
CHARACTERISTIC_AUTH = "00000009-0000-3512-2118-0009af100700"
CHARACTERISTIC_HEART_RATE_MEASURE = "00002a37-0000-1000-8000-00805f9b34fb"
CHARACTERISTIC_HEART_RATE_CONTROL = "00002a39-0000-1000-8000-00805f9b34fb"
CHARACTERISTIC_ALERT = "00002a06-0000-1000-8000-00805f9b34fb"
CHARACTERISTIC_CUSTOM_ALERT = "00002a46-0000-1000-8000-00805f9b34fb"
CHARACTERISTIC_BATTERY = "00000006-0000-3512-2118-0009af100700"
CHARACTERISTIC_STEPS = "00000007-0000-3512-2118-0009af100700"
CHARACTERISTIC_LE_PARAMS = BASE % "FF09"
CHARACTERISTIC_REVISION = 0x2a28
CHARACTERISTIC_SERIAL = 0x2a25
CHARACTERISTIC_HRDW_REVISION = 0x2a27
CHARACTERISTIC_CONFIGURATION = "00000003-0000-3512-2118-0009af100700"
CHARACTERISTIC_DEVICEEVENT = "00000010-0000-3512-2118-0009af100700"
CHARACTERISTIC_CURRENT_TIME = BASE % '2A2B'
CHARACTERISTIC_AGE = BASE % '2A80'
CHARACTERISTIC_USER_SETTINGS = "00000008-0000-3512-2118-0009af100700"
NOTIFICATION_DESCRIPTOR = 0x2902
# Device Firmware Update
SERVICE_DFU_FIRMWARE = "00001530-0000-3512-2118-0009af100700"
CHARACTERISTIC_DFU_FIRMWARE = "00001531-0000-3512-2118-0009af100700"
CHARACTERISTIC_DFU_FIRMWARE_WRITE = "00001532-0000-3512-2118-0009af100700"
先搜索认证的UUID : 00000009-0000-3512-2118-0009af100700
通过字符串的交叉引用很容易就定位认证的位置,可以通过参数个数,参数类型等比较猜测具体函数,先来看DA蓝牙的一些常用函数:
bool ble_uuid_from_string(const char *str, att_uuid_t *uuid)
描述:UUID字符形式转存到字符数组中
输入:*Str:字符形式UUID
*uuid:最终存放uuid的数组
返回:Bool类型
ble_error_t ble_gatts_add_service(const att_uuid_t *uuid, const gatt_service_t type,uint16_t num_attrs)
描述:添加服务
输入:*uuid:该服务的UUID
Type:主服务 or 次要服务
Num_attrs:属性数量
返回:ble_error_t
ble_error_t ble_gatts_add_characteristic(const att_uuid_t *uuid, gatt_prop_t prop, att_perm_t perm,uint16_t max_len, gatts_flag_t flags, uint16_t *h_offset,uint16_t *h_val_offset)
描述:添加特征值属性
输入:*uuid:该特征值的UUID
gatt_prop_t prop:读、写、通知等属性
att_perm_t perm:ATT attribute permission
gatts_flag_t flags:GATT Server flags
uint16_t max_len:数据长度
uint16_t *h_offset:NULL
uint16_t *h_val_offset:custom rx characteristic handle
返回:ble_error_t
ble_error_t ble_gatts_add_descriptor(const att_uuid_t *uuid, att_perm_t perm, uint16_t max_len, gatts_flag_t flags, uint16_t *h_offset)
描述:添加特征值属性
输入:*uuid:该特征值的UUID
att_perm_t perm:ATT attribute permission
gatts_flag_t flags:0
uint16_t max_len:数据长度
uint16_t * h_offset:custom descripte handle
返回:ble_error_t
根据函数功能、参数可恢复一些关键符号如下图所示:
可以看到有些地址在加载基址之前,需要进一步研究,但并不影响认证过程的大致分析。还可以看到,红框中第二个UUID正对应小米手环3的 service UUID。通过此种方法还可以恢复一些其他BLE读写操作,分析都十分简单,这里不再赘述。
0x02 - 01 BLE Hack
Hack 的目的很简单,就是通过BLE接管手环,并显示预设的文字 (但当我研究到一半才发现小米手环3 没有显示文字信息这个功能 -.-)。
- 环境准备
手机端测试的APP可使用 nrf connect,功能还是比较强大的。
Andriod 协议抓包的工具也很多,现在root困难,都流行代理式(实际就是中间人),在这里BLE协议抓包可使用 Andriod 的自身机制,十分简便。即打开蓝牙log和USB调试功能:
等与手环交互完毕,日志就存在 /sdcard/btsnoop_hci.log 中,可以直接试用Wireshark打开分析:
- BLE 劫持
本来意图是在前人研究基础上做个信息显示,后来发现手环3没有这个功能,硬要改也只是改个显示时间,所以最多算做了个复现。可以看到前人的 研究工具 已经比较全面了:
笔者试用的是Attifyos3.0 (ubuntu 18.04)虚拟机环境,python3.7, 复现时需对工具作多处修改,都是语法类的错误,与BLE协议本身实现无关,简介下步骤,也算是比较傻瓜了,感兴趣的小伙伴可以一同复现:
1、sudo hcitool lescan扫描手环MAC地址
2、利用 python3 main.py DF:DE:80:B7:1A:3B劫持BLE
简单试了下获取心率和通知功能(要修改部分代码),由于 资料描述已经很详细了,这里再描述充其量也就算个翻译,所以不再赘述。
0x03 做个总结
本篇是针对小米手环3作了一些BLE协议和固件逆向方面的基础研究。固件方面虽然去除了符号,一定程度提高分析门槛,但没有实质的保护措施;BLE协议方面还是存在一些缺陷的,比较容易遭到攻击,好在连接有物理确认。当然,小米手环已经出到7代,各方面安全性应该有所提升,等有钱买再研究吧,下个设(po)备(lan)见。
0x04 参考资料
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)


