Skip to content

Tools to support VanMoof S3/X3 reverse engineering efforts

Notifications You must be signed in to change notification settings

chwdt/vanmoof-tools

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

95 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

vanmoof-tools

Tools to support VanMoof S3/X3 reverse engineering efforts

Known firmware images:

  • 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: see batteryware.bin)
  • muco-boot.bin (MCU: see mainware.bin)
  • bleboot.bin (MCU: see bleware.bin)
  • shifterboot.bin (MCU: see shifterware.bin)

Some observations about firmware images:

Most components of the bike contain a split boot loader and firmware image, the Bluetooth firmware might differ from this scheme.

The boot loader starts with the typical ARM vector table and is 20Kb (0x5000 bytes) long. The last 8 bytes of the boot loader binary contain:

  • 4 bytes version encoded as ASCII characters
  • 4 bytes CRC32.

The CRC is calculated using the STM32 CRC algorithm over the whole boot loader data without the last 4 bytes

The firmware image starts with a magic 0xaa55aa55, followed by these header items:

  • 4 bytes version info
  • 4 bytes CRC32
  • 4 bytes length
  • 12 bytes date in ASCII
  • 12 bytes time in ASCII

All 4 byte values are encoded little endian

The CRC is calculated using the STM32 CRC algorithm over the whole image data with the header CRC and length fields both set to 0xffffffff.

Setup / Installation

You can use a Raspberry Pi, your dusty old Linux Machine or fancy new Stuff like WSL and a Linux Machine (Ubuntu or Debian is fine).

apt update
apt install build-essential gcc-arm-none-eabi binutils-arm-none-eabi
make

On windows, you need to install mingw32 and the arm-none-eabi compiler toolchain, e.g. gcc 12.3.1 from arm-eabi toolchains, then run mingw32-make.

Sadly macos does not seem to work. (Coming soon, hopefully).

unpack

usage: unpack <packfile>

This tool extracts the contents of a VanMoof update file, also known as PACK file. A PACK file starts with a header containing the magic "PACK", an offset to a directory structure and the length of the directory structure. The directory structure (at the end of the file) contains one or more entries containing a filename, an offset, and the length of the data. See pack.h for details of these structures.

The tool will overwrite any file present in the current directory if this is contained in the PACK file. Run this in a separate directory to be shure not to loose any data.

pack

usage: pack <packfile> <warefile> [<warefile> ...]

This tool packs one or more firmware files into a VanMoof update file, also known as PACK file. This is the reverse operation of the unpack command.

crc32

usage: crc32 <warefile>

This tool calculates and verifies the CRC of both boot loader and firmware images.

patch

usage: patch [-v] [-f <fake_version>] [-m <model>] <mainware>

This tool patches a modern VanMoof mainware file, so the region OFFROAD is not reset to region US during boot. The power assistance level can be configured to 5 again. When cycling through power assistance levels using the handle bar, the bike will cycle through power level 5 as well if region is OFFROAD.

The file given on the command line is overwritten with the patched version of the file, so please make a backup of your mainware before using the tool.

Options:

  • -v: Be verbose
  • -f <fake-version>: Specify a different version, format: <major>.<minor>.<patch>
  • -m <model>: Change default bike model, format: <model (3|4)>,<shifter (0|1)>,<display (0|1)>

Currently the tool only works for mainware version 1.9.3.

ble-patch

usage: ble-patch <bleware>

This tool patches the VanMoof bleware file, so the command rtos-statistics is replaced with a command dump. Using dump keys inside the bledebug console will show the stored keys. These keys are two factory default keys used during bringup of the bike in factory, your API key, and the VanMoof manufacturer key. The latter is used to encrypt firmware images (when sending updates to the APP).

You can update the patched bleware on the bike by creating a pack containing this bleware and using the command pack-upload inside the bledebug console. You need to send the created pack using ymodem.

Output looks like:

> 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

The keys 0x7d and 0x7f seem to be the factory bring-up keys, translated to ASCII they read:

Key 0x7d: UKEY _____OWNER_PERMS 00000000 ffffffff CRC 4f25ee68
Key 0x7f: MKEY F88AXXXXXXXXMOOF 00000000 ffffffff CRC 4aXXXX7e
               ^- Bike MAC Address

This can also dump memory (i.e. ROM, internal FLASH, or external 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

There is a special command which shows the values of CCFG_TI_OPTIONS and CCFG_TAP_DAP_*, this command will patch to boot loader of the BLE chip to enable the JTAG debug port of the BLE chip for further debugging. The first time the command is called, it will output values like:

> dump ccfg
CCFG_TI_OPTIONS: 0xffffff00
CCFG_TAP_DAP_0:  0xff000000
CCFG_TAP_DAP_1:  0xff000000
JTAGCFG:         0x00000000

When these values are found, the last sector of flash (including the CCFG) is copied down from 0x56000 to 0x46000 while patching the CCFG values and the version string, and then copying the sector back to 0x56000. After a reset these new CCFG values take effect and the JTAG port is enabled:

> dump ccfg
CCFG_TI_OPTIONS: 0xffffffc5
CCFG_TAP_DAP_0:  0xffc5c5c5
CCFG_TAP_DAP_1:  0xffc5c5c5
JTAGCFG:         0x00000003

The boot loader is not touched again once these CCFG values are present.

patch-dump

This tool patches a modern VanMoof mainware as patch above, but adds a function to dump FLASH or memory to the console. This function is patched into the help command and will output FLASH or memory as hexdump. Use as help <addr> <count>.

The hexdump can be converted to binary using the dump2bin.sh script.

An older version would output the whole FLASH as S-Records, the source is still provided in the repo, edit the Makefile if you want to use this function.

Capture the terminal output to a logfile and clip out the S-Records to a file vanmoof.srec. To convert this dump to the different binaries used inside the bike, use these shell commands:

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

Offsets in smart controller internal flash:

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

Offsets in smart controller internal 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	(&deg;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] bitmask

Offsets in BLE controller internal 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)

