Reverse Engineering the greenGOOSE Starter Kit

2012-04-11 @ 2026 MDT -- Receiving packets with egg from a home grown CC1101 board

2012-04-10 @ 2034 MDT -- Packets Captured!

2012-04-09 @ 2034 MDT -- Complete Config Capture

2012-04-04 @ 2114 MDT -- Dumped CC1101 Register Settings

2012-03-20 @ 2200 MDT -- Sensor μC pinout

2012-03-17 @ 2010 MDT -- Decoding packets....

2012-03-16 @ 2137 MDT -- Have working altered binary!

The Kit!

The greenGOOSE [http://www.greengoose.com] start kit ships with four wireless sensors, a green base station egg, power supply, etc. The cost of the kit was $49 USD at the time I purchased it. Considering the hardware received this is a pretty good deal -- Comparable CC1101 transceiver boards on ebay go for upwards of $10 USD.


Hardware

The Egg

Interesting Components:
PIC 18F25J10 µC, ENC28J60 ethernet controller
Interesting Components:
Ti CC1101 ISM Transceiver. PIC Programming Header (J2 -- see below)

Dumping the firmware

Before beginning to customize the base station I thought it prudent to backup the stock firmware. This was done by attaching test leads to the PGD, MCLR, PGC, VDD, and VSS lines on the 18F25J10. Other than a few scattered test points, the only likely place for a programming header was J2. After a quick probing, it turned out that J2, was in fact, the programming header.

J2 PINOUT
J218F25J10
1MCLR
2VDD
3VSS
4PGD
5PGC

Next, I pulled straight up on the plastic housing of J2. I knocked off the pins with a hot iron and put on test leads on pins 1-5.

Turns out that the Pickit2 pinout mapped exactly to the pinout for J2.

I jumpered all together, fired up the PICkit 2 Programmer software, and was able to read the flash.

Interesting strings found in the hex dump:

Content-Length: 54
POST /cgi-bin/xxxxxxxxxx.cgi
HTTP/1.1
Host:
xxx.xxxxxxxxxx.com
HTTP/1.1 200 OK
Connection: close

Tweaking the Binary

After determining that the gg egg was using an HTTP post, I could have fired up wireshark or spoofed DNS responses but that would be no fun. Simply modifying the dumped binary to point to my own backend seemed like the way to go.

The URL to which the gg egg posts data is a 19 byte null terminated string which can be located in the intel hex dump at:

:1077E000 3A20636C6F736500 START HERE ->6170...
:1077F000 ...E636F6D00 <- END HERE FF056A046AB5

I was lucky enough to have a URL with exactly the same number of characters as xxx.xxxxxxxxxx.com --> bozeman.dyndns.org If you are not so lucky, I'm sure it would work to stay under 18 characters and simply pad-out with nulls. Below is a quick and dirty perl snippet to calculate the new intel hex checksum for the line you alter.

			#!/usr/bin/perl -w
			use strict;

			my $bytes = shift or die "$!";
			die "Please specify an even number of nibbles!\n" if length($bytes) % 2;

			my $sum = 0;

			for(my $i = 0; $i < length($bytes); $i += 2){
				$sum += ord(pack("H2", substr($bytes, $i, 2)));
			}

			$sum &= 0x000000FF;
			$sum = 0x00000100 - $sum;

			printf("%02X\n", $sum);
		

My altered code that changes xxx.xxxxxxxxxx.com to bozeman.dyndns.org is below:

:1077E0003A20636C6F736500626F7A656D616E2E0F :1077F00064796E646E732E6F726700FF056A046AA7

Next, I loaded up the altered binary in the Pickit2 software, and loaded it into the egg. Success.

I, almost immediately, began to see entries in my webserver log:

			10.0.9.1 bozeman.dyndns.org - [16/Mar/2012:21:22:33 -0600] "POST /cgi-bin/gglistener.cgi HTTP/1.1" 500 369 "-" "-"
			10.0.9.1 bozeman.dyndns.org - [16/Mar/2012:21:22:43 -0600] "POST /cgi-bin/gglistener.cgi HTTP/1.1" 500 369 "-" "-"
			10.0.9.1 bozeman.dyndns.org - [16/Mar/2012:21:22:53 -0600] "POST /cgi-bin/gglistener.cgi HTTP/1.1" 500 369 "-" "-"
			10.0.9.1 bozeman.dyndns.org - [16/Mar/2012:21:23:03 -0600] "POST /cgi-bin/gglistener.cgi HTTP/1.1" 500 369 "-" "-"
			10.0.9.1 bozeman.dyndns.org - [16/Mar/2012:21:23:13 -0600] "POST /cgi-bin/gglistener.cgi HTTP/1.1" 500 369 "-" "-"
		

The initial cgi did not exist. Hence the 500 error code. Also, interestingly, the LED was red. After stubbing out a quick test cgi:

			#!/usr/bin/perl -w
			use strict;
			use CGI;
			my $text;
			open(OUT, ">>/tmp/gg.log");
			if($ENV{'CONTENT_LENGTH'}){
				read(STDIN,$text,$ENV{'CONTENT_LENGTH'});
				my $bytes = unpack("H*", $text);
				print OUT time() . " -- " . $bytes . "\n";
			}
			print "Connection: close\n\n";
		

the LED was still red although I began to receive data in my cgi's log:

			1331954948 -- 474700e1050100000050c000000000000011ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
			1331954953 -- 474700e2050100000050c000000000000011ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
			1331954958 -- 474700e3050100000050c000000000000011ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
			1331954963 -- 474700e4050100000050c000000000000011ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
			1331954968 -- 474700e5050100000050c000000000000011ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff

		

Next, I tweaked the cgi script to reply properly and with "AOK":

			#!/usr/bin/perl -w
			use strict;
			use CGI;
			my $text;
			open(OUT, ">>/tmp/gg.log");
			if($ENV{'CONTENT_LENGTH'}){
				read(STDIN,$text,$ENV{'CONTENT_LENGTH'});
				my $bytes = unpack("H*", $text);
				print OUT time() . " -- " . $bytes . "\n";
			}
			print "\n\nAOK\n";
			print "Connection: close\n\n";
		

The LED turned green, and the data stopped coming every 5-10 seconds and began to come every 25-30 seconds. I assume these are "check-in" messages.

			1331955133 -- 47470108050100000050c000000000000011ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
			1331955148 -- 4747010a050100000050c000000000000011ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
			1331955158 -- 4747010b050200000050c000000000000d5000002XXXf28b000000030002XXXf00000000000000000000000000000000000000000000
			1331955182 -- 4747010c050100000050c000000000000011ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
			1331955207 -- 4747010d050100000050c000000000000011ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
			1331955232 -- 4747010e050100000050c000000000000011ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
		

I also shook the treat sensor and received, what I presume to be, messages from it:

			1331954970 -- 474700e6050200000050c00000000000256000002XXX4c00101d4814d00444161004401690063c1690053816d0033464100831549006
			1331954971 -- 474700e7050200000050c000000000000d5000002XXX74870000000000003bab00000000000000000000000000000000000000000000
		

Decoding the Packets

Okay, time to start looking at the packets. From what we've learned it looks like we're dealing with a fixed record length message encoding as indicated by the Content-Length: 54 HTTP header....

Check-In Packet
                        1                    2                   3                   4                   5      
 0 1 2  3  4 5 6 7  8 9 0 1 2 3  4 5 6 7  8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
474700 82 05010000 0050c0000000 00000011 ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
474700 83 05010000 0050c0000000 00000011 ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
Byte OffsetValueMeaning
0-2474700Header -- ASCII String: GG (greengoose)
382Packet Counter 0 through 255
4-705010000Message Type (in this case, check-in)
8-130050c0000000Egg's Ethernet MAC Address (unique identifier)
14-1700000011Message Cause? 11 == check-in?
17-53ffff...ffUnused Buffer Space
Sensor Packet
                                       1                     2                    3                   4                   5      
Timestamp (cgi) 0 1 2  3  4 5 6 7  8 9 0 1 2 3  4 5 6 7  8 9 0 1  2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
11332041003 -- 474700 ec 05020000 0050c0000000 00000d60 00002XXX a4001000a09e10001b9a00000000000000000000000000000000000000000000
11332041003 -- 474700 ed 05020000 0050c0000000 00000d50 00002XXX 72a60001000000000c9d00000000000000000000000000000000000000000000
11332041010 -- 474700 ee 05020000 0050c0000000 00000960 00002XXX a80010005dd70000000000000000000000000000000000000000000000000000
0-2474700Header -- ASCII String: GG (greengoose)
382Packet Counter 0 through 255
4-705010000Message Type (in this case, check-in)
8-130050c0000000Egg's Ethernet MAC Address (unique identifier)
14-1700000d60Message Cause? d60 == start action, d50 == end action? 960 == ?
18-2100002XXXSensor Id. Each of my 4 sensors has a unique id in this field
22-53...Accelerometer data? Packet signal data? Debounce time data?

Well, folks, it's a wrap. We now know how to redirect data from the egg to a custom backend and interpret which sensors are moving, when.

From here, I plan on: