Capturing the goTenna Firmware

The keyboard of a laptop computer.  The laptop has a goTenna and an Adafruit Bluefruit LE Sniffer connected.  A tablet rests on top displaying the goTenna firmware upgrade screen.

Sniffing the Bluetooth Low Energy (BLE) traffic between devices has recently become extremely affordable, with Adafruit’s Bluefruit LE Sniffer being the least expensive option at only $29.95 USD. This is a small price to pay if you’re at all interested in how various BTLE devices communicate. I was, and decided it would be fun to observe the field upgrade process of the goTenna and try to extract the firmware.

Certainly there are easier ways to find the firmware. Directly examining the application they publish in the Apple App Store or Google Play would likely bear fruit. However, pulling the firmware out of thin air, as it were, seemed more interesting.

I plugged the Bluefruit LE Sniffer and the goTenna into my desktop to capture both the BLE packets and the serial output of the goTenna. During the upgrade process the serial output provides some context to the BLE packets I was capturing.

[1814001-9987] FW Exchange initiated.
[1817903-3902] Bluetooth Response Sent.
[1817997-094] NRF Message sent
[1819315-1318] FW data packet received
[1819316-001] seqid 11
[1819316-000] Bluetooth Response Sent.
[1819509-193] NRF Message sent
[1820827-1318] FW data packet received
[1820827-000] seqid 12
[1820828-001] stored pkt 1 of 655 stored length 250 of total length 163652
[1820839-011] Bluetooth Response Sent.
[1821020-181] NRF Message sent
[1822387-1367] FW data packet received
[1822387-000] seqid 13
[1822388-001] stored pkt 2 of 655 stored length 500 of total length 163652
...
[2815323-050] Received positive finalize command.
[2815324-001] stored pkt 655 of 655
[2829807-14483] FIRMWARE DOWNLOAD SUCCESSFUL 1
[2829812-005] Bluetooth Response Sent.
[2829812-000] FIRMWARE DOWNLOAD SUCCESSFUL 2
[2829898-086] NRF Message sent

The Bluefruit LE Sniffer uses Wireshark, which is a very familiar interface for examining traffic. However, I quickly gave up trying to do any sort of protocol analysis directly in Wireshark as each Bluetooth frame was small and there were thousands. I wrote a small Python program to parse the pcap file and print the individual packets. I did observe, in Wireshark, that the goTenna uses the Bluetooth Attribute Protocol for the majority of communication with the host device. I ended up with packets that looked like this:

[jhe@oxcart gotenna-upgrade-pcap-tools]$ ./dump < gotenna-upgrade.pcap
FRAME DIR OP HANDLE CONTENTS
------------------------------------------------------------
00137 M2S 12 1f00   1002080a0000028f00027f4417fd1003
00141 S2M 1d 2100   1002480a252f1003
00141 M2S 12 1f00   1002090b3a1010040000ffffffffffffffffffff
00142 M2S 12 1f00   ffff7efbffff810a3a101008000000600020c108
00142 M2S 12 1f00   00007d3a0100813a01002b0a3a10100810100085
00142 M2S 12 1f00   3a0100893a01008d3a0100913a0100c00a3a1010
00142 M2S 12 1f00   082000953a0100993a01009d3a0100a13a010070
00142 M2S 12 1f00   0a3a1010083000a53a0100a93a0100ad3a010037
00142 M2S 12 1f00   c10100130a3a101008400037c1010037c1010037
00142 M2S 12 1f00   c1010037c10100c40a3a101008500037c1010037
00142 M2S 12 1f00   c1010037c1010037c10100b40a3a101008600037
00142 M2S 12 1f00   c1010037c1010037c1010037c10100a40a3a1010
00142 M2S 12 1f00   08700037c1010037c1010037c1010037c1010094
00143 M2S 12 1f00   0a3a101008800037c1010037c1010037c1010037
00143 M2S 12 1f00   c10100840a3a101008900037c1010037c1010037
00143 M2S 12 1f00   c1010037c101bfb41003

After some scrutiny I noticed a pattern: every message has a start delimiter of 0x1002, a single byte identifier, a single byte rolling sequence number, a payload, and an ending delimiter of 0x1003. Inside a message there is never a single 0x10 without and accomompanying 0x10, which likely means that any 0x10 byte is escaped as 0x1010. This is likely due to 0x10 being a special control character in the protocol. I’ve broken the messages apart to illustrate this:

FRAME DIR OP HANDLE CONTENTS
------------------------------------------------------------
00137 M2S 12 1f00   1002 08 0a 0000028f00027f44 17fd 1003
00141 S2M 1d 2100   1002 48 0a 252f 1003
00141 M2S 12 1f00   1002 09 0b 3a1010040000ffffffffffffffffffff
00142 M2S 12 1f00   ffff7efbffff810a3a101008000000600020c108
00142 M2S 12 1f00   00007d3a0100813a01002b0a3a10100810100085
00142 M2S 12 1f00   3a0100893a01008d3a0100913a0100c00a3a1010
00142 M2S 12 1f00   082000953a0100993a01009d3a0100a13a010070
00142 M2S 12 1f00   0a3a1010083000a53a0100a93a0100ad3a010037
00142 M2S 12 1f00   c10100130a3a101008400037c1010037c1010037
00142 M2S 12 1f00   c1010037c10100c40a3a101008500037c1010037
00142 M2S 12 1f00   c1010037c1010037c10100b40a3a101008600037
00142 M2S 12 1f00   c1010037c1010037c1010037c10100a40a3a1010
00142 M2S 12 1f00   08700037c1010037c1010037c1010037c1010094
00143 M2S 12 1f00   0a3a101008800037c1010037c1010037c1010037
00143 M2S 12 1f00   c10100840a3a101008900037c1010037c1010037
00143 M2S 12 1f00   c1010037c101bfb4 1003
00143 S2M 1d 2100   1002 49 0b 063f 1003