Offsets in BLE controller internal 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)

Offsets in BLE controller internal 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

A simple cheasy update tool to send firmware packed with pack to the bike. This needs pymoof to run.

You need to insert your bikes API key and manufacturer key before using the tool.

Use as a reference for the firmware update over BLE.

read_logs.py

A simple cheasy tool to read the internal debug logs from the bike using BLE. This needs pymoof to run.

You need to insert your bikes API key before using the tool.

Use as a reference howto read logs over BLE.

Internal communication

Main MCU communicates with the other MCUs via:

UART TX RX Function Protocol Comments
USART1 PA9 PA10 Alternative debug console maybe accessed through debug header TC1?
USART2 PA2 PA3 GSM uBlox G350 AT commands passthrough in gsmdebug mode
USART3 PD8 PD9 Shifter MCU Modbus
UART4 PA0 PA1 Battery MCU Modbus
UART5 PB13 PB12 BLE MCU control SSP SLIP encoded packets
USART6 PC6 PC7 Motor MCU SSP SLIP encoded packets
UART7 PE8 PE7 Main MCU debug console port behind rear light
UART8 PE1 PE0 BLE MCU debug console passthrough in bledebug mode

The SLIP encoded packets contain one byte sender address, a command byte, a sequence byte, a little endian 16 bit word which is an offset or a function code, a 16 bit data length word, data, and a 16 bit CRC, which is the same as the Modbus CRC.

The command byte is 06 for READ, 07 for WRITE, and 05 for ACK packets. The CRC is calculated over the packet without the C0 framing characters.

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

The MCU handles both packet streams from the BLE and the Motor MCU inside the same packet handler, so the offsets/function codes of the BLE and the Motor need to be disjunct.

BLE service 6acc5505-e631-4069-944d-b8ca7598ad50

The bluetooth service @5505 contains backoffice messages, these are used to configure UKEYs or MKEYs on the bike, for example for bike sharing or workshop access. These messages are encrypted using the bikes MKEY. The messages are prefixed with a two byte nonce, the byte 0x01 and an offset byte. These 4 bytes are not encrypted. The message continues with n * 16 bytes MKEY encrypted data.

Nonce Const 1 Offset Encrypted backoffice message
a12d 01 00 acc3d0f327c70f5a4755185bcb27c40df508b19df62e7551127abe79c9c822326adef001d97d51b45f8c58a7d2cc0cc0

The decrypted message is build up like this (format 1):

M-ID Cmd Len UKEY data Index Perms Modbus CRC Padding
00000008 0001 18 98d29703b832207ed7c67b34edfadc02 00000002 000001f4 6845 0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f

Message format 2:

M-ID Cmd Len Index Modbus CRC Padding
00000004 0003 04 00000002 2577 0505050505

Message format 3:

M-ID Cmd Len State Modbus CRC Padding
00000007 0005 01 00 5ae4 0808080808080808

Message format 4:

M-ID Cmd Len Modbus CRC Padding
00000005 0006 00 6c18 090909090909090909

Message format 5:

M-ID Cmd Len UKEY data Index Perms ... UKEY data Index Perms Modbus CRC Padding
00000008 0007 48 98d29703b832207ed7c67b34edfadc02 00000002 000001f4 ... cb27c40df508b19df62e7551127abe79 00000004 000001f4 xxxx 07070707070707

Message format 6:

M-ID Cmd Len Unknown1 Unknown2 Modbus CRC Padding
00000004 0008 02+n xxxx yy .. yy (n bytes) zzzz ..

The possible Cmd values are:

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

Debug console

The Login: prompt on the debug console knows two passwords, one fixed password hardcoded in the firmware vEVjGF!paYsM2EBV8SoDT8*T0eB&#T6xevaoxCaO and one password containing the last three bytes of the bikes MAC address followed by the word "DeBug", as output by printf("%02X%02X%02XDeBug", MAC[3], MAC[4], MAC[5]).

The debug console has a help command. The BLE chip and the GSM modem can also be accessed from the debug port, by using the commands bledebug and gsmdebug respectively.

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

The BLE console also has a help command and one can return to the MCU console with the command exit.

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 <start-index> <n>        - print <n> blocks starting at address <start-index>
    log-flush                         - flush all log-entries
    log-inject <n>                    - Create <n> fake-logs
    audio-play <index>                - 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 <index>              - upload audio binary using Y-Modem at the address linked to the specified index
    audio-volume-set-all <level>      - 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

The GSM console talks extended ublox AT commands. It can be exited with the sequence <ESC>[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,<latitude>,<longitude>,0,601,0,0,0,2,0,0,0

Ideas

I see a crash of the controller when sending firmware update packets > 256 bytes, so there might be the chance to use this as an exploit to read out the MKEY over bluetooth. We need the MKEY to be able to:

  • decode the update packages received from Vanmoof
  • send our own update package (patched with features like offroad) to the bike

External resources

Special thanks!

Fun facts

The magic numbers used by VanMoof have been used previously by others, this gives some funny output when using the unix file command:

$ 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)

About

Tools to support VanMoof S3/X3 reverse engineering efforts

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors