with thanks to Alexei Karpenko
Reverse engineering a Bluetooth device is rather straightforward, but quite a few good neighbors don't know where to begin. This article demonstrates exactly how an Android client was reverse engineered in order to produce open source clients in Python and QT Mobility. I'm writing with the assumption that you are trying to reverse engineering your own device, which is similar but not identical to mine. As this is an introductory guide, I'll stay clear of any code reverse engineering, sticking only to network traffic.
The subject of this article is the Spot Connect, which transmits one-way text messages and GPS coordinates by L-band to the GlobalStar satellite constellation. These messages are then forwarded by email or SMS. Except in its emergency mode, the device is operated through Bluetooth by a smart phone. Thanks to Android's use of the Bluez bluetooth stack, it is rather easy to get the necessary traffic dumps.
Kind thanks are due to Alexei Karpenko (Natrium42) for his article on SPOT Reverse Engineering, which covers the original SPOT unit in excellent and thorough detail. It was his article that got me looking at the Spot Connect, and his description of the GPS format saved me quite a bit of travel for sample collection.
The first step is to load the official client onto a rooted Android phone, in my case a Nexus S. I had to swap SIM cards as my Brazilian one put me in a region of the Android market that didn't have the application. Switching to a Swiss card fixed this, and a moment later the app was installing.
The Spot Connect uses RFCOMM, which is Bluetooth's alternative to a TCP socket or a UART. As it is easy to prototype and always delivers packets in order, RFCOMM has become the standard way of implementing custom protocols. To sniff the traffic before knowing the mode, we'll use hcidump running in a debugging shell of the phone. For this, run adb shell hcidump -X | tee spotlog.txt on your workstation, send a transmission, and watch the result in the log.
The message being sent stands out as ASCII, of course, so it's the first thing to look for. With no knowledge of the HCI protocol, you can still be sure that you have a cleartext recording.
35 00 40 00 0b ef 63 aa 31 26 01 00 01 00 01 4d 5.@...c.1&.....M
72 2e 20 57 61 74 73 6f 6e 2c 20 63 6f 6d 65 20 r. Watson, come
68 65 72 65 2e 20 49 20 77 61 6e 74 20 74 6f 20 here. I want to
73 65 65 20 79 6f 75 2e 9a see you..
From Alexei's article, you can expect that frames inside of RFCOMM will begin with 0xAA, followed by a length, followed by a verb and the objects. These bytes will be wrapped in padding on the outbound end, and they'll be fragmented on the inbound end. Sure enough, these are the bytes that come before the word ``Watson'':
01 00 01 00 01 Flags (OK, Check In)
4d 72 2e 20 57 ASCII Message (abbreviated)
Counting 0x31 bytes out, notice that the packet ends exactly on a byte of the ASCII message, without a checksum! By looking for bytes of AA and searching for length, with allowances for packet fragmentation and the RFCOMM wrapper, it becomes possible to decode every command and its matching response.
Be aware that responses will be fragmented more than transmissions. If you need to reverse engineer longer transactions or have a more complete log, it will be handy to have a script to reassembly from the HCI frames. In those cases, toss together a proper HCI decoder to get a more accurate interpretation of the records.
Looking through the entire log, it the protocol appears to be as follows. First, the client queries the Device ID with verb 0x01, using the exact same format as Alexei's article. Then it uses verb 0x25 to query the last known position of the device, which will be returned in the style that Alexei reverse engineered from the original unit. Use pen and paper to decode these transactions from my Python client.
With these recordings in hand, the complete language can now be described and implemented. Luckily, three verbs make for a quick implementation!
I use py-bluez for prototyping such implementations, as its rfcomm-client.py example is simple enough to get a working client in minutes. As py-bluez is specific to Linux, Mac users might prefer lightblue.
For simplicity, cut the UUID code or switch it to RFCOMM's UUID, which is 00001101-0000-1000-8000-00805F9B34FB. For a list of all services on a device, run 'sdptool records $adr'. This only lists those which are publicly announced by SDP, the Service Discovery Protocol. To scan for unadvertised services, try BT Audit from Collin Mulliner.
0x01 -- Get ID
A minimal test client will just test the serial number of the device. To do this, simply send "\xAA\x03\x01" and then catch the reply with verb 0x01. Bytes 3, 4, 5, and 6 of the reply will contain the serial number in Big Endian notation. For this first implementation, commands and their responses may be handled synchronously for simplicity.
Where self.tx() takes a frame as its input and returns the response, this is implemented in Python as the following. What could be simpler?
0x25 -- Get Last Position
Similar in calling convention, the 0x25 verb requests the last known GPS position of the device. The coordinate format is exactly the same as in Alexei Karpenko's Spot Hacking article, consisting of three bytes apiece to describe latitude and longitude. The following is my C++ code for parsing the position data, which has already been requested as "\xAA\x03\x25".
0x26 -- Transmit Text
Transmitting text is just as easy, with the Spot Connect handling all the work after a message has been loaded. The following is Python code to transmit a short text message with the OK message-code. This lacks length checks and doesn't support the changing of flags, but it will work perfectly well for a test.
After the device receives this command, it will reply with an acknowledgment and then begin to attempt transmissions at irregular intervals. Each transmission consists of a number of fragments, such that the packet can be reassembled so long as one copy of each fragment makes it through. If you have a clear view of the sky and have configured the first destination to be your email address, you should receive a notification within a few minutes. If you don't receive a notification by the time the mailbox icon has ceased blinking, then the transmission failed.
These three verbse--0x01, 0x25, and x026--are sufficient to implement a minimal client for the Spot Connect. If you'd care to help out, it would be useful to have more documentation for the flags of the 0x26 verb, as well as documentation for 0x52, 0x40, and 0x38. By scanning and listening for error codes, it should be possible to get a complete list of those verbs that are unused by the Android application.
You can find my Python client at https://github.com/travisgoodspeed/pyspot . It ought to run as-is on Linux with py-bluez, including the Nokia N900.
A Graphical Client
Now that the protocol has been sufficiently well documented to have a Python implementation, it is worthwhile to rewrite it as a GUI. In my case, I wanted a QT Mobility client for my Nokia N9. You can find my work in progress at https://github.com/travisgoodspeed/goodspot.
If hcidump isn't available for your platform, you might try Sniffing with a USRP or reflashing a dongle to become a commercial sniffer. For a jailbroken iPhone, see the iPhone Wiki's documentation.
Another option would be to create a Bluetooth proxy, relying on the slim authentication performed in the protocol. In this case, the proxy would open all relevant port to the device being reverse engineered, ferrying commands back and forth as a way to record them. You might also need to experiment with changing the device class and, in the case of iOS devices, there is also a lockout sequence that must be implemented.
If none of that works for your device, you could sniff the UART lines that come from the bluetooth module, shown here on the left board. This particular module happens to be a BT23, and Page 8 of BT23_Datasheet.pdf shows that pins 14 and 13 should be tapped to get a communications log.
As a last resort, you could always ask for documentation. I didn't bother with this because of my own impatience, but for some devices, such as the Metawatch, documentation is freely available. More than once, a neighborly vendor has been so kind as to give me the source code or documentation just to be neighborly.
This article will be followed with one on the physical layer protocol of the SPOT, which I've been able to sniff thanks to some kind help from Michael Ossmann. For a preview of that technique, you are welcome to stalk my SPOT Connect Set on Flickr. The most neighborly of these shows individual bits from a FunCube Dongle recording of the transmission. It's cleartext, of course.
Replacement firmware is also a possibility. The Spot Connect uses an MSP430F5xx microcontroller with the standard USB bootloader, using a Java client on Windows and an Objective C client on OS X. The firmware itself is downloaded by HTTPS, and a copy could be acquired either by a MITM attack on HTTPS or by asking the bootloader politely, using the password that is to be found within the firmware update. Be careful when doing this to test on a unit without a service contract, as service cannot be moved from one unit to another and bricking is a distinct possibility.
I hope that this article has given you a decent overview of the methods for reverse engineering Bluetooth RFCOMM devices. While my subject was the Spot Connect, these methods would apply equally well to something like a GPS, the Metawatch, Bluetooth chat applications, and multiplayer games. Other brands of Bluetooth satellite communicators are available, and open documentation for them would be quite handy. For a list of a few thousand potential targets, search the Bluetooth SIG's Gadget Guide.