首页
社区
课程
招聘
[原创]How to fxxk 华强北手表
发表于: 6天前 1484

[原创]How to fxxk 华强北手表

6天前
1484

某个周末,找发小去玩,一眼看到他手腕上的苹果表,看起来比我的牛逼,遂抢过来玩玩。

图片描述

不得不说,还挺流畅,表盘也比我的好看(苹果你该死啊!!!)。

突然想起来,手里还有传奇的nRF52832 Dongle。

link start。

关于低功耗蓝牙嗅探和渗透,参考这篇文章:https://p1yang.github.io/article/94a01627.html#%E5%97%85%E6%8E%A2

感谢yichen115师傅的启发:https://bbs.kanxue.com/thread-283915.htm

基本信息

mac地址:4d:23:a0:c1:00:ec
图片描述

通过广播包,可以看到设备名称叫H14 Ultar+。

app叫lewear,长得还挺像样子。

图片描述

绑定

通过app绑定和使用三方调试助手对比,发现并没有做加密或认证绑定操作,所以可以直接绕过绑定分析和加密分析。(世界安全能力降低1w倍之全世界没有加密)

不过华强北嘛,哪有安全,能用就行。

图片描述

图片描述

重放尝试

这里先尝试微信消息显示。

图片描述

图片描述

包是这样的,数据为72030101703179616e673a54657374a1cf71a9,大概可以看出来07030101为消息标识,70到a1为消息体,最后一段还不确定,但是不影响重放。

这里没截到,是对UUID为6е400002b5a3f393e0a9e50e24dcca9e的特征(Characteristics)进行的数据交换。

并且发现几乎所有的数据交换都是通过该特征进行的,所以大胆推测下07的代表的功能就是消息推送。

后续再分析。

图片描述

后边测试重放是可以的,甚至可以通过重放完成拒绝服务攻击。

具体攻击:

https://www.bilibili.com/video/BV11nzbY8ESx/

逆向app

在通过查找uuid关键字,能发现CommandHanle,即所有写入都调用这个文件下的方法

所以功能基本都调用UUID_READ 和UUID_WRITE,通过不同的标识区分不同的功能。

图片描述

1
2
3
4
5
6
7
8
9
10
11
12
13
6e40fff0-b5a3-f393-e0a9-e50e24dcca9e UUID_SERVICE
6e400003-b5a3-f393-e0a9-e50e24dcca9e UUID_READ
6e400002-b5a3-f393-e0a9-e50e24dcca9e UUID_WRITE
00002902-0000-1000-8000-00805f9b34fb GATT_NOTIFY_CONFIG
0000180A-0000-1000-8000-00805F9B34FB SERVICE_DEVICE_INFO 服务设备信息
00002A26-0000-1000-8000-00805F9B34FB CHAR_FIRMWARE_REVISION 固件版本
00002A27-0000-1000-8000-00805F9B34FB CHAR_HW_REVISION 硬件版本
00002A28-0000-1000-8000-00805F9B34FB CHAR_SOFTWARE_REVISION 软件版本
de5bf728-d711-4e47-af26-65e3012a5dc7 SERIAL_PORT_SERVICE 串口服务
de5bf729-d711-4e47-af26-65e3012a5dc7 SERIAL_PORT_CHARACTER_NOTIFY 串口字符通知
de5bf72a-d711-4e47-af26-65e3012a5dc7 SERIAL_PORT_CHARACTER_WRITE 串口字符写入
00003802-0000-1000-8000-00805f9b34fb PAY_MAIN_SERVICE_UUID 支付主服务UUID
00004a02-0000-1000-8000-00805f9b34fb PAY_BASIC_WRITE_NOTIFY_UUID 支付基本写入通知UUID

图片描述

通过代码调用可知,CommandHandle为蓝牙交互的基本方法,所都的调用类型都在这里。

其参数baseReqCmd.getData()就是整个功能编码成蓝牙交互数据的关键。

图片描述

这里通过查找手表功能来理解下。

数据包:

图片描述

findWatch

图片描述

图片描述

所有发包功能继承实现了baseReqCmd。

所以理清baseReqCmd的逻辑,然后找到功能标识参数,即可伪造发送功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public abstract class BaseReqCmd {
    protected static final String TAG = "Jxr35"; //日志
    protected byte key;
    protected int type;
 
    protected abstract byte[] getSubData();
 
    public BaseReqCmd(byte b) {
        this.key = b;
    }
 
    public byte[] getData() {
        byte[] bArr = new byte[Constants.CMD_DATA_LENGTH];  //固定数值发包长度为16个字符
        bArr[0] = this.key; //功能对应键,在findwatch中对应80,即0x50
        byte[] subData = getSubData();
        if (subData != null) {
            System.arraycopy(subData, 0, bArr, 1, subData.length); //将subData的内容放入
        }
        addCRC(bArr); //添加crc校验码,这里的crc并不是一个标准的CRC,单纯起到一个简单的错误检测。
        return bArr;
    }
 
    private void addCRC(byte[] bArr) {
        int i = 0;
        for (int i2 = 0; i2 < bArr.length - 1; i2++) {
            i += bArr[i2];
        }
        bArr[bArr.length - 1] = (byte) (i & 255);
    }
}

所以大致思路就是传入key+内容+CRC校验码共16位。

通过该思路,可以反推一个数据包中所代表的功能。

这里我用python推了一个编码脚本。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class Constants:
    CMD_DATA_LENGTH = 16
 
 
class BaseReqCmd:
    TAG = "Jxr35"
   
    def __init__(self, key: int):
        self.key = key
        self.type = 0 
   
    def getSubData(self) -> bytes:
        raise NotImplementedError("This method should be overridden by subclasses")
   
    def getData(self) -> bytes:
        bArr = bytearray(Constants.CMD_DATA_LENGTH)
        bArr[0] = self.key
        subData = self.getSubData()
        if subData:
            bArr[1:1 + len(subData)] = subData
        self.addCRC(bArr)
        return bytes(bArr)
   
    def addCRC(self, bArr: bytearray):
        crc = sum(bArr[:-1]) & 0xFF
        bArr[-1] = crc
 
 
class FindDeviceReq(BaseReqCmd):
    def __init__(self):
        super().__init__(80)
   
    def getSubData(self) -> bytes:
        return bytes([85, -86&0xff])
 
 
if __name__ == "__main__":
    req = FindDeviceReq()
    data = req.getData()
    print(f"Generated data: {data}")
    print(f"Hex representation: {[hex(b) for b in data]}")

伪造消息

这里重点关注了消息推送服务,尝试能否解析伪造。

通过对多次发消息的对比和代码逆向。

图片描述

大致思路就是,将一段长消息分成每段11个字节的短消息。

整个数据包为:key:75, app: 01-xx, 消息段数, 消息段, 消息, 校验码。

根据这个思路写的脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import sys
 
def addCRC(list):
    checkNum = sum(list[:-1])&0xff
    k = 15 - len(list)
    for i in range(0,k):
        list.append(0)
    list.append(checkNum)
    return list
def string_to_unicode_and_ascii(input_str):
    result = []
    for char in input_str:
        if char.isascii():
            # 如果字符是 ASCII 字符,则直接添加
            result.append(ord(char))
        else:
            # 否则,将字符转换为 Unicode 编码
            s = char.encode('utf-8')
            for i in s:
                result.append(i)
    return result
 
def format_list_to_hex_string(lst):
    return ''.join(f"{item:02x}" for item in lst)
 
if __name__ == '__main__':
    input_str = sys.argv[1]
    output_str = string_to_unicode_and_ascii(input_str)
    num_0 = len(output_str)//11+1
    num_1 = 1
    for i in range(0,len(output_str),11):
        list = [0x72,3,num_0,num_1]
        for s in output_str[i:i+11] :
            list.append(s)
        addCRC(list)
        num_1 += 1
        print(format_list_to_hex_string(list))
  

图片描述

图片描述

这里应该不能算结束,我本来是想怎么想办法刷机getadb,提取手表app,拿来看看能不能rce玩的,但是没有wifi功能,只能算个蓝牙设备。无奈狗日的不让我拆,等我放假回老家给byd揍一顿再拆。


[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 6天前 被p1yang编辑 ,原因:
收藏
免费 1
支持
分享
最新回复 (4)
雪    币: 431
活跃值: (14100)
能力值: ( LV13,RANK:606 )
在线值:
发帖
回帖
粉丝
2

可以可以,我的那块拆掉了hhh,里面是瑞昱的 RTL8762DT

6天前
0
雪    币: 286
活跃值: (1824)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
3
yichen115 可以可以,我的那块拆掉了hhh,里面是瑞昱的 RTL8762DT
是安卓的系统么,我在想办法刷机了
6天前
0
雪    币: 431
活跃值: (14100)
能力值: ( LV13,RANK:606 )
在线值:
发帖
回帖
粉丝
4
p1yang 是安卓的系统么,我在想办法刷机了
不是安卓,只是个ARM M 核的小芯片
6天前
0
雪    币: 286
活跃值: (1824)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
5
yichen115 不是安卓,只是个ARM M 核的小芯片
嗷嗷
6天前
0
游客
登录 | 注册 方可回帖
返回
//