Reverse engineering of an OFO smart lock

After the dockless bike sharing firm ofo ceased its service in Milan, both due to the municipality revoking the permissions granted and to vandalism (it has been estimated that around the 10% of the bikes deployed has been damaged), the majority of the bikes has been retired by the company, but a certain number of them - mostly damaged - is still laying around in the city.

These bikes are equipped with a smart padlock which can be unlocked by anyone possessing a smartphone with the firm’s app installed. And here comes the hacker’s curiosity: being always interested in technology, and especially in embedded systems, I always have been wondering what there is under the hood of these padlocks. Thanks to the people belonging to an hackerspace in Milan I satisfied my curiosity: these guys collected some of the now abandoned (and wrecked) ofo bikes with the plan of repairing and then making them available to the other members of the center. Within this process, they detached and put apart the bikes’ padlocks, giving me the ocasion of picking up one of them to do a bit of reverse engineering.

As regards a general description of what’s inside the locking device, this website provides some information: in this page I will extend them with my findings.

Microcontroller’s connections:

The first operation I did was mapping the connections between the nRF51822 microcontroller [1] and the other components, both mounted on the PCB and the ones outside it (motor, buzzer and keypad). The results are summarised here:

Pin number: Port label: Function:
3 P0.30 keypad white leds
4 P0.00
5 P0.01 buzzer
6 P0.02 SW1, locking mechanism
7 P0.03 GPS rx data (usart)
8 P0.04 GPS tx data (usart)
9 P0.05 GSM tx data (usart)
10 P0.06 GSM rx data (usart)
11 P0.07 SW2, locking mechanism
14 P0.08 LIS3DSH I2C sda
15 P0.09 LIS3DSH I2C scl
16 P0.10 LIS3DSH interrupt
17 P0.11 DC motor for unlocking
18 P0.12 keypad button "back"
19 P0.13 keypad button "4"
20 P0.14 keypad button "3"
Pin number: Port label: Function:
21 P0.15 keypad button "2"
22 P0.16 keypad button "1"
25 P0.17
26 P0.18 GSM module reset
27 P0.19 GSM module pwrdown
28 P0.20 GSM module pwrkey
40 P0.21 TTX
41 P0.22 TRX
42 P0.23 red led
43 P0.24 green led
44 P0.25 GPS power enable
45 P0.26 32kHz crystal
46 P0.27 32kHz crystal
47 P0.28
48 P0.29 4V_en (GSM power)

Microcontroller’s firmware:

Coming to the software side, the padlock’s microcontroller comes with a code locking mechanism which (in theory) prevents reading the contents of the flash memory using a debugger. However, the nRF51822 microcontrollers are affected by a vulnerability which allows, through a debugger, to get the content of the flash memory even when the locking mechanism is enabled [2]. Luckily a python script is available [3], providing a good tool which allows to dump the content of the flash exploiting the aforementioned vulnerability: it’s only necessary to connect the microcontroller to a PC via a SWD tool, like ST-Link.

Once the content of the flash memory has been retrieved, is time to analyse the binary dump searching for program image(s). These can be found easily exploiting the fact that unwritten flash memory cells have (hex) value 0xFFFFFFFF and knowing that a program compiled for Cortex M architecture must start with a vector table defining the addresses for the various interrupt vectors. Sifting through the binary dump, emerges that the microcontroller’s flash memory contains four vector tables at the addresses 0x0000, 0x1000, 0x18000 and 0x3B000. The table below contains the addresses associated to each vector in each of the four tables:

