A couple of years ago, upon suggestion by a reader of this blog, I switched my glucometer to an Accu-Chek Mobile by Roche. I have not even looked into reverse engineering it at the time, as the killer application of that meter was not needing software at all. Indeed all the data is made available over a USB Mass Storage interface as CSV and graphs.
While there is a secondary interface available on the device to connect to software on PC, you had to order a copy of the download software online and receive it physically to be able to download the data to a computer, which I still find kind of silly for a device that is designed to have a standard USB connector.
Things changed recently, as Roche joined Abbott (and probably more to come) on the bandwagon of “show us yours”: upload your blood glucose reading to their cloud systems, and they will help you managing diabetes. I guess this is what happens when people are not waiting. I’m not particularly fond of uploading my health information to the cloud, but signing up for this service also meant being able to grab a proper chatter of the protocol over USB.
The software I’m using right now for snooping over the USB connection is USBlyzer which is proprietary and closed source — I’m not proud, but it gets its job done. I have been thinking of switching of a hardware snooping solution, and I bought a BeagleBone Black to use for that, but I have not started working on that due to time.
So instead, I have collected over time a set of libraries and utilities that operate on the CSV files that the software export (particularly as with version 2.2 they are actually quite well written, the older version was messier to post-process). I should look into publishing this collection, and I promise I’ll give it a try to publish before end of this year.
One of the tools I have prints out the “chatter”, coded hexdumps with include direction information to make it easier for me to read the stream. The first run of it was a bit noisy, but a quick check told me that what I’m interested in is bulk transfers (rather than control transfers, which are the most basic), so I filtered for those only, and then the first thing became obvious very quickly.
The maximum length of the bulk transfers in the trace is 64 bytes, which corresponds to the maximum size of bulk transfers for full speed endpoints. But the chatter shows the device sending multiple packets back from a single command, which is not unusual, as you can’t fit much blood sugar data in 64 bytes. And as usual when there is fragmentation, the actual data transfer size is coded somewhere at the beginning of the message.
0023 >>>> 00000000: E3 00 00 2C 00 03 50 79 00 26 80 00 00 00 80 00 ...,..Py.&...... 0023 >>>> 00000010: 80 00 00 00 00 00 00 00 80 00 00 00 00 08 00 60 ...............` 0023 >>>> 00000020: 19 00 01 08 00 00 00 00 00 01 01 01 00 00 00 00 ................ 0025 <<<< 00000000: E7 00 01 0A 01 08 00 00 01 01 01 02 00 00 FF FF ................ 0025 <<<< 00000010: FF FF 0D 1C 00 F8 50 00 00 05 00 F2 00 06 00 01 ......P......... 0025 <<<< 00000020: 00 04 00 24 09 2F 00 04 00 02 71 BC 0A 46 00 02 ...$./....q..F.. 0025 <<<< 00000030: F0 40 09 96 00 02 08 52 0A 55 00 0C 00 02 00 08 .@.....R.U...... 0025 <<<< 00000040: 09 90 00 08 0A 4C 00 02 00 06 00 02 00 04 00 24 .....L.........$ 0025 <<<< 00000050: 09 2F 00 04 00 02 71 D0 0A 46 00 02 F0 40 09 96 ./....q..F...@.. 0025 <<<< 00000060: 00 02 08 52 0A 55 00 0C 00 02 00 08 09 90 00 08 ...R.U.......... 0025 <<<< 00000070: 0A 4C 00 02 00 05 00 03 00 03 00 1E 09 2F 00 04 .L.........../.. 0025 <<<< 00000080: 00 80 71 D8 0A 46 00 02 F0 40 0A 55 00 0C 00 02 ..q..F...@.U.... 0025 <<<< 00000090: 00 08 09 90 00 08 0A 66 00 02 00 05 00 04 00 03 .......f........ 0025 <<<< 000000A0: 00 1E 09 2F 00 04 00 80 72 48 0A 46 00 02 F0 48 .../....rH.F...H 0025 <<<< 000000B0: 0A 55 00 0C 00 02 00 08 09 90 00 08 0A 49 00 02 .U...........I.. 0025 <<<< 000000C0: 00 3D 00 05 00 08 00 46 0A 4D 00 02 98 20 09 43 .=.....F.M... .C 0025 <<<< 000000D0: 00 02 00 00 09 41 00 04 00 00 17 70 09 44 00 04 .....A.....p.D.. 0025 <<<< 000000E0: 00 00 01 E5 09 53 00 02 00 00 0A 57 00 12 00 10 .....S.....W.... 0025 <<<< 000000F0: 50 61 74 69 65 6E 74 20 52 65 73 75 6C 74 73 00 Patient Results. 0025 <<<>>> 00000000: E7 00 00 0E 00 0C 00 01 01 03 00 06 00 00 00 00 ................ 0047 >>>> 00000010: 00 00 .. 0049 <<<< 00000000: E7 00 00 F6 00 F4 00 01 02 03 00 EE 00 00 00 08 ................ 0049 <<<< 00000010: 00 E8 09 28 00 0E 00 06 52 6F 63 68 65 00 00 04 ...(....Roche... 0049 <<<< 00000020: 31 32 30 35 09 84 00 0A 00 08 00 60 19 04 B5 1B 1205.......`.... 0049 <<<< 00000030: DF 5C 0A 44 00 02 40 00 09 2D 00 78 00 04 00 74 ..D..@..-.x...t 0049 <<<< 00000040: 00 01 00 00 00 18 73 65 72 69 61 6C 2D 6E 75 6D ......serial-num 0049 <<<< 00000050: 62 65 72 3A 20 30 30 31 38 32 36 36 35 32 00 04 ber: 001826652.. 0049 <<<< 00000060: 00 00 00 14 73 77 2D 72 65 76 20 4D 45 3A 20 56 ....sw-rev ME: V 0049 <<<< 00000070: 30 33 2E 31 33 20 20 20 00 05 00 00 00 16 66 77 03.13 ......fw 0049 <<<< 00000080: 2D 72 65 76 69 73 69 6F 6E 3A 20 56 30 33 2E 39 -revision: V03.9 0049 <<<< 00000090: 30 20 20 20 00 06 00 00 00 1A 70 72 6F 74 6F 63 0 ......protoc 0049 <<<< 000000A0: 6F 6C 2D 72 65 76 69 73 69 6F 6E 3A 20 52 50 43 ol-revision: RPC 0049 <<<< 000000B0: 20 31 2E 30 09 87 00 08 20 17 02 24 21 07 00 00 1.0.... ..$!... 0049 <<<< 000000C0: 0A 45 00 10 C0 00 1F 00 FF FF FF FF 00 64 00 00 .E...........d.. 0049 <<<< 000000D0: 00 00 00 00 0A 4B 00 16 00 02 00 12 02 01 00 08 .....K.......... 0049 <<<< 000000E0: 01 05 00 01 00 02 20 11 02 02 00 02 00 00 0A 5A ...... ........Z 0049 <<<< 000000F0: 00 08 00 01 00 04 10 11 00 01 ..........
As you can see in this particular exchange, bytes at offset 2-3 represent a (big-endian) length for the whole transfer. You just keep reading until that is complete.
While I have not (yet at the time of writing) figured out what the command and response actually convey, one thing that is kind of obvious is that there is some kind of (type-)length-value encoding at play, although in a bit of a funny way.
All records with type 0xE700 appear to have two-level lengths, as you can see on the two responses and the second command: in red it’s the length of the packet, in magenta the same length minus two (which matches the size of the length itself). There are also a number of strings, some zero terminated (Roche
) and some not (1205
), but still encoded with a 16-bit length in front of them.
The next thing to figure out in these cases is whether there is a checksum or not. For effectively all the meters I have reverse engineered up to now, except for maybe the cheap Korean one, include a checksum somewhere. I checked the chatter and found that there are a number of packets that appear to include the same information, but adding a checksum to the packet showed them different.
Once I dumped the (recomposed) packets to binary files, I noticed a number of packets with the same sizes. hexdump
, wdiff
and colordiff
make it very easy to tell what changed between them. It didn’t quite look like a cryptographic checksum, as changing one byte would replace it with a very different number, but it didn’t quite match up with a “dumb” checksum of all the bytes values.
A couple of diff later, it become obvious.
[flameeyes@saladin Accu-Chek]$ wdiff <(hexdump -C 0039-0034-in) <(hexdump -C 0049-0046-in) | colordiff 00000000 e7 00 00 f6 00 f4 00 [-2d-] {+01+} 02 03 00 ee 00 00 00 08 [-|.......-........|-] {+|................|+} 00000010 00 e8 09 28 00 0e 00 06 52 6f 63 68 65 00 00 04 |...(....Roche...| 00000020 31 32 30 35 09 84 00 0a 00 08 00 60 19 04 b5 1b |1205.......`....| 00000030 df 5c 0a 44 00 02 40 00 09 2d 00 78 00 04 00 74 |..D..@..-.x...t| 00000040 00 01 00 00 00 18 73 65 72 69 61 6c 2d 6e 75 6d |......serial-num| 00000050 62 65 72 3a 20 30 30 31 38 32 36 36 35 32 00 04 |ber: 001826652..| 00000060 00 00 00 14 73 77 2d 72 65 76 20 4d 45 3a 20 56 |....sw-rev ME: V| 00000070 30 33 2e 31 33 20 20 20 00 05 00 00 00 16 66 77 |03.13 ......fw| 00000080 2d 72 65 76 69 73 69 6f 6e 3a 20 56 30 33 2e 39 |-revision: V03.9| 00000090 30 20 20 20 00 06 00 00 00 1a 70 72 6f 74 6f 63 |0 ......protoc| 000000a0 6f 6c 2d 72 65 76 69 73 69 6f 6e 3a 20 52 50 43 |ol-revision: RPC| 000000b0 20 31 2e 30 09 87 00 08 20 17 02 24 21 [-06 53-] {+07 00+} 00 | 1.0.... [-..$!.S.|-] {+..$!...|+} 000000c0 0a 45 00 10 c0 00 1f 00 ff ff ff ff 00 64 00 00 |.E...........d..| 000000d0 00 00 00 00 0a 4b 00 16 00 02 00 12 02 01 00 08 |.....K..........| 000000e0 01 05 00 01 00 02 20 11 02 02 00 02 00 00 0a 5a |...... ........Z| 000000f0 00 08 00 01 00 04 10 11 00 01 |..........| 000000fa [flameeyes@saladin Accu-Chek]$ wdiff <(hexdump -C 0039-0034-in) <(hexdump -C 0073-0068-in) | colordiff 00000000 e7 00 00 f6 00 f4 00 [-2d-] {+02+} 02 03 00 ee 00 00 00 08 [-|.......-........|-] {+|................|+} 00000010 00 e8 09 28 00 0e 00 06 52 6f 63 68 65 00 00 04 |...(....Roche...| 00000020 31 32 30 35 09 84 00 0a 00 08 00 60 19 04 b5 1b |1205.......`....| 00000030 df 5c 0a 44 00 02 40 00 09 2d 00 78 00 04 00 74 |..D..@..-.x...t| 00000040 00 01 00 00 00 18 73 65 72 69 61 6c 2d 6e 75 6d |......serial-num| 00000050 62 65 72 3a 20 30 30 31 38 32 36 36 35 32 00 04 |ber: 001826652..| 00000060 00 00 00 14 73 77 2d 72 65 76 20 4d 45 3a 20 56 |....sw-rev ME: V| 00000070 30 33 2e 31 33 20 20 20 00 05 00 00 00 16 66 77 |03.13 ......fw| 00000080 2d 72 65 76 69 73 69 6f 6e 3a 20 56 30 33 2e 39 |-revision: V03.9| 00000090 30 20 20 20 00 06 00 00 00 1a 70 72 6f 74 6f 63 |0 ......protoc| 000000a0 6f 6c 2d 72 65 76 69 73 69 6f 6e 3a 20 52 50 43 |ol-revision: RPC| 000000b0 20 31 2e 30 09 87 00 08 20 17 02 24 21 [-06 53-] {+07 02+} 00 | 1.0.... [-..$!.S.|-] {+..$!...|+} 000000c0 0a 45 00 10 c0 00 1f 00 ff ff ff ff 00 64 00 00 |.E...........d..| 000000d0 00 00 00 00 0a 4b 00 16 00 02 00 12 02 01 00 08 |.....K..........| 000000e0 01 05 00 01 00 02 20 11 02 02 00 02 00 00 0a 5a |...... ........Z| 000000f0 00 08 00 01 00 04 10 11 00 01 |..........| 000000fa [flameeyes@saladin Accu-Chek]$ wdiff <(hexdump -C 0039-0034-in) <(hexdump -C 0087-0084-in) | colordiff 00000000 e7 00 00 f6 00 f4 00 [-2d-] {+04+} 02 03 00 ee 00 00 00 08 [-|.......-........|-] {+|................|+} 00000010 00 e8 09 28 00 0e 00 06 52 6f 63 68 65 00 00 04 |...(....Roche...| 00000020 31 32 30 35 09 84 00 0a 00 08 00 60 19 04 b5 1b |1205.......`....| 00000030 df 5c 0a 44 00 02 40 00 09 2d 00 78 00 04 00 74 |..D..@..-.x...t| 00000040 00 01 00 00 00 18 73 65 72 69 61 6c 2d 6e 75 6d |......serial-num| 00000050 62 65 72 3a 20 30 30 31 38 32 36 36 35 32 00 04 |ber: 001826652..| 00000060 00 00 00 14 73 77 2d 72 65 76 20 4d 45 3a 20 56 |....sw-rev ME: V| 00000070 30 33 2e 31 33 20 20 20 00 05 00 00 00 16 66 77 |03.13 ......fw| 00000080 2d 72 65 76 69 73 69 6f 6e 3a 20 56 30 33 2e 39 |-revision: V03.9| 00000090 30 20 20 20 00 06 00 00 00 1a 70 72 6f 74 6f 63 |0 ......protoc| 000000a0 6f 6c 2d 72 65 76 69 73 69 6f 6e 3a 20 52 50 43 |ol-revision: RPC| 000000b0 20 31 2e 30 09 87 00 08 20 17 02 24 [-21 06 53-] {+20 55 15+} 00 | 1.0.... [-..$!.S.|-] {+..$ U..|+} 000000c0 0a 45 00 10 c0 00 1f 00 ff ff ff ff 00 64 00 00 |.E...........d..| 000000d0 00 00 00 00 0a 4b 00 16 00 02 00 12 02 01 00 08 |.....K..........| 000000e0 01 05 00 01 00 02 20 11 02 02 00 02 00 00 0a 5a |...... ........Z| 000000f0 00 08 00 01 00 04 10 11 00 01 |..........| 000000fa
It may become much clearer if you go back at the first dump and observe the part highlighted in blue: it’s 8 bytes that represent 20 17 02 24 21 07 00 00
. To help understanding this, you should know that I was looking at this around 9pm on February 24th, 2017. Indeed, these bytes effectively represent the date and time in binary-coded decimal, which is not something I was expecting to see, but make sense.
Once you know this, it’s easy to tell that there is no checksum in the messages, and that is one less problem to worry about. Indeed, when looking for the “big” packets, I could find the telltale representation of fixed-size records with what looked like (and I confirmed being) glucometer readings (in mg/dL, even though the device is mmol/l based).
If you’re wondering why the peculiar change in time on the last part of the diff, the reason is quite simple: the software noted that the time on the device didn’t match the time on the computer, and asked me to sync it. Which means I also know the command to set the time now.
Looking at the commands, there are a few more things that are interesting to see:
0037 >>>> 00000000: E7 00 00 0E 00 0C 00 2D 01 03 00 06 00 00 00 00 .......-........ 0037 >>>> 00000010: 00 00 .. 0047 >>>> 00000000: E7 00 00 0E 00 0C 00 01 01 03 00 06 00 00 00 00 ................ 0047 >>>> 00000010: 00 00 .. 0071 >>>> 00000000: E7 00 00 0E 00 0C 00 02 01 03 00 06 00 00 00 00 ................ 0071 >>>> 00000010: 00 00 .. 0081 >>>> 00000000: E7 00 00 1A 00 18 00 03 01 07 00 12 00 00 0C 17 ................ 0081 >>>> 00000010: 00 0C 20 17 02 24 20 54 57 00 00 00 00 00 .. ..$ TW..... 0085 >>>> 00000000: E7 00 00 0E 00 0C 00 04 01 03 00 06 00 00 00 00 ................ 0085 >>>> 00000010: 00 00 .. 0095 >>>> 00000000: E7 00 00 14 00 12 00 05 01 07 00 0C 00 05 0C 0D ................ 0095 >>>> 00000010: 00 06 00 01 00 02 00 00 ........
The time change command is 0081 (and I highlighted in green the new time, also provided as BCD). The remaining commands appear to be querying some information about the device. Commands 0037, 0047, 0071 and 0085 are exactly the same, except as I found out initially, no packet was identical. In blue I highlighted what appears to be a packet counter of sorts. I’m not sure why it starts at 0x2D, but after that it appears to increment normally, although only after 0xE7 commands (there appear to be a handful more).
Unfortunately this does not cover enough of the protocol yet, but it’s a good starting point for a few hours spent trying to prod things around on a Friday night (what an exciting life I live). I also managed to find how the device is reporting the readings, in blocks of less than 1KB records, but I have not figure out how the software knows when to stop asking for them. In this case it definitely is handy that I have so many readings on the device — this is probably the glucometer I used the most, and I still think is the best blood-reading glucometer, for handiness and results.
Stay tuned for more details, and hopefully to see a spec for the protocol soon, too.