The bulk of the messages I captured have large payloads spanning multiple frames and the identifier of 0x09. These contain the firmware, in 250 byte blocks, that is sent to the goTenna during a field upgrade. I created a Python program to dump the contents of these messages into a new file.

[jhe@oxcart gotenna-pcap-tools]$ ./extract-firmware < gotenna-upgrade.pcap > test.bin
[jhe@oxcart gotenna-pcap-tools]$ ll test.bin
-rw-rw-r-- 1 jhe jhe 165634 Dec 11 21:21 test.bin

However, the file was larger than expected. I knew from the serial log I captured that the firmware should be 163,652 bytes in total. I had to figure out why I had 1,982 extra bytes. After further analysis I determined every message has a two byte CRC-CCITT xmodem style hash at the end, just before the postamble. This CRC is in addition to the CRC that the Bluetooth frame already has.

FRAME DIR OP HANDLE CONTENTS
------------------------------------------------------------
00085 M2S 12 1f00   1002 00 05 000000000000000000 c270 1003
00085 S2M 1d 2100   1002 40 05 000000000000000000 86a5 1003
00091 M2S 12 1f00   1002 00 06 000000000000000000 73bf 1003
00091 S2M 1d 2100   1002 40 06 000000000000000000 376a 1003
00100 M2S 12 1f00   1002 00 07 000000000000000000 1cfa 1003
00100 S2M 1d 2100   1002 40 07 000000000000000000 582f 1003
00101 M2S 12 1f00   1002 00 08 000000000000000000 4a4b 1003
00101 S2M 1d 2100   1002 40 08 000000000000000000 0e9e 1003
00119 M2S 12 1f00   1002 01 09 003fff53edb53e5389 ab57 1003
00120 S2M 1d 2100   1002 c1 09 b44c 1003

I made a change to ignore these two bytes and extracted the firmware again.

[jhe@oxcart gotenna-pcap-tools]$ ./extract-firmware < gotenna-upgrade.pcap > test.bin
[jhe@oxcart gotenna-pcap-tools]$ ll test.bin
-rw-rw-r-- 1 jhe jhe 164330 Dec 11 21:28 test.bin

Closer, but still 678 extra bytes. After making changes to further decode the Bluetooth header and skip frames that did not pass the Bluetooth frame’s own CRC check, as well as remove duplicate frames, I was left with a file 1,711 bytes smaller than expected. This is likely as close as we’ll get given the Bluetooth sniffer is entirely passive and not able to request retransmission of frames it didn’t properly receive. Fully extracting the firmware in this manner would likely require upgrading several devices in order to collect all transmitted firmware blocks with no errors.

[jhe@oxcart gotenna-pcap-tools]$ ./extract-firmware < gotenna-upgrade.pcap > test.bin
[jhe@oxcart gotenna-upgrade-pcap-extractor]$ ll test.bin
-rw-rw-r-- 1 jhe jhe 161941 Dec 13 01:13 test.bin

Next, I examined the binary file containing the firmware (most of it, at least). A quick check with strings reveals many previously undiscovered CLI commands that can be issued to the goTenna over its serial port. Previously, I had been manually fuzzing the CLI commands and documenting my progress. Having access to a mostly completed firmware allowed me to discover new commands I would not have otherwise uncovered.

[jhe@oxcart gotenna-pcap-tools]$ strings test.bin
...
dev_info
PAP\
 Offset %d
SERI
AL no:
get_seria
read_eflash
...

The read_eflash command sounded promising. I tried running this and saw what appeared to be the flash contents in hexidecimal.

[130224] goTenna> read_eflash
[130224] goTenna> 
[130235-1037] cli_parse_command >> read_eflash
[130235-000] printer: cli-cmd = read_eflash

000000: 3A1008000000600020C10800004D2A01
000010: 00512A0100AB0A3A10081000552A0100
000020: 592A01005D2A0100612A0100C00A3A10
000030: 082000652A0100692A01006D2A010071
000040: 2A0100700A3A10083000752A0100792A
000050: 01007D2A0100ABA70100790A3A100840
...

After a little bit of tinkering, I wrote some small utility programs to aid with dumping the flash contents and converting it in to a binary file. Extracting the firmware in this manner is likely the most reliable. Capturing it during the field upgrade was certainly more fascinating to me. In addition, I now also have a foundation for the BLE protocol the goTenna uses, bringing me one step closer to creating my own API layer for interfacing it with a computer. As of writing, goTenna has yet to release their official SDK.

I have also made the utility programs used in this post available for reference.

Return to all posts