Relative position: Vector name: 0x00000 image: 0x01000 image: 0x18000 image: 0x3B000 image:
0x0000 __StackTop 0x000007C0 0x20001500 0x20003EE0 0x20003650
0x0004 Reset_Handler 0x000006D1 0x000164CD 0x00018281 0x0003B16D
0x0008 NMI_Handler 0x000000D1 0x00002225 0x0001829B 0x0003B187
0x000C HardFault_Handler 0x000006B1 0x00016433 0x0001829D 0x0003B189
0x0010 0x00000000 0x00000000 0x00000000 0x00000000
0x0014 0x00000000 0x00000000 0x00000000 0x00000000
0x0018 0x00000000 0x00000000 0x00000000 0x00000000
0x001C 0x00000000 0x00000000 0x00000000 0x00000000
0x0020 0x00000000 0x00000000 0x00000000 0x00000000
0x0024 0x00000000 0x00000000 0x00000000 0x00000000
0x0028 0x00000000 0x00000000 0x00000000 0x00000000
0x002C SVC_Handler 0x00000751 0x0001653D 0x0001829F 0x0003B0D5
0x0030 0x00000000 0x00000000 0x00000000 0x00000000
0x0034 0x00000000 0x00000000 0x00000000 0x00000000
0x0038 PendSV_Handler 0x000000DB 0x00002225 0x000182A1 0x0003B18D
0x003C SysTick_Handler 0x000000E5 0x00002225 0x000182A3 0x0003B18F
0x0040 POWER_CLOCK_IRQHandler 0x000000EF 0x000165A9 0x000182A5 0x0003B191
0x0044 RADIO_IRQHandler 0x000000F9 0x000165AF 0x000182A5 0x0003B191
0x0048 UART0_IRQHandler 0x00000103 0x00002225 0x0001EBC1 0x0003B505
0x004C SPI0_TWI0_IRQHandler 0x0000010D 0x00002225 0x0001E7F5 0x0003B191
0x0050 SPI1_TWI1_IRQHandler 0x00000117 0x00002225 0x000182A5 0x0003B191
0x0054 0x00000121 0x00002225 0x00000000 0x00000000
0x0058 GPIOTE_IRQHandler 0x0000012B 0x00002225 0x0001C3D5 0x0003B191
0x005C ADC_IRQHandler 0x00000135 0x00002225 0x0001B74D 0x0003B191
0x0060 TIMER0_IRQHandler 0x0000013F 0x000165B5 0x000182A5 0x0003B191
0x0064 TIMER1_IRQHandler 0x00000149 0x00002225 0x000182A5 0x0003B191
0x0068 TIMER2_IRQHandler 0x00000153 0x00002225 0x000182A5 0x0003B191
0x006C RTC0_IRQHandler 0x0000015D 0x000165BB 0x000182A5 0x0003B191
0x0070 TEMP_IRQHandler 0x00000167 0x00002225 0x000182A5 0x0003B191
0x0074 RNG_IRQHandler 0x00000171 0x000165C1 0x000182A5 0x0003B191
0x0078 ECB_IRQHandler 0x0000017B 0x0001C765 0x000182A5 0x0003B191
0x007C CCM_AAR_IRQHandler 0x00000185 0x000165CD 0x000182A5 0x0003B191
0x0080 WDT_IRQHandler 0x0000018F 0x00002225 0x0001ED15 0x0003B609
0x0084 RTC1_IRQHandler 0x00000199 0x00002225 0x0001E7B9 0x0003B465
0x0088 QDEC_IRQHandler 0x000001A3 0x00002225 0x000182A5 0x0003B191
0x008C LPCOMP_IRQHandler 0x000001AD 0x00002225 0x000182A5 0x0003B191
0x0090 SWI0_IRQHandler 0x000001B7 0x00002225 0x0001E809 0x0003B485
0x0094 SWI1_IRQHandler 0x000001C1 0x00002225 0x000182A5 0x0003B191
0x0098 SWI2_IRQHandler 0x000001CB 0x00002225 0x0001E811 0x0003B48D
0x009C SWI3_IRQHandler 0x000001D5 0x00002225 0x000182A5 0x0003B191
0x00A0 SWI4_IRQHandler 0x000001DF 0x000165D3 0x000182A5 0x0003B191
0x00A4 SWI5_IRQHandler 0x000001E9 0x000165D9 0x000182A5 0x0003B191
0x00A8 0x000001F3 0x00002225 0x00000000 0x00000000
0x00AC 0x000001FD 0x00002225 0x00000000 0x00000000
0x00B0 0x00000207 0x00002225 0x00000000 0x00000000
0x00B4 0x00000211 0x00002225 0x00000000 0x00000000
0x00B8 0x0000021B 0x00002225 0x00000000 0x00000000
0x00BC 0x00000225 0x00002225 0x00000000 0x00000000

Currently I’m decompiling and analysing the binary image contained in the flash memory to find what each executable does, I’ll progressively publish here the results.

References:

[1] nRF51822 datasheet

[2] Firmware dumping technique for an ARM Cortex-M0 SoC

[3] Python script for dumping firmware from read-back protected nRF51 chips