chwdt/vanmoof-tools
GitHub: chwdt/vanmoof-tools
一套针对 VanMoof S3/X3 智能自行车的逆向工程工具集,支持固件解包打包、区域限制破解、密钥提取及底层硬件调试。
Stars: 33 | Forks: 4
# vanmoof-tools
用于支持 VanMoof S3/X3 逆向工程工作的工具
## 已知固件镜像:
- mainware.bin (MCU: ST STM32F413VGT6)
- bleware.bin (MCU: TI CC2642R1F)
- motorware.bin (MCU: TI TMS320F28054F)
- shifterware.bin (MCU: MindMotion MM32F031F6U6)
- batteryware.bin (MCU: ST STM32L072CZT6)
- bmsboot.bin (MCU: 参见 batteryware.bin)
- muco-boot.bin (MCU: 参见 mainware.bin)
- bleboot.bin (MCU: 参见 bleware.bin)
- shifterboot.bin (MCU: 参见 shifterware.bin)
## 关于固件镜像的一些观察:
自行车的大多数组件都包含分离的 boot loader 和固件镜像,Bluetooth 固件可能与该方案不同。
boot loader 以典型的 ARM vector table 开始,长度为 20Kb (0x5000 字节)。
boot loader 二进制文件的最后 8 个字节包含:
- 4 字节版本信息,编码为 ASCII 字符
- 4 字节 CRC32。
CRC 使用 STM32 CRC 算法计算,覆盖整个 boot loader 数据,但不包含最后 4 个字节。
固件镜像以魔数 0xaa55aa55 开始,后跟以下头部项:
- 4 字节版本信息
- 4 字节 CRC32
- 4 字节长度
- 12 字节日期(ASCII)
- 12 字节时间(ASCII)
所有 4 字节值均采用小端编码。
CRC 使用 STM32 CRC 算法计算,覆盖整个镜像数据,此时头部的 CRC 和长度字段均设置为 0xffffffff。
## 设置 / 安装
你可以使用 Raspberry Pi、你积灰的旧 Linux 机器,或者像 WSL 和 Linux 机器(Ubuntu 或 Debian 均可)这样时髦的新东西。
遗憾的是 macos 似乎不起作用。
```
apt update
apt install build-essential gcc-arm-none-eabi binutils-arm-none-eabi
make
```
## unpack
用法:`unpack `
此工具提取 VanMoof 更新文件(也称为 PACK 文件)的内容。一个 PACK 文件以包含魔数“PACK”的头部开始,后跟指向目录结构的偏移量和目录结构的长度。目录结构(位于文件末尾)包含一个或多个条目,每个条目包含文件名、偏移量和数据长度。有关这些结构的详细信息,请参阅 pack.h。
如果当前目录中存在 PACK 文件中包含的文件,该工具将覆盖该文件。请在一个单独的目录中运行此工具,以确保不会丢失任何数据。
## pack
用法:`pack [ ...]`
此工具将一个或多个固件文件打包成 VanMoof 更新文件,也称为 PACK 文件。这是 `unpack` 命令的逆操作。
## crc32
用法:`crc32 `
此工具计算并验证 boot loader 和固件镜像的 CRC。
## patch
用法:`patch [-v] [-f ] [-m ] `
此工具修补现代 VanMoof mainware 文件,以便在启动期间不会将 OFFROAD 区域重置为 US 区域。功率辅助级别可以再次配置为 5。当使用车把循环切换功率辅助级别时,如果区域为 OFFROAD,自行车也将循环切换到功率级别 5。
命令行上给定的文件将被覆盖为文件的修补版本,因此在使用该工具之前,请备份你的 mainware。
### 选项:
- `-v`:详细输出
- `-f `:指定不同的版本,格式:`..`
- `-m `:更改默认自行车型号,格式:`,,`
目前该工具仅适用于 mainware 版本 1.9.3。
## ble-patch
用法:`ble-patch `
此工具修补 VanMoof bleware 文件,将命令 `rtos-statistics` 替换为命令 `dump`。在 bledebug 控制台内使用 `dump keys` 将显示存储的密钥。这些密钥是两个在工厂自行车调试期间使用的出厂默认密钥、你的 API 密钥以及 VanMoof 制造商密钥。后者用于加密固件镜像(当向 APP 发送更新时)。
你可以通过创建包含此 bleware 的 pack 并在 bledebug 控制台内使用命令 `pack-upload` 来更新自行车上已修补的 bleware。你需要使用 ymodem 发送创建的 pack。
输出如下所示:
```
> dump keys
Key 0x00: UKEY 52XXXXXXXXXXXXXXXXXXXXXXXXXXXX2f 00000001 000003fe CRC 81XXXX92
Key 0x01: UKEY 98XXXXXXXXXXXXXXXXXXXXXXXXXXXX02 00000002 000001f4 CRC 72XXXX94
Key 0x7c: M-ID 00000000000000000000000000000000 00000008 00000000 CRC 7062da7e
Key 0x7d: UKEY 5f5f5f5f5f4f574e45525f5045524d53 00000000 ffffffff CRC 4f25ee68
Key 0x7e: MKEY 710b2ea0dc8568b7b5e5ec0b8a39dae9 00000000 00000000 CRC a69429b6
Key 0x7f: MKEY 46383841XXXXXXXXXXXXXXXX4d4f4f46 00000000 ffffffff CRC 4aXXXX7e
```
密钥 0x7d 和 0x7f 似乎是工厂调试密钥,转换为 ASCII 后内容为:
```
Key 0x7d: UKEY _____OWNER_PERMS 00000000 ffffffff CRC 4f25ee68
Key 0x7f: MKEY F88AXXXXXXXXMOOF 00000000 ffffffff CRC 4aXXXX7e
^- Bike MAC Address
```
这也可以转储内存(即 ROM、内部 FLASH 或外部 FLASH):
```
> dump mem 10000000 40000
10000000 00 20 00 11 b1 19 00 10 bf 20 00 10 c1 20 00 10 . ...... . ... ..
10000010 c3 20 00 10 c3 20 00 10 c3 20 00 10 00 00 00 00 . ... .. . ......
...
> dump extflash 5afa0 20
0005afa0 5f 5f 5f 5f 5f 4f 57 4e 45 52 5f 50 45 52 4d 53 _____OWN ER_PERMS
0005afb0 00 00 00 00 ff ff ff ff 55 4b 45 59 68 ee 25 4f ........ UKEYh.%O
```
有一个特殊命令显示 `CCFG_TI_OPTIONS` 和 `CCFG_TAP_DAP_*` 的值,此命令将修补 BLE 芯片的 boot loader 以启用 BLE 芯片的 JTAG 调试端口,以便进一步调试。第一次调用该命令时,它将输出如下值:
```
> dump ccfg
CCFG_TI_OPTIONS: 0xffffff00
CCFG_TAP_DAP_0: 0xff000000
CCFG_TAP_DAP_1: 0xff000000
JTAGCFG: 0x00000000
```
当发现这些值时,闪存的最后一个扇区(包括 CCFG)在修补 CCFG 值和版本字符串的同时,从 0x56000 向下复制到 0x46000,然后将该扇区复制回 0x56000。经过 `reset` 后,这些新的 CCFG 值生效,JTAG 端口即被启用:
```
> dump ccfg
CCFG_TI_OPTIONS: 0xffffffc5
CCFG_TAP_DAP_0: 0xffc5c5c5
CCFG_TAP_DAP_1: 0xffc5c5c5
JTAGCFG: 0x00000003
```
一旦存在这些 CCFG 值,boot loader 就不会再被触碰。
## patch-dump
此工具像上面的 `patch` 一样修补现代 VanMoof mainware,但添加了一个将 FLASH 或内存转储到控制台的功能。此功能被修补到 `help` 命令中,并将 FLASH 或内存输出为 hexdump。用法为 `help `。
hexdump 可以使用 `dump2bin.sh` 脚本转换为二进制。
旧版本会将整个 FLASH 输出为 S-Records,源代码仍在 repo 中提供,如果你想使用此功能,请编辑 Makefile。
将终端输出捕获到日志文件中,并将 S-Records 裁剪到文件 `vanmoof.srec` 中。要将此转储转换为自行车内部使用的不同二进制文件,请使用这些 shell 命令:
```
objcopy -I srec -O binary vanmoof.srec vanmoof.bin
dd if=vanmoof.bin of=muco-boot.bin bs=4096 count=8
dd if=vanmoof.bin of=vanmoof-config-a.bin bs=4096 skip=8 count=4
dd if=vanmoof.bin of=vanmoof-config-b.bin bs=4096 skip=12 count=4
dd if=vanmoof.bin of=shifterware.bin bs=4096 skip=16 count=16
dd if=vanmoof.bin of=mainware.bin bs=4096 skip=32 count=64
dd if=vanmoof.bin of=shadowware.bin bs=4096 skip=96 count=64
dd if=vanmoof.bin of=motorware.bin bs=4096 skip=160 count=32
dd if=vanmoof.bin of=batteryware.bin bs=4096 skip=192 count=32
dd if=vanmoof.bin of=bmsboot.bin bs=4096 skip=224 count=32
```
## 智能控制器内部闪存中的偏移量:
```
0x08000000: stm32 boot loader
0x08008000: bike config A
0x0800c000: bike config B
0x08010000: shifterware image
0x08020000: mainware image
0x08060000: shadow image
0x080a0000: motorware image
0x080c0000: batteryware image
0x080e0000: bmsboot image
```
## 智能控制器内部 SRAM 中的偏移量:
```
0x20000a00: Bike state/config
from FLASH at 0x8008000 or 0x800c000, 0xc0 bytes
+ 0x0f4: Sound bitmask [1] low
+ 0x0f8: Sound bitmask [1] medium
+ 0x0fc: Sound bitmask [1] high
+ 0x100: Backup code
+ 0x102: Lux low
+ 0x105: Volume low
+ 0x106: Volume medium
+ 0x107: Volume high
+ 0x108: Shift mode: AUTO/MANUAL
+ 0x109: Region
+ 0x10a: Unit system
+ 0x10b: Wheel motor type
+ 0x10c: Light mode
+ 0x10e: Shift up EU (3 x uint16_t)
+ 0x114: Shift up US (3 x uint16_t)
+ 0x11a: Shift up JP (3 x uint16_t)
+ 0x120: Shift up OFFROAD (3 x uint16_t)
+ 0x126: Shift down EU (3 x uint16_t)
+ 0x12c: Shift down US (3 x uint16_t)
+ 0x132: Shift down JP (3 x uint16_t)
+ 0x138: Shift down OFFROAD (3 x uint16_t)
+ 0x140: Mainware version
+ 0x144: Region lock
+ 0x145: Model: Bit0: 0=ES3, 1=ES4; Bit1: 1=E-Shifter, Bit2: 1=Display
+ 0x146: Custom soc
+ 0x147: HW revision
+ 0x1c0: CRC32 over FLASH data
Motor support settings (from mainware):
+ 0x2c6: Motor percent power level 0
+ 0x2c8: Motor speed limit power level 0
+ 0x2ca: Motor percent power level 1
+ 0x2cc: Motor speed limit power level 1
+ 0x2ce: Motor percent power level 2
+ 0x2d0: Motor speed limit power level 2
+ 0x2d2: Motor percent power level 3
+ 0x2d4: Motor speed limit power level 3
+ 0x2d6: Motor percent power level 4
+ 0x2d8: Motor speed limit power level 4
+ 0x2d6: Motor percent power level 5
+ 0x2d8: Motor speed limit power level 5
from EEPROM, 0x3c bytes
+ 0x310: Alarm state
+ 0x311: Play lock sound
+ 0x312: Remote locked
+ 0x313: Logging APP/Serial
+ 0x314: Shipping
+ 0x315: Cached BMS soc
+ 0x316: Power level + Boost
+ 0x317: Alarm enable/disable
+ 0x318: Horn file index
+ 0x31c: Odometer: km * 10
+ 0x320: Timestamp bell button
+ 0x324: Timestamp boost button
+ 0x328: Timestamp GSM check
+ 0x32c: Firmware update order (6 bytes)
+ 0x332: BMS soc override
+ 0x333: BLE sleep request
+ 0x334: Shifter retries
+ 0x336: Shifter firmware version
+ 0x338: Shifter total shifts
+ 0x33c: GSM tracking heartbeat
+ 0x340: Kicklock state
+ 0x341: Battery state
+ 0x342: BMS firmware version
+ 0x344: Wake counter
+ 0x348: CRC32 over EEPROM data
Bike state
+ 0x34c: BLE debug flag
+ 0x34d: GSM debug flag
+ 0x34e: Shift debug flag
+ 0x34f: BMS debug flag
+ 0x358: Loop count actual
+ 0x35c: Loop count min
+ 0x360: Loop count max
+ 0x364: Motor error
+ 0x366: Motor speed
+ 0x368: Motor I
+ 0x36a: Motor m_tmp
+ 0x36c: Motor d_tmp
+ 0x36e: Motor wlsp
+ 0x370: Motor Ubat
+ 0x372: Motor Pdsp
+ 0x374: Motor Pdtrg
+ 0x376: Motor io
+ 0x388: Motor firmware version
+ 0x38c: BLE firmware version
+ 0x390: BLE MAC address (6 bytes)
+ 0x396: BMS ESN (18 bytes)
+ 0x3a8: Debug password (16 bytes)
+ 0x3ba: Lipo version
+ 0x3c0: Error flags (2 x uint32_t)
+ 0x3cc: Speed 1
+ 0x3ce: Speed 2
+ 0x3d1: Power level (init from 0x316 & 0x7f)
+ 0x3d2: Power level (copy, init from 0x316 & 0x7f)
+ 0x3d3: Ride change
+ 0x3e0: Lipo state
+ 0x3e4: Powerbank soc
+ 0x3e6: Powerbank serial number (4 bytes)
+ 0x3ea: Powerbank serial version (3 bytes)
+ 0x3ed: Powerbank soh
+ 0x3ee: Powerbank noc
+ 0x3f1: Powerbank present
+ 0x3f8: GSM type pointer
Battery state
+ 0x402: Type
+ 0x406: Error flags
+ 0x408: Temperature (°C/100)
+ 0x40a: Voltage (mV)
+ 0x40c: State of charge (%)
+ 0x40e: Current (mA)
+ 0x412: Discharging flag
+ 0x414: Testmode flag
+ 0x416: HW version
+ 0x418: SW version
+ 0x41a: Serial number (14 bytes)
+ 0x428: Manufacture date (3 bytes)
+ 0x42c: cap_nominal
+ 0x42e: cap_full
+ 0x430: cap_remain
+ 0x432: health
+ 0x434: cycle_count
+ 0x438: cell_voltage (10 x mV)
+ 0x44c: tp1
+ 0x44e: tp2
+ 0x450: mos_tmp
+ 0x454: u_max
+ 0x456: u_min
+ 0x45a: Boot loader version
+ 0x462: fsr
+ 0x490: dotp
+ 0x492: dutp
+ 0x494: cotp
+ 0x496: cutp
+ 0x498: docp1
+ 0x49a: docp2
+ 0x49c: cocp1
+ 0x49e: cocp2
+ 0x4a0: ovp1
+ 0x4a2: ovp2
+ 0x4a4: uvp1
+ 0x4a6: uvp2
+ 0x4a8: pdocp
+ 0x4aa: pdscp
+ 0x4ac: motp
+ 0xac scp
```
`[1]` [位掩码](vanmoof_sx3_sound_bitmask.md)
## BLE 控制器内部 ROM 中的偏移量:
```
10000000: TI ROM boot loader
10007000: TI ROM ble5stack
1002b400: TI ROM tirtos7
50001000: FCFG1
500012e8: FCFG1::MAC_BLE_0
500012ec: FCFG1::MAC_BLE_1
50003000: CCFG (mirrored from FLASH 56000)
```
## BLE 控制器内部 FLASH 中的偏移量:
```
00000000: bleware.bin
00056000: boot loader
00057f38: boot loader version: "BVERApr 23 2020"
00057f48: boot loader version: "14:10:12"
00057fa8: CCFG (Customer configuration) Mirrored at CCFG 50004fa8
00057fec: CCFG::IMAGE_VALID_CONF -> 56000 (boot loader entry)
```
## BLE 控制器内部 SRAM 中的偏移量:
```
2000a3dc: Memory location of UKEY, when used in BLE protocol (1.4.1)
2000a3fc: Memory location of MKEY, when used in BLE protocol (1.4.1)
2000cec8: Memory location of UKEY, when used in BLE protocol (2.4.1)
2000cee8: Memory location of MKEY, when used in BLE protocol (2.4.1)
```
## update.py
一个简单的 cheasy 更新工具,用于将用 `pack` 打包的固件发送到自行车。运行此工具需要 [pymoof](https://github.com/quantsini/pymoof)。
在使用该工具之前,你需要插入自行车的 API 密钥和制造商密钥。
用作通过 BLE 进行固件更新的参考。
## read_logs.py
一个简单的 cheasy 工具,用于通过 BLE 从自行车读取内部调试日志。运行此工具需要 [pymoof](https://github.com/quantsini/pymoof)。
在使用该工具之前,你需要插入自行车的 API 密钥。
用作如何通过 BLE 读取日志的参考。
## 内部通信
主 MCU 通过以下方式与其他 MCU 通信:
| UART | TX | RX | 功能 | 协议 | 备注 |
| :-- | :-- | :-- | :-- | :-- | :-- |
| USART1 | PA9 | PA10 | 替代调试 | console | 可通过调试头 TC1 访问? |
| USART2 | PA2 | PA3 | GSM uBlox G350 | AT commands | 在 `gsmdebug` 模式下透传 |
| USART3 | PD8 | PD9 | Shifter MCU | [Modbus](https://en.wikipedia.org/wiki/Modbus) ||
| UART4 | PA0 | PA1 | Battery MCU | [Modbus](https://en.wikipedia.org/wiki/Modbus) ||
| UART5 | PB13 | PB12 | BLE MCU control | SSP | [SLIP](https://en.wikipedia.org/wiki/Serial_Line_Internet_Protocol) 编码包 |
| USART6 | PC6 | PC7 | Motor MCU | SSP | [SLIP](https://en.wikipedia.org/wiki/Serial_Line_Internet_Protocol) 编码包 |
| UART7 | PE8 | PE7 | Main MCU debug | console | 尾灯后面的端口 |
| UART8 | PE1 | PE0 | BLE MCU debug | console | 在 `bledebug` 模式下透传 |
[SLIP](https://en.wikipedia.org/wiki/Serial_Line_Internet_Protocol) 编码的数据包包含一个字节的发送者地址、一个命令字节、一个序列字节、一个表示偏移量或功能码的小端 16 位字、一个 16 位数据长度字、数据和一个 16 位 CRC,该 CRC 与 [Modbus](https://en.wikipedia.org/wiki/Modbus) CRC 相同。
命令字节对于 READ 为 06,对于 WRITE 为 07,对于 ACK 数据包为 05。CRC 是在没有 C80 成帧字符的数据包上计算的。
```
C0 01 06 56 1A 01 33 F8 C0 MCU -> BLE READ req 56: 0x011a
C0 02 05 56 53 6E C0 BLE -> MCU ACK 56
C0 02 07 79 1A 01 06 00 F8 8A 5E XX XX XX YY YY C0 BLE -> MCU WRITE req 79: 0x011a: 0x0006 bytes: 0xF8 8A 5E XX XX XX
C0 01 05 79 E2 B2 C0 MCU -> BLE ACK 79
```
MCU 在同一个数据包处理程序中处理来自 BLE 和 Motor MCU 的两个数据包流,因此 BLE 和 Motor 的偏移量/功能码必须是互不重叠的 (disjunct)。
## BLE 服务 `6acc5505-e631-4069-944d-b8ca7598ad50`
蓝牙服务 `@5505` 包含后台消息,这些消息用于在自行车上配置 UKEY 或 MKEY,例如用于自行车共享或车间访问。这些消息使用自行车的 MKEY 加密。消息前缀是一个两个字节的随机数 (nonce)、字节 `0x01` 和一个偏移量字节。这 4 个字节未加密。消息紧随其后的是 n * 16 字节的 MKEY 加密数据。
| Nonce | Const 1 | Offset | Encrypted backoffice message |
| :-- | :-- | :-- | :-- |
| a12d | 01 | 00 | acc3d0f327c70f5a4755185bcb27c40df508b19df62e7551127abe79c9c822326adef001d97d51b45f8c58a7d2cc0cc0 |
解密后的消息构建如下(格式 1):
| M-ID | Cmd | Len | UKEY data | Index | Perms | Modbus CRC | Padding |
| :-- | :-- | :-- | :-- | :-- | :-- | :-- | :-- |
| 00000008 | 0001 | 18 | 98d29703b832207ed7c67b34edfadc02 | 00000002 | 000001f4 | 6845 | 0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f |
消息格式 2:
| M-ID | Cmd | Len | Index | Modbus CRC | Padding |
| :-- | :-- | :-- | :-- | :-- | :-- |
| 00000004 | 0003 | 04 | 00000002 | 2577 | 0505050505 |
消息格式 3:
| M-ID | Cmd | Len | State | Modbus CRC | Padding |
| :-- | :-- | :-- | :-- | :-- | :-- |
| 00000007 | 0005 | 01 | 00 | 5ae4 | 0808080808080808 |
消息格式 4:
| M-ID | Cmd | Len | Modbus CRC | Padding |
| :-- | :-- | :-- | :-- | :-- |
| 00000005 | 0006 | 00 | 6c18 | 090909090909090909 |
消息格式 5:
| M-ID | Cmd | Len | UKEY data | Index | Perms | ... | UKEY data | Index | Perms | Modbus CRC | Padding |
| :-- | :-- | :-- | :-- | :-- | :-- | :-- | :-- | :-- | :-- | :-- | :-- |
| 00000008 | 0001 | 48 | 98d29703b832207ed7c67b34edfadc02 | 00000002 | 000001f4 | ... | cb27c40df508b19df62e7551127abe79 | 00000004 | 000001f4 | xxxx | 07070707070707 |
消息格式 6:
| M-ID | Cmd | Len | Unknown1 | Unknown2 | Modbus CRC | Padding |
| :-- | :-- | :-- | :-- | :-- | :-- | :-- |
| 00000004 | 0008 | 02+n | xxxx | yy .. yy (n bytes) | zzzz | .. |
Cmd 值有:
| Cmd | Message (format) |
| :-- | :-- |
| 0001 | Update UKEY (1) |
| 0002 | Update MKEY (1) |
| 0003 | Erase key by index (2) |
| 0004 | Nothing (always succeed) |
| 0005 | Set Module State `@5562` (3) |
| 0006 | Erase all keys (4) |
| 0007 | Update multiple keys (5) |
| 0008 | Unknown, read keys? (6) |
| 0009 | Unknown, FMNA related |
| 000a | Unknown, FMNA related |
## 调试控制台
调试控制台上的 `Login:` 提示符知道两个密码,一个是固件中硬编码的固定密码 `vEVjGF!paYsM2EBV8SoDT8*T0eBT6xevaoxCaO`,另一个密码包含自行车 MAC 地址的最后三个字节,后跟单词 "DeBug",由 `printf("%02X%02X%02XDeBug", MAC[3], MAC[4], MAC[5])` 输出。
调试控制台有一个 `help` 命令。通过分别使用命令 `bledebug` 和 `gsmdebug`,也可以从调试端口访问 BLE 芯片和 GSM 调制解调器。
```
Login: ***********
Welcome to ES3
help
Available commands:
help This tekst
reboot reboot CPU
login Login shell
logout Logout shell
ver Software version
distance Manual set dst
gear set gear
region Region 0..3
model model
blereset hard reset BLE
bledebug redirect uart8
show Parameters
motorupdate Update F2806 CPU
vollow Audio volume
volmid Audio volume
volhigh Audio volume
speed override speed
loop main loop time
shipping Shipping mode
factory-shipping Factory shipping mode (ignores BMS)
logprn Print log
logclr Clear log 6
logapp 1/ 0
powerchange 1/ 0
factory Load factory defaults
battery Show battery
batware Battery update
batboot BatteryBL update
batreset Battery reset
shiftware Battery update
shifterstatus Show shifter
shiftdebug Show Modbus
shiftresetcounter Reset shift counter
motorstatus
gsminfo Info from Ublox
gsmstart start GSM function
gsmdebug redirect uart2
bmsdebug Show Modbus
sound sample,volume,times
adc read adc
bwritereg Modbus Bat write register
bwritedata Modbus Bat write data
breadreg Modbus Bat read register
swritereg Modbus Shift write register
swritedata Modbus Shift write data
sreadreg Modbus Shift read register
stc read lipo monitor
stcreset
setoad test
setgear save muco shifter
soc overrule soc
customsoc sound soc
hwrev hardware revision
error set errorcode
ver
ES3.0 Main 1.09.03 (10:30:52 Apr 30 2025)
ES3 boot 1.9
Motorware S.0.00.22
BMSWare BL:007 FW:1.17 RSOC:100 Cycles:42 HW:3.10 ESN:XXXXXXXXXXXXXX
Shifterware 0.237 stored: 0.237
BLEWare 1.4.01
GSMWare 08.90
CMD_BLE_MAC F8:8A:5E:XX:XX:XX
```
BLE 控制台也有一个 `help` 命令,可以使用命令 `exit` 返回 MCU 控制台。
```
Login: ***********
Welcome to ES3
bledebug
Connect to UART8
> help
The following commands are available:
firmware-update - update a new image of firmware to the external flash
extflash-verify - verify the current flashchip
log-count - get log-count statistic
log-dump - print blocks starting at address
log-flush - flush all log-entries
log-inject - Create fake-logs
audio-play - play audio bound to the specified index
audio-stop - stop playing the current audio file
audio-dump - dump all audio files in external memory
audio-upload - upload audio binary using Y-Modem at the address linked to the specified index
audio-volume-set-all - set audio level of all audio-clips (0-3)
pack-upload - upload a PACK file by Y-Modem
pack-list - list the contents of a PACK file
pack-delete - delete a PACK file
pack-process - process pack files in external flash memory
ble-info - dump current BLE connection info / statistics
ble-disconnect - force a disconnect of all connected devices
ble-erase-all-bonds - erase all bonds
shutdown - shutdown the system
rtos-statistics - dump memory stats every 500ms
rtos-nvm-compact - Compact the non-volatile storage
dump - dump keys/memory/extflash
info/ver - show basic firmware info
exit - exit from shell
help - show all monitor commands
> info
BLE MAC Address: "f8:8a:5e:xx:xx:xx"
Device name ................ : ES3-F88A5EXXXXXX
Firmware version ........... : 1.04.01
Compile date / time ........ : May 12 2025 / 09:03:35
BIM firmware version ....... : 1.00.00
BIM compile date / time .... : Apr 23 2020 / 14:10:12
reset type ................. : pin reset
systick .................... : -117259002
> exit
```
GSM 控制台使用扩展的 ublox [AT](https://en.wikipedia.org/wiki/Hayes_AT_command_set) 命令。可以使用序列 `[14~` 退出。
```
Login: ***********
Welcome to ES3
gsmdebug
Modem powering on..
ATI
SARA-G350-02S-01
OK
AT+UGSRV?
+UGSRV: "ublox1.vanmoof.com","ublox1.vanmoof.com","PBNjh0V46Eev8CcfS4LPJg",14,4,1,65,0,15
OK
AT+UPSDA=0,3
OK
AT+UPSND=0,8
+UPSND: 0,8,1
OK
AT+ULOCIND=1
OK
AT+ULOC=2,2,1,180,1,10
OK
+UULOCIND: 0,0
+UULOCIND: 1,0
+UULOCIND: 2,0
+UULOCIND: 3,0
+UULOC: 16/05/2025,08:27:54.000,,,0,601,0,0,0,2,0,0,0
```
## 想法
我发现发送大于 256 字节的固件更新包时控制器会崩溃,因此可能有机会利用这一点作为 exploit 通过蓝牙读出 MKEY。我们需要 MKEY 才能:
- 解码从 Vanmoof 收到的更新包
- 向自行车发送我们自己的更新包(修补了诸如 offroad 等功能)
## 外部资源
- [线束](https://www.moofrepair.nl/wiring-harness/)
- [调试控制台](https://www.reddit.com/r/vanmoofbicycle/comments/17744l7/comment/k4z0wyi/?utm_source=share&utm_medium=web3x&utm_name=web3xcss&utm_term=1&utm_content=share_button)
- [硬件信息](https://github.com/ciborg971/VanmoofX3RE/tree/master/OG)
- [硬件信息](https://github.com/dtngx/VMBattery)
- [pymoof](https://github.com/quantsini/pymoof)
## 特别感谢!
- [Tobias](https://github.com/Knight1)
- [Quinten](https://github.com/quintenadema)
- [Max](https://github.com/MPeek1995)
## 趣闻
VanMoof 使用的魔数以前曾被其他人使用过,这在使用 unix `file` 命令时会产生一些有趣的输出:
```
$ file packfile.bin
packfile.bin: Quake I or II world or extension, 340 entries
$ file mainware.bin
mainware.bin: BIOS (ia32) ROM Ext. (85*512)
```
标签:Cutter, Linux工具, Raspberry Pi, STM32, TI CC2642, VanMoof, 二进制分析, 云安全运维, 云资产清单, 内存执行, 固件分析, 固件解包, 客户端加密, 嵌入式安全, 物联网, 电动自行车, 硬件黑客, 端口探测, 蓝牙低功耗, 逆向工具, 逆向工程