Hardware RevEng Security

Hacking Brightway scooters: A case study

Xiaomi 3 Lite dashboard with attached SWD and UART wires

Choose your character!

Brightway (Brightway Innovation Intelligent Technology (Suzhou) Co., Ltd.) produces scooters for the NAVEE and Xiaomi brand. The latest additions to the Xiaomi portfolio are the Electric Scooter 3 Lite, 4, 4 Pro and 4 Ultra. With exception of the 4 Pro, all are produced by Brightway.

The cheapest Xiaomi/Brightway model is the 3 Lite. It costs almost the same as the Xiaomi/Ninebot Mi 1S, but has somewhat weaker specs, such as a 5200mAh instead of a 7650mAh battery. So what’s the point of buying this seemingly inferior scooter? Well… it’s security research! By studying the inner workings of the 3 Lite, both from a hardware and a software standpoint, we can shed a light onto how serious Brightway takes security in their scooters.

In this blog post, I will be dismantling my newly bought 3 Lite scooter. First, I’m giving insights into the pairing and activation procedure, after which I describe the available registers, the command and message format and the structure of the serial numbers. It follows a detailed description of all the hardware and respective SOC memory layouts, after which I look at the UART and BLE protocols. Lastly, I will talk about the obvious security flaws discovered in the 3 Lite and how this relates to all other new Xiaomi scooter models.

Pairing process

Xiaomi started rolling out a stronger bonding mechanism with the 3 Lite and 4 Pro models. For connecting with the scooter, you have to download the Xiaomi Mi Home app. After scanning for scooters and selecting your 3 Lite scooter, the app asks for the camera permission. Now you scan a QR code that’s printed on top of a sticker that covers the dashboard. After scanning the QR code and its verification, you’re connected. A button press is no longer required.

The QR code is stored in the app and can be viewed while being connected to the scooter. The app tells you to take a screenshot of the QR code in case you lose the original one.

Every time you remove the scooter from the app and re-add it, you have to scan the QR code. However, the verification fails unless you do a BLE hard-reset first (throttle + 5x button press).


When you first turn on the 3 Lite it will constantly beep and be speed-limited to 10 km/h. Just like with previous (Xiaomi/Ninebot) models, you have to activate it. The activation button becomes available after watching the usual introduction video in the Mi Home app.

I’m greeted by a firmware update dialog immediately after connecting to the scooter. This update is not mandatory and the dialog can be dismissed.

Mi Home app offering firmware update for 3 Lite

Info registers

At this point, I will refer to the Mi Home scooter app as the „Brightway plugin“. The way Mi Home works is that it provides an API which vendors can use to integrate their hardware, as a plugin. Now, since the 3 Lite is produced by Brightway, their plugin looks and feels different from the Ninebot plugin we’re used to. Nonetheless, the provided functionality and available information is equivalent. By looking into the Brightway plugin script we can locate all register addresses with their respective functions (typos included [sic]).

[Weak=1E, Middle=3C, Strong=5A]
Registers described in the plugin JSON

Command and package format

The Brightway plugin script gives further insight into the command types and how a package is defined.

Command types

A package consists of DATA plus the checksum of DATA. A custom function is used to calculate the checksum. DATA is defined as follows:

  • TYPE is a command type (see table)
  • ADDRESS is a register address (see table)
  • REST is the number of packages remaining, in the case where multiple packages have to be send
  • LEN is the length of the PAYLOAD
  • PAYLOAD is the actual hex values to transmit

Serial numbers

Brightway scooters have a 20-character SN format:
5-digit prefix + '/' + 14-character suffix.

The SN prefix is used to identify different regions. The following table shows all known prefixes for the 3 Lite (valid prefixes start with a ‚3‘) and their restrictions.

SN PrefixRegionNo BacklightOnNo CCSpeedlimit (20 km/h)
All 3 Lite SN regions with their restrictions

