3 #GoodFET Chipcon SPI Client
4 # (C) 2011 Travis Goodspeed
5 # Additions 2011-2012 Ryan Speers ryan@rmspeers.com
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.""";
24 return ord(packet[1])&0x08;
27 def pktnonceseq(packet):
28 """Returns the nonce sequence of a packet."""
30 for byte in [0xa,9,8,7]:
31 nonce=(nonce<<8)|ord(packet[byte]);
35 print "Usage: %s verb [objects]\n" % sys.argv[0];
36 print "%s info" % sys.argv[0];
37 print "%s regs" % sys.argv[0];
38 print "%s ram" % sys.argv[0];
39 print "%s ramtest" % sys.argv[0];
40 print "%s test" % sys.argv[0];
41 print "%s peek 0x$start [0x$stop]" % sys.argv[0];
42 print "%s poke 0x$adr 0x$val" % sys.argv[0];
43 print "%s txtest" % sys.argv[0];
45 print "\n%s rssi" % sys.argv[0];
46 print "%s spectrum" % sys.argv[0];
47 print "%s spectrumcsv" % sys.argv[0];
49 print "\n%s surf" % sys.argv[0];
50 print "%s sniff [chan]" % sys.argv[0];
51 print "%s fastsniff [chan]" % sys.argv[0];
52 print "%s sniffstrings [chan]" % sys.argv[0];
53 print "%s bsniff [chan]" % sys.argv[0];
54 print "%s sniffcrypt 0x$key [chan]" % sys.argv[0];
55 print "%s sniffdissect" % sys.argv[0];
56 print "%s sniffnonce" % sys.argv[0];
58 print "\n%s txtoscount [-i|-r] TinyOS BlinkToLED" % sys.argv[0];
59 print "%s reflexjam [channel=11] [delay=0]" % sys.argv[0];
61 print "\n%s txpiptest" % sys.argv[0];
62 print "%s txpipscapy" % sys.argv[0];
66 #Initialize FET and set baud rate
67 client=GoodFETCCSPI();
73 #Might read as all ones if chip has a startup delay.
75 if(sys.argv[1]=="carrier"):
77 client.RF_setfreq(eval(sys.argv[2]));
83 if(sys.argv[1]=="modulated_spectrum"):
85 client.RF_setfreq(eval(sys.argv[2]));
87 client.RF_modulated_spectrum();
91 if(sys.argv[1]=="reflexjam" or sys.argv[1]=="reflexjamack"):
92 #Setup the radio to listen promiscously on a frequency
93 client.RF_promiscuity(1);
96 freq=eval(sys.argv[2]);
98 client.RF_setfreq(freq);
100 client.RF_setchan(freq);
103 duration=eval(sys.argv[3]);
105 print "Reflexively jamming on %i MHz" % (client.RF_getfreq()/10**6);
106 #Now we let the firmware take over, watching for packets and jamming them.
107 #Standard reflexive jam is done with duration=0.
108 #To selectively jam packets that are above a certain length, set duration
109 # to the number of milliseconds needed to jam frames of that length.
110 # Api-Do project has script available to tune/test this duration.
111 # code.google.com/p/zigbeesecurity (rmspeers)
112 if sys.argv[1]=="reflexjam":
113 client.RF_reflexjam(duration);
114 elif sys.argv[1]=="reflexjamack":
115 client.RF_reflexjam_autoack();
117 if(sys.argv[1]=="info"):
118 print "Found %s" % client.identstr();
119 print "Freq: %05f MHz" % (client.RF_getfreq()/(10**6));
120 print "Status: %s" % client.status();
121 if(sys.argv[1]=="regs"):
122 for adr in range(0x10,0x40): #*1024):
123 val=client.peek(adr);
124 print "%04x:=0x%04x" % (adr,val);
125 if(sys.argv[1]=="ram"):
126 for adr in range(0x0,0x16D,16):
127 row=client.peekram(adr,32);
130 s=s+(" %02x" % ord(foo))
131 print "%04x: %s" % (adr,s);
132 if(sys.argv[1]=="ramtest"):
133 client.pokeram(0x00,[0xde,0xad,0xbe,0xef,
143 0xde,0xad,0xbe,0xef]);
145 for adr in range(0x0,0x16D,16):
146 row=client.peekram(adr,32);
149 s=s+(" %02x" % ord(foo))
150 print "%04x: %s" % (adr,s);
151 if(sys.argv[1]=="test"):
152 data=client.trans([0x20, 0xde, 0xad]);
153 print "%02x %02x" % (ord(data[1]), ord(data[2]));
154 data=client.trans([0x40|0x20, 0xde, 0xad]);
155 print "%02x %02x" % (ord(data[1]), ord(data[2]));
156 if(sys.argv[1]=="rssi"):
158 freq=eval(sys.argv[2]);
160 client.RF_setfreq(freq);
162 client.RF_setchan(freq);
163 print "Listening on %f MHz." % (client.RF_getfreq()/10.0**6);
165 client.strobe(0x02); #Calibrate
170 #client.strobe(0x03); #SRXON
171 rssi=client.RF_getrssi();
172 #client.CC_RFST_IDLE(); #idle
175 for foo in range(0,rssi>>2):
176 string=("%s."%string);
177 print "%02x %04i %s" % (rssi,rssi, string);
178 if(sys.argv[1]=="spectrum"):
179 for chan in range(2400000000,2480000000,5000000):
180 client.RF_setfreq(chan);
181 #print "Listening on %f MHz." % (client.RF_getfreq()/10.0**6);
183 client.strobe(0x02); #Calibrate
187 for foo in range(1,10):
189 rssi=client.RF_getrssi();
190 maxrssi=max(rssi,maxrssi);
192 for foo in range(50,rssi):
193 string=("%s."%string);
194 print "%04i %i %s" % (client.RF_getfreq()/10.0**6,rssi, string);
195 if(sys.argv[1]=="spectrumcsv"):
198 for freq in range(2400000000,2480000000,1000000):
199 client.RF_setfreq(freq);
201 client.strobe(0x02); #Calibrate
203 rssi=client.RF_getrssi();
205 print "%f %i %3i" % (
207 client.RF_getfreq()/10.0**6,
211 if sys.argv[1]=="surf":
212 print "Scanning channels [11,26].";
215 client.RF_promiscuity(1);
216 client.RF_autocrc(1);
220 chan=eval(sys.argv[2]);
224 #Now we're ready to get packets.
228 client.setup(); #Really oughtn't be necessary, but can't hurt.
229 client.RF_setchan(chan);
232 lasttime=time.time();
233 while packet==None and time.time()-lasttime<0.5:
234 packet=client.RF_rxpacket();
236 client.printpacket(packet=packet,
237 prefix=("%02d: "%chan));
241 if(sys.argv[1]=="sniff" or sys.argv[1]=="sniffdissect" or sys.argv[1]=="sniffstrings" or
242 sys.argv[1]=="sniffnonce" or sys.argv[1]=="fastsniff"):
244 client.RF_promiscuity(1);
245 client.RF_autocrc(1);
248 freq=eval(sys.argv[2]);
250 client.RF_setfreq(freq);
252 client.RF_setfreq(freq*1000000);
254 client.RF_setchan(freq);
256 print "Listening as %010x on %i MHz" % (client.RF_getsmac(),
257 client.RF_getfreq()/10**6);
258 #If fastsniffing, then send that command.
259 if sys.argv[1]=="fastsniff":
260 client.RF_rxpacketrepeat();
262 #Now we're ready to get packets.
264 #client.CC_RFST_RX(); # Cop-out that confuses reception!
268 packet=client.RF_rxpacket();
269 if sys.argv[1]=="sniffdissect":
270 client.printdissect(packet);
271 elif sys.argv[1]=="sniffstrings":
273 elif sys.argv[1]=="sniffnonce":
274 if isencrypted(packet):
276 print "%04x: %08x -- %s" % (srcadr(packet),
278 client.packet2str(packet)
283 client.printpacket(packet);
286 if(sys.argv[1]=="bsniff"):
288 client.RF_promiscuity(0);
289 client.RF_setsmac(0xFFFFFFFF);
290 client.RF_autocrc(1);
293 freq=eval(sys.argv[2]);
295 client.RF_setfreq(freq);
297 client.RF_setchan(freq);
299 print "Listening as %010x on %i MHz" % (client.RF_getsmac(),
300 client.RF_getfreq()/10**6);
301 #Now we're ready to get packets.
305 packet=client.RF_rxpacket();
306 client.printpacket(packet);
309 if(sys.argv[1]=="sniffcrypt"):
310 print "Zigbee crypto is pretty damned complicated, and this doesn't work yet.";
312 client.RF_promiscuity(1);
313 client.RF_setsmac(0xFFFFFFFF);
314 client.RF_autocrc(1);
315 #client.poke(0x19, 0x03C7); #SECCTRL0, enabling CCM crypto w/ KEY0
316 client.poke(0x19, 0x03C6); #SECCTRL0, enabling CTRL crypto w/ KEY0
318 #What follows is the nonce.
319 client.poke(0x20, 0x000a); #SECCTRL1, skipping 10 bytes of header
322 key=int(sys.argv[2],16);
323 nonce=int(sys.argv[3],16);
325 print "Setting KEY0 to %x" % key;
326 print "Setting NONCE to %x" % nonce;
327 client.RF_setkey(key);
328 client.RF_setnonce(nonce);
330 freq=eval(sys.argv[3]);
332 client.RF_setfreq(freq);
334 client.RF_setchan(freq);
336 print "Listening as %010x on %i MHz" % (client.RF_getsmac(),
337 client.RF_getfreq()/10**6);
338 #Now we're ready to get packets.
342 packet=client.RF_rxpacketdec();
343 client.printpacket(packet);
346 if(sys.argv[1]=="txtest"):
348 freq=eval(sys.argv[2]);
350 client.RF_setfreq(freq);
352 client.RF_setchan(freq);
353 print "Transmitting DEADBEEF as %010x on %i MHz" % (
355 client.RF_getfreq()/10**6);
358 client.RF_txpacket([0x0f, 0x01, 0x08, 0x82,
359 0xff, 0xff, 0xff, 0xff,
360 0xde, 0xad, 0xbe, 0xef,
363 if(sys.argv[1]=="txtoscount"):
365 Clone of what TinyOS's RadioCountToLeds demo code does. Specify a
366 channel a TinyOS mote programmed with RadioCountToLeds is on, and
367 this will act as the second device. (ryan@rmspeers.com)
369 if (len(sys.argv)<=3):
370 print "Provide -r to work via replays or -i to work via incrementing itself.";
372 if (sys.argv[3]=="-r"):
373 client.RF_promiscuity(1);
374 client.RF_autocrc(1);
376 freq=eval(sys.argv[2]);
378 client.RF_setfreq(freq);
380 client.RF_setchan(freq);
381 if (sys.argv[3]=="-r"):
383 print "Listening as %010x on %i MHz" % (client.RF_getsmac(), client.RF_getfreq()/10**6);
384 print "Transmitting like the TinyOS CountToRadio program on %i MHz" % (client.RF_getfreq()/10**6);
385 if (sys.argv[3]=="-i"):
387 countpkt = [0x0f, 0x41, 0x88, 0xFF, 0x22, 0x00, 0xff, 0xff, 0x01, 0x00, 0x3f, 0x06, 0x00, 0xFF];
389 if (sys.argv[3]=="-r"): #give -r to do via replays from the other device
392 packet=client.RF_rxpacket();
394 client.RF_txpacket(pkt);
395 elif (sys.argv[3]=="-i"): #give -i to have it increment and send
396 #Use this code for it to actually do increments itself:
400 client.RF_txpacket(pkt);
405 if(sys.argv[1]=="txpiptest" or sys.argv[1]=="txpipscapy"):
407 freq=eval(sys.argv[2]);
409 client.RF_setfreq(freq);
411 client.RF_setchan(freq);
412 print "Transmitting on PIP injection as %010x on %i MHz" % (
414 client.RF_getfreq()/10**6);
416 client.RF_setsync(0xFFFF);
419 if(sys.argv[1]=="txpiptest"):
422 #Real header, must begin with SFD.
427 0x1f, 0x01, 0x08, 0x82,
428 0xDF, 0xff, 0xff, 0xff,
429 0xde, 0xad, 0xbe, 0xef,
435 0x00, 0xA7, #CC2420 SFD
437 0x0f, 0x01, 0x08, 0x82,
438 0xff, 0xff, 0xff, 0xff,
439 0xde, 0xad, 0xbe, 0xef,
442 0xff, 0xff, 0xff, 0xff,
443 0xff, 0xff, 0xff, 0xff,
444 0xff, 0xff, 0xff, 0xff,
445 0xff, 0xff, 0xff, 0xff,
446 0xff, 0xff, 0xff, 0xff,
447 0xff, 0xff, 0xff, 0xff,
448 0xff, 0xff, 0xff, 0xff,
450 elif(sys.argv[1]=="txpipscapy"):
451 # NB: Requires Scapy with dot15d4.py layer. (rmspeers)
453 from scapy.all import Dot15d4, Dot15d4FCS, Dot15d4Data, Raw
456 print "To use packet building, Scapy must be installed and have the dot15d4 layer present."
457 print "try: hg clone http://hg.secdev.org/scapy-com";
458 print " sudo ./setup.py install";
459 #Overall method is to build from the inner packet outwards in the pkt string
461 scapyinner = Dot15d4FCS(seqnum=130)/Dot15d4Data()/Raw('\xde\xad\xbe\xef');
462 pkt = str(scapyinner); #build inner pkt to bytes, adding FCS automatically
463 pkt = struct.pack('b', len(pkt)) + pkt #prepend with its length
464 pkt = "\x00\x00\x00\x00\xA7" + pkt #add preamble and SFD to inner packet
465 # Make outer (wrapping) packet
466 scapyouter = Dot15d4(seqnum=130)/Dot15d4Data(dest_panid=0xffdf)/Raw('\xde\xad\xbe\xef\xba\xbe\xc0') #TODO why need these last 3 bytes?
467 pkt = str(scapyouter) + pkt
468 pkt = struct.pack('b', len(pkt)) + pkt #prepend with its length
469 pkt = '\x00\x00\x00\x00\xA7' + pkt + ('\xff'*28) #start with preamble/SFD and add 0xff fill at end
470 pkt = struct.pack('b', len(pkt)) + pkt #prepend with its length (originally used \x7f)
471 client.printpacket(pkt)
472 client.RF_autocrc(1);
473 client.RF_txpacket(pkt)
475 if(sys.argv[1]=="peek"):
478 start=int(sys.argv[2],16);
481 stop=int(sys.argv[3],16);
482 print "Peeking from %04x to %04x." % (start,stop);
484 print "%04x: 0x%04x" % (start,client.peek(start));
486 if(sys.argv[1]=="poke"):
490 start=int(sys.argv[2],16);
492 val=int(sys.argv[3],16);
493 print "Poking r%02x to become 0x%04x." % (start,val);
495 client.poke(start,val);