3 #GoodFET Chipcon SPI Client
4 # (C) 2011 Travis Goodspeed
5 # Additions 2011-2012 Ryan Speers ryan@rmspeers.com
8 #Might be CC2420 Specific
14 from GoodFETCCSPI import GoodFETCCSPI;
17 #Some quick functions for yanking values out of a packet.
19 """Returns the source address of a packet as an integer."""
20 return ord(packet[4])+(ord(packet[5])<<8);
21 def isencrypted(packet):
22 """Returns true if the packet is encrypted.""";
23 return ord(packet[1])&0x08;
24 def pktnonceseq(packet):
25 """Returns the nonce sequence of a packet."""
27 for byte in [0xa,9,8,7]:
28 nonce=(nonce<<8)|ord(packet[byte]);
32 print "Usage: %s verb [objects]\n" % sys.argv[0];
33 print "%s info" % sys.argv[0];
34 print "%s regs" % sys.argv[0];
35 print "%s ram" % sys.argv[0];
36 print "%s ramtest" % sys.argv[0];
37 print "%s test" % sys.argv[0];
38 print "%s peek 0x$start [0x$stop]" % sys.argv[0];
39 print "%s poke 0x$adr 0x$val" % sys.argv[0];
40 print "%s txtest" % sys.argv[0];
42 print "\n%s rssi" % sys.argv[0];
43 print "%s spectrum" % sys.argv[0];
44 print "%s spectrumcsv" % sys.argv[0];
46 print "\n%s surf" % sys.argv[0];
47 print "%s sniff [chan]" % sys.argv[0];
48 print "%s sniffstrings [chan]" % sys.argv[0];
49 print "%s bsniff [chan]" % sys.argv[0];
50 print "%s sniffcrypt 0x$key [chan]" % sys.argv[0];
51 print "%s sniffdissect" % sys.argv[0];
52 print "%s sniffnonce" % sys.argv[0];
54 print "\n%s txtoscount [-i|-r] TinyOS BlinkToLED" % sys.argv[0];
55 print "%s reflexjam [channel=11] [delay=0]" % sys.argv[0];
59 #Initialize FET and set baud rate
60 client=GoodFETCCSPI();
66 #Might read as all ones if chip has a startup delay.
68 if(sys.argv[1]=="carrier"):
70 client.RF_setfreq(eval(sys.argv[2]));
76 if(sys.argv[1]=="modulated_spectrum"):
78 client.RF_setfreq(eval(sys.argv[2]));
80 client.RF_modulated_spectrum();
84 if(sys.argv[1]=="reflexjam" or sys.argv[1]=="reflexjamack"):
85 #Setup the radio to listen promiscously on a frequency
86 client.RF_promiscuity(1);
89 freq=eval(sys.argv[2]);
91 client.RF_setfreq(freq);
93 client.RF_setchan(freq);
96 duration=eval(sys.argv[3]);
98 print "Reflexively jamming on %i MHz" % (client.RF_getfreq()/10**6);
99 #Now we let the firmware take over, watching for packets and jamming them.
100 #Standard reflexive jam is done with duration=0.
101 #To selectively jam packets that are above a certain length, set duration
102 # to the number of milliseconds needed to jam frames of that length.
103 # Api-Do project has script available to tune/test this duration.
104 # code.google.com/p/zigbeesecurity (rmspeers)
105 if sys.argv[1]=="reflexjam":
106 client.RF_reflexjam(duration);
107 elif sys.argv[1]=="reflexjamack":
108 client.RF_reflexjam_autoack();
110 if(sys.argv[1]=="info"):
111 print "Found %s" % client.identstr();
112 print "Freq: %05f MHz" % (client.RF_getfreq()/(10**6));
113 print "Status: %s" % client.status();
114 if(sys.argv[1]=="regs"):
115 for adr in range(0x10,0x40): #*1024):
116 val=client.peek(adr);
117 print "%04x:=0x%04x" % (adr,val);
118 if(sys.argv[1]=="ram"):
119 for adr in range(0x0,0x16D,16):
120 row=client.peekram(adr,32);
123 s=s+(" %02x" % ord(foo))
124 print "%04x: %s" % (adr,s);
125 if(sys.argv[1]=="ramtest"):
126 client.pokeram(0x00,[0xde,0xad,0xbe,0xef,
136 0xde,0xad,0xbe,0xef]);
138 for adr in range(0x0,0x16D,16):
139 row=client.peekram(adr,32);
142 s=s+(" %02x" % ord(foo))
143 print "%04x: %s" % (adr,s);
144 if(sys.argv[1]=="test"):
145 data=client.trans([0x20, 0xde, 0xad]);
146 print "%02x %02x" % (ord(data[1]), ord(data[2]));
147 data=client.trans([0x40|0x20, 0xde, 0xad]);
148 print "%02x %02x" % (ord(data[1]), ord(data[2]));
149 if(sys.argv[1]=="rssi"):
151 freq=eval(sys.argv[2]);
153 client.RF_setfreq(freq);
155 client.RF_setchan(freq);
156 print "Listening on %f MHz." % (client.RF_getfreq()/10.0**6);
158 client.strobe(0x02); #Calibrate
163 #client.strobe(0x03); #SRXON
164 rssi=client.RF_getrssi();
165 #client.CC_RFST_IDLE(); #idle
168 for foo in range(0,rssi>>2):
169 string=("%s."%string);
170 print "%02x %04i %s" % (rssi,rssi, string);
171 if(sys.argv[1]=="spectrum"):
172 for chan in range(2400000000,2480000000,5000000):
173 client.RF_setfreq(chan);
174 #print "Listening on %f MHz." % (client.RF_getfreq()/10.0**6);
176 client.strobe(0x02); #Calibrate
180 for foo in range(1,10):
182 rssi=client.RF_getrssi();
183 maxrssi=max(rssi,maxrssi);
185 for foo in range(50,rssi):
186 string=("%s."%string);
187 print "%04i %i %s" % (client.RF_getfreq()/10.0**6,rssi, string);
188 if(sys.argv[1]=="spectrumcsv"):
191 for freq in range(2400000000,2480000000,1000000):
192 client.RF_setfreq(freq);
194 client.strobe(0x02); #Calibrate
196 rssi=client.RF_getrssi();
198 print "%f %i %3i" % (
200 client.RF_getfreq()/10.0**6,
204 if sys.argv[1]=="surf":
205 print "Scanning channels [11,26].";
208 client.RF_promiscuity(1);
209 client.RF_autocrc(1);
213 chan=eval(sys.argv[2]);
217 #Now we're ready to get packets.
221 client.setup(); #Really oughtn't be necessary, but can't hurt.
222 client.RF_setchan(chan);
225 lasttime=time.time();
226 while packet==None and time.time()-lasttime<0.5:
227 packet=client.RF_rxpacket();
229 client.printpacket(packet=packet,
230 prefix=("%02d: "%chan));
234 if(sys.argv[1]=="sniff" or sys.argv[1]=="sniffdissect" or sys.argv[1]=="sniffstrings" or
235 sys.argv[1]=="sniffnonce"):
237 client.RF_promiscuity(1);
238 client.RF_autocrc(1);
241 freq=eval(sys.argv[2]);
243 client.RF_setfreq(freq);
245 client.RF_setfreq(freq*1000000);
247 client.RF_setchan(freq);
249 print "Listening as %010x on %i MHz" % (client.RF_getsmac(),
250 client.RF_getfreq()/10**6);
251 #Now we're ready to get packets.
253 #client.setup(); #Really oughtn't be necessary, but can't hurt.
258 packet=client.RF_rxpacket();
259 if sys.argv[1]=="sniffdissect":
260 client.printdissect(packet);
261 elif sys.argv[1]=="sniffstrings":
263 elif sys.argv[1]=="sniffnonce":
264 if isencrypted(packet):
265 print "%04x: %08x" % (srcadr(packet),pktnonceseq(packet));
267 client.printpacket(packet);
270 if(sys.argv[1]=="bsniff"):
272 client.RF_promiscuity(0);
273 client.RF_setsmac(0xFFFFFFFF);
274 client.RF_autocrc(1);
277 freq=eval(sys.argv[2]);
279 client.RF_setfreq(freq);
281 client.RF_setchan(freq);
283 print "Listening as %010x on %i MHz" % (client.RF_getsmac(),
284 client.RF_getfreq()/10**6);
285 #Now we're ready to get packets.
289 packet=client.RF_rxpacket();
290 client.printpacket(packet);
293 if(sys.argv[1]=="sniffcrypt"):
294 print "Zigbee crypto is pretty damned complicated, and this doesn't work yet.";
296 client.RF_promiscuity(1);
297 client.RF_setsmac(0xFFFFFFFF);
298 client.RF_autocrc(1);
299 #client.poke(0x19, 0x03C7); #SECCTRL0, enabling CCM crypto w/ KEY0
300 client.poke(0x19, 0x03C6); #SECCTRL0, enabling CTRL crypto w/ KEY0
302 #What follows is the nonce.
303 client.poke(0x20, 0x000a); #SECCTRL1, skipping 10 bytes of header
306 key=int(sys.argv[2],16);
307 nonce=int(sys.argv[3],16);
309 print "Setting KEY0 to %x" % key;
310 print "Setting NONCE to %x" % nonce;
311 client.RF_setkey(key);
312 client.RF_setnonce(nonce);
314 freq=eval(sys.argv[3]);
316 client.RF_setfreq(freq);
318 client.RF_setchan(freq);
320 print "Listening as %010x on %i MHz" % (client.RF_getsmac(),
321 client.RF_getfreq()/10**6);
322 #Now we're ready to get packets.
326 packet=client.RF_rxpacketdec();
327 client.printpacket(packet);
330 if(sys.argv[1]=="txtest"):
332 freq=eval(sys.argv[2]);
334 client.RF_setfreq(freq);
336 client.RF_setchan(freq);
337 print "Transmitting DEADBEEF as %010x on %i MHz" % (
339 client.RF_getfreq()/10**6);
342 client.RF_txpacket([0x0f, 0x01, 0x08, 0x82,
343 0xff, 0xff, 0xff, 0xff,
344 0xde, 0xad, 0xbe, 0xef,
347 if(sys.argv[1]=="txtoscount"):
349 Clone of what TinyOS's RadioCountToLeds demo code does. Specify a
350 channel a TinyOS mote programmed with RadioCountToLeds is on, and
351 this will act as the second device. (ryan@rmspeers.com)
353 if (len(sys.argv)<=3):
354 print "Provide -r to work via replays or -i to work via incrementing itself.";
356 if (sys.argv[3]=="-r"):
357 client.RF_promiscuity(1);
358 client.RF_autocrc(1);
360 freq=eval(sys.argv[2]);
362 client.RF_setfreq(freq);
364 client.RF_setchan(freq);
365 if (sys.argv[3]=="-r"):
367 print "Listening as %010x on %i MHz" % (client.RF_getsmac(), client.RF_getfreq()/10**6);
368 print "Transmitting like the TinyOS CountToRadio program on %i MHz" % (client.RF_getfreq()/10**6);
369 if (sys.argv[3]=="-i"):
371 countpkt = [0x0f, 0x41, 0x88, 0xFF, 0x22, 0x00, 0xff, 0xff, 0x01, 0x00, 0x3f, 0x06, 0x00, 0xFF];
373 if (sys.argv[3]=="-r"): #give -r to do via replays from the other device
376 packet=client.RF_rxpacket();
378 client.RF_txpacket(pkt);
379 elif (sys.argv[3]=="-i"): #give -i to have it increment and send
380 #Use this code for it to actually do increments itself:
384 client.RF_txpacket(pkt);
389 if(sys.argv[1]=="txpiptest" or sys.argv[1]=="txpipscapy"):
391 freq=eval(sys.argv[2]);
393 client.RF_setfreq(freq);
395 client.RF_setchan(freq);
396 print "Transmitting on as %010x on %i MHz" % (
398 client.RF_getfreq()/10**6);
400 client.RF_setsync(0xFFFF);
403 if(sys.argv[1]=="txpiptest"):
406 #Real header, must begin with SFD.
411 0x1f, 0x01, 0x08, 0x82,
412 0xDF, 0xff, 0xff, 0xff,
413 0xde, 0xad, 0xbe, 0xef,
419 0x00, 0xA7, #CC2420 SFD
421 0x0f, 0x01, 0x08, 0x82,
422 0xff, 0xff, 0xff, 0xff,
423 0xde, 0xad, 0xbe, 0xef,
426 0xff, 0xff, 0xff, 0xff,
427 0xff, 0xff, 0xff, 0xff,
428 0xff, 0xff, 0xff, 0xff,
429 0xff, 0xff, 0xff, 0xff,
430 0xff, 0xff, 0xff, 0xff,
431 0xff, 0xff, 0xff, 0xff,
432 0xff, 0xff, 0xff, 0xff,
434 elif(sys.argv[1]=="txpipscapy"):
435 # NB: Requires Scapy with dot15d4.py layer. (rmspeers)
437 from scapy.all import Dot15d4, Dot15d4FCS, Dot15d4Data, Raw
440 print "To use packet building, Scapy must be installed and have the dot15d4 layer present."
441 print "try: hg clone http://hg.secdev.org/scapy-com";
442 print " sudo ./setup.py install";
443 #Overall method is to build from the inner packet outwards in the pkt string
445 scapyinner = Dot15d4FCS(seqnum=130)/Dot15d4Data()/Raw('\xde\xad\xbe\xef');
446 pkt = str(scapyinner); #build inner pkt to bytes, adding FCS automatically
447 pkt = struct.pack('b', len(pkt)) + pkt #prepend with its length
448 pkt = "\x00\x00\x00\x00\xA7" + pkt #add preamble and SFD to inner packet
449 # Make outer (wrapping) packet
450 scapyouter = Dot15d4(seqnum=130)/Dot15d4Data(dest_panid=0xffdf)/Raw('\xde\xad\xbe\xef\xba\xbe\xc0') #TODO why need these last 3 bytes?
451 pkt = str(scapyouter) + pkt
452 pkt = struct.pack('b', len(pkt)) + pkt #prepend with its length
453 pkt = '\x00\x00\x00\x00\xA7' + pkt + ('\xff'*28) #start with preamble/SFD and add 0xff fill at end
454 pkt = struct.pack('b', len(pkt)) + pkt #prepend with its length (originally used \x7f)
455 client.printpacket(pkt)
456 client.RF_autocrc(1);
457 client.RF_txpacket(pkt)
459 if(sys.argv[1]=="peek"):
462 start=int(sys.argv[2],16);
465 stop=int(sys.argv[3],16);
466 print "Peeking from %04x to %04x." % (start,stop);
468 print "%04x: 0x%04x" % (start,client.peek(start));
470 if(sys.argv[1]=="poke"):
474 start=int(sys.argv[2],16);
476 val=int(sys.argv[3],16);
477 print "Poking r%02x to become 0x%04x." % (start,val);
479 client.poke(start,val);