SNs are stored on MCU (flash: 0x93FC, image: 0x23f14) and are read and processed by BLE.


The following section identifies all ICs used by the manufacturer.

The dashboard has the label „P2185_Display_V2.0 211126“. It looks almost identical to the „P2223_Display_V1.0.1 20220111“ of the NAVEE S65. Both boards expose SWD ports! The P2223 has all SWD pins labeled, the P2185 has not; but, it’s easy to find out. Check the image below for the correct SWD pin assignment.

P2185 („3 Lite“): SWD pins identified
P2223 (NAVEE S65): [Source] reveals a lot of internal information

With the help of my microscope, I zoomed into one of the small ICs on the dashboard. It turned out the be the „MJA1“ secure chip!

Dashboard comes with a MJA1 secure chip
VendorPart numberFunction
Xiaomi/RealtekMHCB05P-IB module with RTL8762C chipBLE Mesh
XiaomiMJA1 HCIWSecure chip
?????? (hidden underneath display)20-Pin Display driver IC
Dashboard hardware

For the ESC, the part numbers are difficult to read; the board is covered in some sort of „hot glue“. Nonetheless, I could identify all but one part with my microscope.

VendorPart NumberFunction
LinkosemiLKS32MC081C8T8Motor control unit
ThinkisemiP55NF061N-channel power MOSET
Silan (SL)SD4931Current mode PWM controller
SamwinSW062R08E8T6x N-channel enhanced mode MOSFET
Controller (ESC) hardware
The only part I could not identify on ESC

This SOCs!

It took me quite some time to gather all relevant documents to have a clear understanding of the memory mapping and MCU functions. This section summarizes my findings.

BLERTL8762C (MHCB05P)Cortex-M4ARMv7-M128KB384KB512KB
SOC specs: MCU has limited resources – logic is handled by BLE!
AreaStart addressEnd addressSizeFunction
ROM0x00x5 FFFF384KBBootloader, BT-Stack, Flash driver
RAM (Data)0x20 00000x21 BFFF112KBRAM code
> ROM Data0x20 00000x20 2FFF12KBVariables of ROM
> Main Stack0x20 30000x20 37FF2KBMSP
> Patch RAM10x20 38000x20 7BFF17KBVariables/RAM code of Patch
> APP/Data RAM0x20 7C000x21 7FFF65KBVariables/RAM code of APP + Data heap
> Patch RAM20x21 80000x21 BFFF16KBOptional, same as Patch RAM1
RAM (Cache)0x21 C0000x21 FFFF12KBSpeed up SPI Flash R/W
SPI Flash (Cached)0x80 00000x9F FFFF512KBWith cache (faster)
> Reserved0x80 00000x80 0FFFReserved
> OEM Header0x80 10000x80 1FFFConfig: BT address, AES Key, flash layout
> OTA Bank 0Data and code
> OTA Bank 1Same as Bank 0
> FTLAccess flash with logic address
> OTA TempOTA backup
> APP DefinedUser defined
Memory mapping: RTL8762C
AreaStart addressEnd address
RAM0x2000 00000x2000 1FFF
SYS CTRL0x4000 00000x4000 03FF
FLASH CTRL0x4000 04000x4000 07FF
Memory mapping: LKS32MC08x


The UART bus between dashboard and controller is no longer realized by a single wire, but two wires: TX and RX. The MCU-UART is configured as such: baudrate = 19200, data len = 8 bit, stop bit len = 1 bit, LSB, even parity, check disabled, multidrop disabled.

My recordings do not contain messages, but pulses with three possible state values.

UART protocol: only three different values

A single pulse follows the pattern: F[H|L][H|L]

In reality, only two of the four possible combinations are used: FLL, FHL. We can conclude that FLL translates to „0“ and FHL translates to „1“. We can visualize sampled data using a tool called binocle. This helps us identify patterns.

Commands send via UART-TX, visualized with binocle
Data received via UART-RX, visualized with binocle

