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 PORTNAME="/dev/ttyUSB0"
20 #Some quick functions for yanking values out of a packet.
22 """Returns the source address of a packet as an integer."""
23 return ord(packet[4])+(ord(packet[5])<<8);
24 def isencrypted(packet):
25 """Returns true if the packet is encrypted.""";
27 return ord(packet[1])&0x08;
30 def pktnonceseq(packet):
31 """Returns the nonce sequence of a packet."""
33 for byte in [0xa,9,8,7]:
34 nonce=(nonce<<8)|ord(packet[byte]);
38 print "Usage: %s verb [objects]\n" % sys.argv[0];
39 print "%s info" % sys.argv[0];
40 print "%s regs" % sys.argv[0];
41 print "%s ram" % sys.argv[0];
42 print "%s ramtest" % sys.argv[0];
43 print "%s test" % sys.argv[0];
44 print "%s peek 0x$start [0x$stop]" % sys.argv[0];
45 print "%s poke 0x$adr 0x$val" % sys.argv[0];
46 print "%s txtest" % sys.argv[0];
48 print "\n%s rssi" % sys.argv[0];
49 print "%s spectrum" % sys.argv[0];
50 print "%s spectrumcsv" % sys.argv[0];
52 print "\n%s surf" % sys.argv[0];
53 print "%s sniff [chan]" % sys.argv[0];
54 print "%s fastsniff [chan]" % sys.argv[0];
55 print "%s sniffstrings [chan]" % sys.argv[0];
56 print "%s bsniff [chan]" % sys.argv[0];
57 print "%s sniffcrypt 0x$key [chan]" % sys.argv[0];
58 print "%s sniffdissect" % sys.argv[0];
59 print "%s sniffnonce" % sys.argv[0];
61 print "\n%s txtoscount [-i|-r] TinyOS BlinkToLED" % sys.argv[0];
62 print "%s reflexjam [channel=11] [delay=0]" % sys.argv[0];
64 print "\n%s txpiptest" % sys.argv[0];
65 print "%s txpipscapy" % sys.argv[0];
69 #Initialize FET and set baud rate
70 client=GoodFETCCSPI();
71 client.serInit(port=PORTNAME)
76 #Might read as all ones if chip has a startup delay.
78 if(sys.argv[1]=="carrier"):
80 freq=eval(sys.argv[2]);
82 client.RF_setfreq(freq);
84 client.RF_setchan(freq);
85 print "Channel set to:", freq
91 if(sys.argv[1]=="modulated_spectrum"):
93 client.RF_setfreq(eval(sys.argv[2]));
95 client.RF_modulated_spectrum();
99 if(sys.argv[1]=="reflexjam" or sys.argv[1]=="reflexjamack"):
100 #Setup the radio to listen promiscously on a frequency
101 client.RF_promiscuity(1);
102 client.RF_autocrc(0);
104 freq=eval(sys.argv[2]);
106 client.RF_setfreq(freq);
108 client.RF_setchan(freq);
111 duration=eval(sys.argv[3]);
113 print "Reflexively jamming on %i MHz" % (client.RF_getfreq()/10**6);
114 #Now we let the firmware take over, watching for packets and jamming them.
115 #Standard reflexive jam is done with duration=0.
116 #To selectively jam packets that are above a certain length, set duration
117 # to the number of milliseconds needed to jam frames of that length.
118 # Api-Do project has script available to tune/test this duration.
119 # code.google.com/p/zigbeesecurity (rmspeers)
120 if sys.argv[1]=="reflexjam":
121 client.RF_reflexjam(duration);
122 elif sys.argv[1]=="reflexjamack":
123 client.RF_reflexjam_autoack();
125 if(sys.argv[1]=="info"):
126 print "Found %s" % client.identstr();
127 print "Freq: %05f MHz" % (client.RF_getfreq()/(10**6));
128 print "Status: %s" % client.status();
129 if(sys.argv[1]=="regs"):
130 for adr in range(0x10,0x40): #*1024):
131 val=client.peek(adr);
132 print "%04x:=0x%04x" % (adr,val);
133 if(sys.argv[1]=="ram"):
134 for adr in range(0x0,0x16D,16):
135 row=client.peekram(adr,32);
138 s=s+(" %02x" % ord(foo))
139 print "%04x: %s" % (adr,s);
140 if(sys.argv[1]=="ramtest"):
141 client.pokeram(0x00,[0xde,0xad,0xbe,0xef,
151 0xde,0xad,0xbe,0xef]);
153 for adr in range(0x0,0x16D,16):
154 row=client.peekram(adr,32);
157 s=s+(" %02x" % ord(foo))
158 print "%04x: %s" % (adr,s);
159 if(sys.argv[1]=="test"):
160 data=client.trans([0x20, 0xde, 0xad]);
161 print "%02x %02x" % (ord(data[1]), ord(data[2]));
162 data=client.trans([0x40|0x20, 0xde, 0xad]);
163 print "%02x %02x" % (ord(data[1]), ord(data[2]));
164 if(sys.argv[1]=="rssi"):
166 freq=eval(sys.argv[2]);
168 client.RF_setfreq(freq);
170 client.RF_setchan(freq);
171 print "Listening on %f MHz." % (client.RF_getfreq()/10.0**6);
173 client.strobe(0x02); #Calibrate
178 #client.strobe(0x03); #SRXON
179 rssi=client.RF_getrssi();
180 #client.CC_RFST_IDLE(); #idle
183 for foo in range(0,rssi>>2):
184 string=("%s."%string);
185 print "%02x %04i %s" % (rssi,rssi, string);
186 if(sys.argv[1]=="spectrum"):
187 for chan in range(2400000000,2480000000,5000000):
188 client.RF_setfreq(chan);
189 #print "Listening on %f MHz." % (client.RF_getfreq()/10.0**6);
191 client.strobe(0x02); #Calibrate
195 for foo in range(1,10):
197 rssi=client.RF_getrssi();
198 maxrssi=max(rssi,maxrssi);
200 for foo in range(50,rssi):
201 string=("%s."%string);
202 print "%04i %i %s" % (client.RF_getfreq()/10.0**6,rssi, string);
203 if(sys.argv[1]=="spectrumcsv"):
206 for freq in range(2400000000,2480000000,1000000):
207 client.RF_setfreq(freq);
209 client.strobe(0x02); #Calibrate
211 rssi=client.RF_getrssi();
213 print "%f %i %3i" % (
215 client.RF_getfreq()/10.0**6,
219 if sys.argv[1]=="surf":
220 print "Scanning channels [11,26].";
223 client.RF_promiscuity(1);
224 client.RF_autocrc(1);
228 chan=eval(sys.argv[2]);
232 #Now we're ready to get packets.
236 client.setup(); #Really oughtn't be necessary, but can't hurt.
237 client.RF_setchan(chan);
240 lasttime=time.time();
241 while packet==None and time.time()-lasttime<0.5:
242 packet=client.RF_rxpacket();
244 client.printpacket(packet=packet,
245 prefix=("%02d: "%chan));
249 if(sys.argv[1]=="sniff" or sys.argv[1]=="sniffdissect" or sys.argv[1]=="sniffstrings" or
250 sys.argv[1]=="sniffnonce" or sys.argv[1]=="fastsniff"):
252 client.RF_promiscuity(1);
253 client.RF_autocrc(1);
256 freq=eval(sys.argv[2]);
258 client.RF_setfreq(freq);
260 client.RF_setfreq(freq*1000000);
262 client.RF_setchan(freq);
264 print "Listening as %010x on %i MHz" % (client.RF_getsmac(),
265 client.RF_getfreq()/10**6);
266 #If fastsniffing, then send that command.
267 if sys.argv[1]=="fastsniff":
268 client.RF_rxpacketrepeat();
270 #Now we're ready to get packets.
272 #client.CC_RFST_RX(); # Cop-out that confuses reception!
276 packet=client.RF_rxpacket();
277 if sys.argv[1]=="sniffdissect":
278 client.printdissect(packet);
279 elif sys.argv[1]=="sniffstrings":
281 elif sys.argv[1]=="sniffnonce":
282 if isencrypted(packet):
284 print "%04x: %08x -- %s" % (srcadr(packet),
286 client.packet2str(packet)
291 client.printpacket(packet);
294 if(sys.argv[1]=="bsniff"):
296 client.RF_promiscuity(0);
297 client.RF_setsmac(0xFFFFFFFF);
298 client.RF_autocrc(1);
301 freq=eval(sys.argv[2]);
303 client.RF_setfreq(freq);
305 client.RF_setchan(freq);
307 print "Listening as %010x on %i MHz" % (client.RF_getsmac(),
308 client.RF_getfreq()/10**6);
309 #Now we're ready to get packets.
313 packet=client.RF_rxpacket();
314 client.printpacket(packet);
317 if(sys.argv[1]=="sniffcrypt"):
318 print "Zigbee crypto is pretty damned complicated, and this doesn't work yet.";
320 client.RF_promiscuity(1);
321 client.RF_setsmac(0xFFFFFFFF);
322 client.RF_autocrc(1);
323 #client.poke(0x19, 0x03C7); #SECCTRL0, enabling CCM crypto w/ KEY0
324 client.poke(0x19, 0x03C6); #SECCTRL0, enabling CTRL crypto w/ KEY0
326 #What follows is the nonce.
327 client.poke(0x20, 0x000a); #SECCTRL1, skipping 10 bytes of header
330 key=int(sys.argv[2],16);
331 nonce=int(sys.argv[3],16);
333 print "Setting KEY0 to %x" % key;
334 print "Setting NONCE to %x" % nonce;
335 client.RF_setkey(key);
336 client.RF_setnonce(nonce);
338 freq=eval(sys.argv[3]);
340 client.RF_setfreq(freq);
342 client.RF_setchan(freq);
344 print "Listening as %010x on %i MHz" % (client.RF_getsmac(),
345 client.RF_getfreq()/10**6);
346 #Now we're ready to get packets.
350 packet=client.RF_rxpacketdec();
351 client.printpacket(packet);
354 if(sys.argv[1]=="txtest"):
356 freq=eval(sys.argv[2]);
358 client.RF_setfreq(freq);
360 client.RF_setchan(freq);
361 print "Transmitting DEADBEEF as %010x on %i MHz" % (
363 client.RF_getfreq()/10**6);
366 client.RF_txpacket([0x0f, 0x01, 0x08, 0x82,
367 0xff, 0xff, 0xff, 0xff,
368 0xde, 0xad, 0xbe, 0xef,
371 if(sys.argv[1]=="txtoscount"):
373 Clone of what TinyOS's RadioCountToLeds demo code does. Specify a
374 channel a TinyOS mote programmed with RadioCountToLeds is on, and
375 this will act as the second device. (ryan@rmspeers.com)
377 if (len(sys.argv)<=3):
378 print "Provide -r to work via replays or -i to work via incrementing itself.";
380 if (sys.argv[3]=="-r"):
381 client.RF_promiscuity(1);
382 client.RF_autocrc(1);
384 freq=eval(sys.argv[2]);
386 client.RF_setfreq(freq);
388 client.RF_setchan(freq);
389 if (sys.argv[3]=="-r"):
391 print "Listening as %010x on %i MHz" % (client.RF_getsmac(), client.RF_getfreq()/10**6);
392 print "Transmitting like the TinyOS CountToRadio program on %i MHz" % (client.RF_getfreq()/10**6);
393 if (sys.argv[3]=="-i"):
395 countpkt = [0x0f, 0x41, 0x88, 0xFF, 0x22, 0x00, 0xff, 0xff, 0x01, 0x00, 0x3f, 0x06, 0x00, 0xFF];
397 if (sys.argv[3]=="-r"): #give -r to do via replays from the other device
400 packet=client.RF_rxpacket();
402 client.RF_txpacket(pkt);
403 elif (sys.argv[3]=="-i"): #give -i to have it increment and send
404 #Use this code for it to actually do increments itself:
408 client.RF_txpacket(pkt);
413 if(sys.argv[1]=="txpiptest" or sys.argv[1]=="txpipscapy"):
415 freq=eval(sys.argv[2]);
417 client.RF_setfreq(freq);
419 client.RF_setchan(freq);
420 print "Transmitting on PIP injection as %010x on %i MHz" % (
422 client.RF_getfreq()/10**6);
424 client.RF_setsync(0xFFFF);
427 if(sys.argv[1]=="txpiptest"):
430 #Real header, must begin with SFD.
435 0x1f, 0x01, 0x08, 0x82,
436 0xDF, 0xff, 0xff, 0xff,
437 0xde, 0xad, 0xbe, 0xef,
443 0x00, 0xA7, #CC2420 SFD
445 0x0f, 0x01, 0x08, 0x82,
446 0xff, 0xff, 0xff, 0xff,
447 0xde, 0xad, 0xbe, 0xef,
450 0xff, 0xff, 0xff, 0xff,
451 0xff, 0xff, 0xff, 0xff,
452 0xff, 0xff, 0xff, 0xff,
453 0xff, 0xff, 0xff, 0xff,
454 0xff, 0xff, 0xff, 0xff,
455 0xff, 0xff, 0xff, 0xff,
456 0xff, 0xff, 0xff, 0xff,
458 elif(sys.argv[1]=="txpipscapy"):
459 # NB: Requires Scapy with dot15d4.py layer. (rmspeers)
461 from scapy.all import Dot15d4, Dot15d4FCS, Dot15d4Data, Raw
464 print "To use packet building, Scapy must be installed and have the dot15d4 layer present."
465 print "try: hg clone http://hg.secdev.org/scapy-com";
466 print " sudo ./setup.py install";
467 #Overall method is to build from the inner packet outwards in the pkt string
469 scapyinner = Dot15d4FCS(seqnum=130)/Dot15d4Data()/Raw('\xde\xad\xbe\xef');
470 pkt = str(scapyinner); #build inner pkt to bytes, adding FCS automatically
471 pkt = struct.pack('b', len(pkt)) + pkt #prepend with its length
472 pkt = "\x00\x00\x00\x00\xA7" + pkt #add preamble and SFD to inner packet
473 # Make outer (wrapping) packet
474 scapyouter = Dot15d4(seqnum=130)/Dot15d4Data(dest_panid=0xffdf)/Raw('\xde\xad\xbe\xef\xba\xbe\xc0') #TODO why need these last 3 bytes?
475 pkt = str(scapyouter) + pkt
476 pkt = struct.pack('b', len(pkt)) + pkt #prepend with its length
477 pkt = '\x00\x00\x00\x00\xA7' + pkt + ('\xff'*28) #start with preamble/SFD and add 0xff fill at end
478 pkt = struct.pack('b', len(pkt)) + pkt #prepend with its length (originally used \x7f)
479 client.printpacket(pkt)
480 client.RF_autocrc(1);
481 client.RF_txpacket(pkt)
483 if(sys.argv[1]=="peek"):
486 start=int(sys.argv[2],16);
489 stop=int(sys.argv[3],16);
490 print "Peeking from %04x to %04x." % (start,stop);
492 print "%04x: 0x%04x" % (start,client.peek(start));
494 if(sys.argv[1]=="poke"):
498 start=int(sys.argv[2],16);
500 val=int(sys.argv[3],16);
501 print "Poking r%02x to become 0x%04x." % (start,val);
503 client.poke(start,val);