I hacked MiBand 3, and here is how I did it Part II — Reverse Engineering to upload Firmware and Resources Over the Air
This Article requires an understanding of:
A while ago I posted an article on how I reverse engineered a BLE Fitness Tracker to send notifications, place a phone call notification, get real-time heartbeat data, resetting the date and much more. Once I was done with that, I was wondering if updating the firmware could be possible or not.
Once again, My Aim?
I really had no any idea if uploading the Firmware Over the Air could be possible or not, but I badly wanted to show this evil icon on my fitness tracker. 💀
So the first step was to get the firmware.
So what is Firmware and why I was excited to play with?
A firmware is a piece of Software that runs on embedded CPU, typically written in C, and is compiled into binary that has been loaded into the device using a programmer or wired connection.
Earlier, I had noticed many times that if the MiBand is corrupted or bricked when connected to the MiFit application, it uploads firmware automatically. I then downloaded the MiFit application apk file (I use Apkpure to download apk files) and decompiled it using apktool. I was pretty much sure that in the worst case I would at least get URL for the firmware update or at the best case the firmware file itself.
Interestingly, inside the assets directory, I found something! 😁
There were many files with extension .fw and .res. Did a google search and found that Mili_wuhan.fw and Mili_wuhan.res are the two files I am looking for. The .fw file had the firmware, string resource, software version number and much more. The .res file has all the resource images and animations (in the form of sequential images). I used wxHexEditor 0.23 Beta for Linux to have a look at these hex files. As soon as I opened the .fw file this is how I was greeted with 😆
For a while, I thought this must be the wrong file but then I saw the software revision number. I remember the very exact software revision number on MiFit app when paired.
But there was nothing as such when I opened the res file. Now I had firmware file and a resource file waiting to be uploaded 😎
I then connected the Fitness tracker to the MiFit app and hunt for firmware upload service started. I had mentioned a powerful app/generic tool in my earlier article called nRF Connect that allows you to scan and explore your low energy devices and even communicate with them! So I used the nRF Connect app to look for device firmware upload (DFU) service. I was looking for the device firmware upload service and there was no any service named either DFU or anything as such. This is where reality vs expectation comes in. 😆
There were many services named “Unknown Services”. And it was really difficult to figure out what each service does. I again did a google search for the Unknown Service UUID “00001530–0000–3512–2118–0009af100700” and found that it’s responsible for firmware update services both “00001531–0000–3512–2118–0009af100700”, “00001532–0000–3512–2118–0009af100700” and were responsible for firmware/resource update characteristic.
Now I already had service and characteristic for the firmware update. Hit and trial for many days, I figured that how firmware update works in MiBand. The very first thing was to Authenticate the MiBand which is already explained in Part I.
The next step was to Initialize the firmware/resource Update On Characteristic 1531 with write command of 4-byte
\x01 + fileSize in Hex(3-byte)
But, for the resource, its 5-byte!
\x01 + fileSize in Hex(3-byte) + \x02
Last byte \x02 is for letting the firmware update service know that it's a resource and not the firmware file.
There is something very important here in fileSize. Consider if your file size is 389036, hex of it will be 0x5EFAC. But if you notice, it won’t accept this value, instead it accepts 0xACEF05. I thought I could easily use string reverse and write the value. Poor me, little I knew if I use any string manipulation the type will be converted to string and interestingly char.write() function would convert string again to hex! So it would be logically hex of hex! 😆 😆 😆
Banged my head for many days not knowing the Endianness.
What is Endianness?
Endianness refers to the sequential order in which bytes are arranged into larger numerical values when stored in memory or when transmitted over digital links. Endianness is of interest in computer science because two conflicting and incompatible formats are in common use: words may be represented in big-endian or little-endian format, depending on whether bits or bytes or other components are ordered from the big end (most significant bit) or the little end (least significant bit).
So here 0x5EFAC is in big endian and you are supposed to convert it to the little endian format 0xACEF05.
For that I used python structs. Here is a sample snippet if you wish to know how python structs works.
So the fileSize must be in little endian format.
Now MiBand will wait for firmware/resource to be pushed. And this is how it looks
After this, you have to write the command again to the same “1531” Characteristic which is of 1 byte for Start Data
Now let’s send the firmware/resource! MiBand can receive a maximum of 20 bytes for a single command. The firmware/resource has to be transmitted in parts of 20 bytes at maximum. I honestly do not know why it's being transmitted 20 bytes at a time, but I believe it could be because this is the optimal length for calculating CRC16 hash to verify the integrity of the transmitted firmware. The firmware/resource has to be written on Characteristic “1532”.
Once all bytes are transmitted wait for the notification and update sync command has to be sent on Characteristic “1531”. Write Command for the update sync
Once update sync command is sent, the most important thing is to send the checksum. If the checksum is not sent or is not valid then firmware/resource update fails!
What is checksum and why is it so important?
A checksum is calculated value that is used to determine the integrity of data during the transmission so that man in the middle attack does not happen. Checksum serves as a unique identifier for the data that is transmitted. If the data is changed then so does the checksum value as well. This makes it super easy to verify the integrity of data.
To test the data integrity, the sender of the data calculates the checksum value. When the receiver receives the data, it does calculate the checksum as well. Now sender sends the checksum to the receiver and asks “Hey this is the checksum I’ve got, is it same with you as well?” If the checksum value matches then the receiver has the high degree of confidence that the data received is proper. If the checksum matches, then firmware/resource is accepted!
Bluetooth low energy (Bluetooth 4.0) does not perform error correction but can only perform error detection. Bluetooth 5.0 introduces error correction.
There are several types of checksum available but Bluetooth uses Cyclic Redundancy Check(CRC). In MiBand I found that it was using CRC16 checksum.
I found this A quick guide to CRC very useful, I then wrote an algorithm to calculate CRC16.
Once the CRC is calculated, write the checksum to Characteristic “1531” of 3 bytes.
The checksum must begin with \x04 and your checksum value
\x04 + checksum
If the checksum matches the resource will be accepted and updated. But for firmware, you need to send reboot command as well.
On Characteristic “1531” send \x05 for the reboot.
And yes, the firmware update is done!
Now let’s open wxHexEditor 0.23 Beta for Linux and let's change software version and Device name. And remember this is just a proof of concept. You definitely can do much more than this. I just changed the device name and software version and here it is!
Now let’s have a look at the most fun part, showing skull icon on the MiBand 😆
As I mentioned that you could easily get the original resource file by decompiling the miFit app. Once you get the res file, there is a tool available for Windows called AmazfitBipTools which allows res file to unpack and convert to png, also once you edit the png, it allows you to repack and convert it to res file. Simply drag the res file on WatchFace.exe and it unpacks them, once unpacked, edit those image file, draw whatever you wish to 😆 and repack them. For repacking drag and drop the directory that was created during the unpacking process and you will get the .res file! And upload the resource file using the above procedure.
At the end of the video below, I have shown how to unpack and pack the resources file.
And, How about the skull icon? 😉
Yes! It’s here 💀
Now to make things much more simpler, I developed a tool to automate this whole process of uploading the firmware/resource. You just have to enter the firmware file name or resource file name.
Again, here is how the tool looks.
As always, the code is available on Github.
Link to Github: https://github.com/yogeshojha/MiBand3
This is how you could reverse engineer a Fitness Tracker and send a Firmware Update Over the air (OTA)!
Firmware update over the air is really a cool feature that is found nearly on all embedded devices. I demonstrated how this feature can be exploited to allow attackers to inject malicious firmware modifications into embedded devices. The problem here is hardware manufacturers do not cryptographically sign the firmware embedded in their systems nor include authentication features in their devices that can recognize if the firmware being pushed is signed by them or not. They literally accept the firmware from anyone!
The solution for this could be that hardware manufacturers should design firmware and firmware update they distribute to be cryptographically signed.
If they implement these security measures, again the cost of the devices just increases. But these companies have to sell a lot of them at low cost, and they just ignore it!
If you enjoyed it please bang that clap as much as you can and happy reverse engineering! 😆
If you are there on LinkedIn, let’s get connected: Yogesh Ojha