At the time of conducting the experiments and recording the UART transmission, I didn’t know about the correct parameter stated in the first paragraph. For recording the data, I used a higher baudrate than specified by the MCU. Please leave a comment, if you can verify seeing similar values and patterns in your recording with the correct baudrate set.

If we assume these captures to be accurate, then it means that both the BLE and MCU have a buffer to store incoming packets and then translate them back to messages, as defined in the „Commands and message format“ section.

BLE Encryption / Decryption

The 3 Lite broadcasts the BLE device name „dreame.scooter.epro“. The naming choice isn’t surprising. In my last blog post I explained the relation between Brightway and Dreame.

NrfConnect showing the 3 Lite BLE broadcast data

The 3 Lite provides a BLE GATT service for authentication and another service for TX/RX and broadcasting the button press.

  • Service: 00000101-0065-6C62-2E74-6F696D2E696D
  • TX/RX: 00000100-0065-6C62-2E74-6F696D2E696D
  • Button: 00000102-0065-6C62-2E74-6F696D2E696D

For BLE authentication the Brightway plugin uses MIOT BLE SecurityLock: encryptMessage() and not encryptMessageWithToken(). What’s the difference, you ask?

  • encryptMessage() uses MIOTBluetooth.encryptMessageXiaoMiBLE() -> securityChipEncrypt() [Same as StandardAuthEncrypt()] -> ECDH with secp256r1 curve (we know this)
  • encryptMessageWithToken() uses MIOTBluetooth.miotBleEncrypt() -> miotBleEncrypt() -> BLECipher (MixA, MixB, old crypto)

So, we already know the type of encryption/decryption scheme used. But, the key exchange steps are different in case of a secure chip, where a noticeable difference is the exchange of certificates. I will be looking into the BLE authentication process in detail in an upcoming blog post.

3 Lite: Security oversights

Both the dashboard and controller board expose debugging pins (SWD). These have been left active and allow connection via OpenOCD. I quickly discovered the first security oversight by Brightway: the manufacturer left the content of both SOCs unprotected! I was able to generate full dumps of both BLE and MCU firmware, without any special measures, following the memory mapping tables laid out above.

The second oversight is that the firmware update (OTA) files for both the BLE (called „EEC“) and MCU come unencrypted, meaning that they can be decompiled without further measures. Analysis of these files allowed me draw some of the conclusions in this blog post, e.g., about the UART configuration. I could further observe that the MCU firmware, being very limited in size, contains almost no logic and that its main purpose is motor control.

Last year, Xiaomi introduced firmware signing to secure the BLE firmware from tampering. The BLE/EEC firmware for 3 Lite is signed as well, in accordance to this new Xiaomi standard. However, the MCU firmware is not signed. What’s strange about the MCU firmware update file is that it’s almost three times larger than the MCU flash size. Next to the MCU flash/app content, this file contains one large code section of what seems to be executable DFU loader code and another section of what seems to be executable post-installation / setup code.

3 Lite: Custom firmware?

While the authentication process (pairing with QR code, secure key exchange) doesn’t seem to have any obvious flaws, Brightway has left room for debugging and possible modification of the scooter. It is a misunderstanding that authentication with a secure chip provides absolute security. It’s main safety mechanism is to prevent tampering with the device of someone else, because we don’t have the QR code (certificate) to pair with that (secure chip). The secure chip doesn’t prevent you from hacking your own scooter. This is how it should be.

The BLE Realtek module doesn’t allow writing bytes to the memory-mapped flash directly. The same is true for the MCU SOC. One point could not be fully verified, yet, and will be explored in an upcoming blog post: Should it prove to be true that both the dashboard and controller SOCs are not write-protected in general, it would allow modification of the existing MCU firmware and, after erasing the flash, write the modified firmware to it. If the BLE module is re-writable, it would allow writing and flashing a custom BLE firmware that completely bypasses the secure chip.

