利用蓝牙模拟手环伪造微信运动步数

伪造微信运动步数的方法有很多种,可以hook系统提供步数统计的模块,可以伪造GPS,可以找第三方手环的漏洞。
第三方手环走微信iot平台AirSync协议的话可能通信过程启用AES加密,很难对其攻击。于是考虑自己成为一个手环设备生产商并伪造一个运动手环,欺骗微信运动来获取假的运动步数。

微信平台配置

此部分内容主要参考微信iot平台文档

公众号后台开通“设备功能”插件

如果没有认证过的公共号,也可以用公众号测试账号。
http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login
添加设备时,需要指明接入方案,选平台基础接入方案,连接类型选蓝牙,产品配置选蓝牙发现。登记成功后会得到一个微信硬件的型号编码即product_id。

获取access_token

https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=&secret=
获取完access_token之后,接下来从微信公共号后台找到自己的openid。

获取deviceid和二维码

https://api.weixin.qq.com/device/getqrcode?access_token=&product_id=
得到如下返回。

1
{"base_resp":{"errcode":0,"errmsg":"ok"},"deviceid":"gh_eee2c24a6f8e_852ddc34836c0559","qrticket":"http:\/\/we.qq.com\/d\/AQC5K_3BgSGNsg84sHISxXmHwMJrSp5sDf9AX1sB"}

微信平台会分配一个deviceid和对应的二维码qrticket,在绑定用户的时候用到。

设备授权

https://api.weixin.qq.com/device/authorize_device?access_token=
POST数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
"device_num":"1",
"device_list":[
{
"id":"gh_eee2c24a6f8e_852ddc34836c0559",
"mac":"ff8d22e19590",
"connect_protocol":"3",
"auth_key":"",
"close_strategy":"1",
"conn_strategy":"5",
"crypt_method":"0",
"auth_ver":"0",
"manu_mac_pos":"-1",
"ser_mac_pos":"-2",
"ble_simple_protocol": "1"
}
],
"op_type":"1",
"product_id": "25806"
}

主要为开启蓝牙精简协议。在数据包里配置好id、mac和product_id即可。mac为电脑蓝牙的mac地址。协议的具体内容参照微信文档

强制绑定用户和设备

https://api.weixin.qq.com/device/compel_bind?access_token=
POST数据

1
2
3
4
{
"device_id": "gh_eee2c24a6f8e_852ddc34836c0559",
"openid": "ouSvtwWqDVHQUGT3u0XkTpiJ9QsY"
}

在数据包里配置好device_id和openid。

这样,微信账号就与设备绑定好了。打开公共号会看到公共号下面有个“未连接”的字符串。

伪造手环设备

根据微信蓝牙精简协议文档

设备需要广播包带上微信的service,并在manufature data里带上mac地址。
微信Service uuid:0xFEE7
manufature specific data:需以MAC地址(6字节)结尾。并且manufature specific data长度需大于等于8字节(最前两个字节为company id,没有的话随便填)。
微信service下面需包含一个读特征值,uuid为:0xFEC9,内容为6字节MAC地址(ios系统其他软件连上设备之后,微信会去读该特征值,以确定设备MAC地址)。

所以只需要建立一个uuid为0xFEE7的service,里面包含0xFEA1、0xFEA2、0xFEC9三个Characteristic并设置好值和属性即可。

利用paypal开源的gatt,修改example里的server.go,在脚本里设置好蓝牙的mac和想要的步数,运行即可。

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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
package main
import (
"fmt"
"log"
"github.com/paypal/gatt"
"github.com/paypal/gatt/examples/option"
)
func main() {
mac := []byte{0x13, 0x37, 0xde, 0xad, 0xbe, 0xef}
// steps little endian
// 01 (步数)10 27 00(1万步) 0x002710 = 10000
// http://iot.weixin.qq.com/wiki/new/index.html?page=4-3
steps := []byte{0x01, 0x10, 0x27, 0x00}
const (
flagLimitedDiscoverable = 0x01 // LE Limited Discoverable Mode
flagGeneralDiscoverable = 0x02 // LE General Discoverable Mode
flagLEOnly = 0x04 // BR/EDR Not Supported. Bit 37 of LMP Feature Mask Definitions (Page 0)
flagBothController = 0x08 // Simultaneous LE and BR/EDR to Same Device Capable (Controller).
flagBothHost = 0x10 // Simultaneous LE and BR/EDR to Same Device Capable (Host).
)
d, err := gatt.NewDevice(option.DefaultServerOptions...)
if err != nil {
log.Fatalf("Failed to open device, err: %s", err)
}
// Register optional handlers.
d.Handle(
gatt.CentralConnected(func(c gatt.Central) { fmt.Println("Connect: ", c.ID()) }),
gatt.CentralDisconnected(func(c gatt.Central) { fmt.Println("Disconnect: ", c.ID()) }),
)
// A mandatory handler for monitoring device state.
onStateChanged := func(d gatt.Device, s gatt.State) {
fmt.Printf("State: %s\n", s)
switch s {
case gatt.StatePoweredOn:
s0 := gatt.NewService(gatt.UUID16(0xFEE7))
c0 := s0.AddCharacteristic(gatt.UUID16(0xFEA1))
c0.HandleReadFunc(
func(rsp gatt.ResponseWriter, req *gatt.ReadRequest) {
log.Println("Read: 0xFEA1")
rsp.Write(steps)
})
c0.HandleNotifyFunc(
func(r gatt.Request, n gatt.Notifier) {
go func() {
n.Write(steps)
log.Printf("Indicate 0xFEA2")
}()
})
c1 := s0.AddCharacteristic(gatt.UUID16(0xFEA2))
c1.HandleReadFunc(
func(rsp gatt.ResponseWriter, req *gatt.ReadRequest) {
log.Println("Read: 0xFEA2")
rsp.Write(steps)
})
c1.HandleNotifyFunc(
func(r gatt.Request, n gatt.Notifier) {
go func() {
n.Write(steps)
log.Printf("Indicate 0xFEA2")
}()
})
c1.HandleWriteFunc(
func(r gatt.Request, data []byte) (status byte) {
log.Println("Wrote 0xFEA2:", string(data))
return gatt.StatusSuccess
})
c2 := s0.AddCharacteristic(gatt.UUID16(0xFEC9))
c2.HandleReadFunc(
func(rsp gatt.ResponseWriter, req *gatt.ReadRequest) {
log.Println("Read: 0xFEC9")
rsp.Write(mac)
})
d.AddService(s0)
// Advertise device name and service's UUIDs.
a := &gatt.AdvPacket{}
a.AppendFlags(flagGeneralDiscoverable | flagLEOnly)
a.AppendUUIDFit([]gatt.UUID{s0.UUID()})
a.AppendName("salt")
// company id and data, MAC Address
// https://www.bluetooth.com/specifications/assigned-numbers/company-identifiers
a.AppendManufacturerData(0x2333, mac)
d.Advertise(a)
default:
}
}
d.Init(onStateChanged)
select {}
}

另外看到国内有人用nodejs实现了类似的功能

注意node版本需要大于0.12,另外bleno版本需要为0.4.0。

1
2
sudo apt-get install libusb-1.0-0-dev
npm install bleno@0.4.0

参考文献

http://iot.weixin.qq.com/wiki/new/index.html?page=4-3
https://github.com/luluxie/weixin-iot
https://github.com/paypal/gatt

分享到 评论