Next, since the MCU OTA file isn’t signed, it could allow passing own MCU firmware files to the BLE. This has the hard requirement of having the authentication and update procedure fully reverse-engineered.

Lastly, if it should prove to be correct that the MCU OTA file is packed with executable code that the BLE willingly executes, one could try injecting their own code. This has the potential to defeat every security measure mentioned so far, but also has serious implications for the scooter safety. Proving this requires, again, full knowledge of the authentication and update process.

What about other new Xiaomi models?

Thanks to internal FCC documents we can tell that the hardware of the Xiaomi/Brightway 3 Lite and the Xiaomi/Brightway 4 (non-Pro) model look similar. The MCU has the same amount of pins and we can make out exposed SWD pads. Overall, the board design, including caps and connectors, looks familiar. Chances are, that the 4 Ultra comes with the hardware described in this post.

The Xiaomi/Ninebot 4 Pro uses the same BLE module as the 3 Lite, as disclosed by a Bluetooth certification. This means that the 4 Pro uses the same BLE stack and Xiaomi core libraries for authentication. As for the controller, the 4 Pro uses a STM32 based board in the tradition of previous Ninebot scooters.

Xiaomi/Brightway 3 Lite: MCU with 48 pins underneath blue cap
Xiaomi/Brightway 4: [Source] shows MCU, also with 48 pins, blue cap and SWD pads

In a nutshell, the new Xiaomi models could be pwned as follows:

For 3 Lite + 4 + 4 Ultra, the easy way would be to modify the existing MCU firmware and flash it via SWD. The hard way would be developing and flashing a custom BLE firmware for the (Realtek) BLE module that bypasses the secure chip, with a custom MCU OTA flashing procedure.

For the 4 Pro, there is no easy way at this time. A possible way is to develop and flash a custom ESC firmware for the STM32 board. This firmware could be based, for example, on the OpenSource SmartESC firmware.


In this post, I had a first-look at the hardware, software and security implemented by Brightway, the new Xiaomi scooter supplier, by examining their 3 Lite scooter model.

At the time of writing, I could not discover any security exploits that would warrant a notification of the manufacturer. As for the security flaws, such as missing encryption and read-out protection of the firmware files, I assume that the manufacturer is well aware thereof.

In an upcoming blog posts I will be having a deeper look into the authentication process involving the „MJA1“ secure chip, as well as examining the potential of flashing custom firmware.


Patching APK’s

Ever run into a situation where you can no longer use an older version of an app because it forces you to update? Or the app tries to take money from for unlocking an already built-in but inaccessible „premium feature“?

Patching APK’s consists of these steps:

  1. Unpacking the APK: apktool d -r some.apk -o output_dir
  2. Patching the smali byte-code: Remove or change using your favorite editor
  3. Packing the code back to an APK: apktool b output_dir -o modded.apk
  4. Signing the new APK:
    1. Create key (only needed once): keytool -genkey -v -keystore key.keystore -alias robocoffee -keyalg RSA -keysize 2048 -validity 10000
    2. Sign with key: jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore key.keystore modded.apk robocoffee ~/Android/Sdk/build-tools/<version>/apksigner sign --ks key.keystore modded.apk
  5. Install new APK: adb install modded.apk

Decompiling APK’s

You can decompile even minified / obfuscated APK’s using one of these tools (I prefer the latter). Why would you want to do that? For example, when you like a feature in an App and reverse-engineer how it’s done.

Decompile into Java source code using Procryon:

  1. apktool d -s some.apk -o output_dir
  2. dex2jar output_dir/classes.dex
  3. procyon-decompiler classes-dex2jar.jar -o decompiled_dir

Or alternatively, one tool to rule them all: jadx --deobf some.apk

In my opinion JADX with the built-in deobfuscator gives the most readable results. Note however that some blocks might now be decompiled in either case, leaving you no other option but to read nasty byte-code.