debugged the reInject fuzzer method
[goodfet] / client / experiments.py
1 import sys;
2 import binascii;
3 import array;
4 import csv, time, argparse;
5 import datetime
6 import os
7 from random import randrange
8 import random
9 from GoodFETMCPCAN import GoodFETMCPCAN;
10 from GoodFETMCPCANCommunication import GoodFETMCPCANCommunication
11 from intelhex import IntelHex;
12 import Queue
13 import math
14
15 tT = time
16
17
18 class experiments(GoodFETMCPCANCommunication):
19     """ 
20     This class provides methods for reverse-engineering the protocols on the CAN bus network
21     via the GOODTHOPTER10 board, U{http://goodfet.sourceforge.net/hardware/goodthopter10/}    
22     
23     """
24     
25     def __init__(self, data_location):
26         """ 
27         Constructor
28         @type datalocation: string
29         @param datalocation: path to the folder where data will be stored
30         """
31         GoodFETMCPCANCommunication.__init__(self, data_location)
32         #super(experiments,self).__init(self)
33         self.freq = 500;
34         
35     
36     def filterStdSweep(self, freq, low, high, time = 5):
37         """
38         This method will sweep through the range of standard ids given from low to high.
39         This will actively filter for 6 ids at a time and sniff for the given amount of
40         time in seconds. If at least one message is read in then it will go individually
41         through the 6 ids and sniff only for that id for the given amount of time. All the
42         data gathered will be saved. 
43         
44         @type  freq: number
45         @param freq: The frequency at which the bus is communicating
46         @type   low: integer
47         @param  low: The low end of the id sweep
48         @type  high: integer 
49         @param high: The high end of the id sweep
50         @type  time: number
51         @param time: Sniff time for each trial. Default is 5 seconds
52         
53         @rtype: list of numbers
54         @return: A list of all IDs found during the sweep.
55         """
56         msgIDs = []
57         self.client.serInit()
58         self.client.MCPsetup()
59         for i in range(low, high+1, 6):
60             print "sniffing id: %d, %d, %d, %d, %d, %d" % (i,i+1,i+2,i+3,i+4,i+5)
61             comment= "sweepFilter: "
62             #comment = "sweepFilter_%d_%d_%d_%d_%d_%d" % (i,i+1,i+2,i+3,i+4,i+5)
63             description = "Running a sweep filer for all the possible standard IDs. This run filters for: %d, %d, %d, %d, %d, %d" % (i,i+1,i+2,i+3,i+4,i+5)
64             count = self.sniff(freq=freq, duration = time, description = description,comment = comment, standardid = [i, i+1, i+2, i+3, i+4, i+5])
65             if( count != 0):
66                 for j in range(i,i+5):
67                     comment = "sweepFilter: "
68                     #comment = "sweepFilter: %d" % (j)
69                     description = "Running a sweep filer for all the possible standard IDs. This run filters for: %d " % j
70                     count = self.sniff(freq=freq, duration = time, description = description,comment = comment, standardid = [j, j, j, j])
71                     if( count != 0):
72                         msgIDs.append(j)
73         return msgIDs
74     
75     
76     def sweepRandom(self, freq, number = 5, time = 5):
77         """
78         This method will choose random values to listen out of all the possible standard ids up to
79         the given number. It will sniff for the given amount of time on each set of ids on the given 
80         frequency. Sniffs in groups of 6 but when at least one message is read in it will go through all
81         six individually before continuing.
82         
83         @type  freq: number
84         @param freq: The frequency at which the bus is communicating
85         @type  number: integer
86         @param number: High end of the possible ids. This will define a range from 0 to number that the ids will be chosen from
87         @type  time: number
88         @param time: Sniff time for each trial. Default is 5 seconds
89         
90         @rtype: list of numbers, list of numbers 
91         @return: A list of all IDs found during the sweep and a list of all the IDs that were listened for throughout the test
92         """
93         msgIDs = [] #standard IDs that we have observed during run
94         ids = [] #standard IDs that have been tried
95         self.client.serInit()
96         self.client.MCPsetup()
97         for i in range(0,number+1,6):
98             idsTemp = []
99             comment = "sweepFilter: "
100             for j in range(0,6,1):
101                 id = randrange(2047)
102                 #comment += "_%d" % id
103                 idsTemp.append(id)
104                 ids.append(id)
105             #print comment
106             description = "Running a sweep filer for all the possible standard IDs. This runs the following : " + comment
107             count = self.sniff(freq=freq, duration=time, description=description, comment = comment, standardid = idsTemp)
108             if( count != 0):
109                 for element in idsTemp:
110                     #comment = "sweepFilter: %d" % (element)
111                     comment="sweepFilter: "
112                     description = "Running a sweep filer for all the possible standard IDs. This run filters for: %d " % element
113                     count = self.sniff(freq=freq, duration = time, description = description,comment = comment, standardid = [element, element, element])
114                     if( count != 0):
115                         msgIDs.append(j)
116         return msgIDs, ids
117     
118     
119     def rtrSweep(self,freq,lowID,highID, attempts = 1,duration = 1, verbose = True):
120         """
121         This method will sweep through the range of ids given by lowID to highID and
122         send a remote transmissions request (RTR) to each id and then listen for a response. 
123         The RTR will be repeated in the given number of attempts and will sniff for the given duration
124         continuing to the next id.
125         
126         @type  freq: number
127         @param freq: The frequency at which the bus is communicating
128         @type   lowID: integer
129         @param  lowID: The low end of the id sweep
130         @type  highID: integer 
131         @param highID: The high end of the id sweep
132         @type attempts: integer
133         @param attempts: The number of times a RTR will be repeated for a given standard id
134         @type  duration: integer
135         @param duration: The length of time that it will listen to the bus after sending an RTR
136         @type verbose:  boolean
137         @param verbose: When true, messages will be printed out to the terminal
138         
139         @rtype: None
140         @return: Does not return anything
141         """
142         #set up file for writing
143         now = datetime.datetime.now()
144         datestr = now.strftime("%Y%m%d")
145         path = self.DATA_LOCATION+datestr+"_rtr.csv"
146         filename = path
147         outfile = open(filename,'a');
148         dataWriter = csv.writer(outfile,delimiter=',');
149         dataWriter.writerow(['# Time     Error        Bytes 1-13']);
150         dataWriter.writerow(['#' + "rtr sweep from %d to %d"%(lowID,highID)])
151         if( verbose):
152             print "started"
153         #self.client.serInit()
154         #self.spitSetup(freq)
155         
156         #for each id
157         for i in range(lowID,highID+1, 1):
158             self.client.serInit()
159             self.spitSetup(freq) #reset the chip to try and avoid serial timeouts
160             #set filters
161             standardid = [i, i, i, i]
162             self.addFilter(standardid, verbose = True)
163             
164             #### split SID into different areas
165             SIDlow = (standardid[0] & 0x07) << 5;  # get SID bits 2:0, rotate them to bits 7:5
166             SIDhigh = (standardid[0] >> 3) & 0xFF; # get SID bits 10:3, rotate them to bits 7:0
167             #create RTR packet
168             packet = [SIDhigh, SIDlow, 0x00,0x00,0x40]
169             dataWriter.writerow(["#requested id %d"%i])
170             #self.client.poke8(0x2C,0x00);  #clear the CANINTF register; we care about bits 0 and 1 (RXnIF flags) which indicate a message is being held 
171             #clear buffer
172             packet1 = self.client.rxpacket();
173             packet2 = self.client.rxpacket();
174             #send in rtr request
175             self.client.txpacket(packet)
176             ## listen for 2 packets. one should be the rtr we requested the other should be
177             ## a new packet response
178             starttime = time.time()
179             while ((time.time() - starttime) < duration): #listen for the given duration time period
180                 packet = self.client.rxpacket()
181                 if( packet == None):
182                     continue
183                 # we have sniffed a packet, save it
184                 row = []
185                 row.append("%f"%time.time()) #timestamp
186                 row.append(0) #error flag (not checkign)
187                 row.append("rtrRequest_%d"%i) #comment
188                 row.append(duration) #sniff time
189                 row.append(1) # filtering boolean
190                 for byte in packet:
191                     row.append("%02x"%ord(byte));
192                 dataWriter.writerow(row)
193                 print self.client.packet2parsedstr(packet)
194             trial= 2;
195             # for each trial repeat
196             while( trial <= attempts):
197                 print "trial: ", trial
198                 self.client.MCPrts(TXB0=True);
199                 starttime = time.time()
200                 # this time we will sniff for the given amount of time to see if there is a
201                 # time till the packets come in
202                 while( (time.time()-starttime) < duration):
203                     packet=self.client.rxpacket();
204                     if( packet == None):
205                         continue
206                     row = []
207                     row.append("%f"%time.time()) #timestamp
208                     row.append(0) #error flag (not checking)
209                     row.append("rtrRequest_%d"%i) #comment
210                     row.append(duration) #sniff time
211                     row.append(1) # filtering boolean
212                     for byte in packet:
213                         row.append("%02x"%ord(byte));
214                     dataWriter.writerow(row)
215                     print self.client.packet2parsedstr(packet)
216                 trial += 1
217         print "sweep complete"
218         outfile.close()
219         
220     # This method will do generation based fuzzing on the id given in standard id
221     # dbLimits is a dictionary of the databytes
222     # dbLimits['db0'] = [low, High]
223     # ..
224     # dbLimits['db7'] = [low, High]
225     # where low is the low end of values for the fuzz, high is the high end value
226     # period is the time between sending packets in milliseconds, writesPerFuzz is the times the 
227     # same fuzzed packet will be injecetez. Fuzzes is the number of different packets to be injected
228     def generationFuzzer(self,freq, standardIDs, dbLimits, period, writesPerFuzz, Fuzzes):
229         """
230         This method will perform generation based fuzzing on the bus. The method will inject
231         properly formatted, randomly generated messages at a given period for a I{writesPerFuzz} 
232         number of times. The packets that are injected into the bus will all be saved in the following path
233         DATALOCATION/InjectedData/(today's date (YYYYMMDD))_GenerationFuzzedPackets.csv. An example filename would be 20130222_GenerationFuzzedPackets.csv
234         Where DATALOCATION is provided when the class is initiated. The data will be saved as integers.
235         Each row will be formatted in the following form::
236                      row = [time of injection, standardID, 8, db0, db1, db2, db3, db4, db5, db6, db7]
237         
238         @type  freq: number
239         @param freq: The frequency at which the bus is communicating
240         @type standardIDs: list of integers
241         @param standardIDs: List of standard IDs the user wishes to fuzz on. An ID will randomly be chosen
242                             with every new random packet generated. If only 1 ID is input in the list then it will
243                             only fuzz on that one ID.
244         @type  dbLimits: dictionary
245         @param dbLimits: This is a dictionary that holds the limits of each bytes values. Each value in the dictionary will be a list 
246                          containing the lowest possible value for the byte and the highest possible value. The form is shown below::
247                             
248                             dbLimits['db0'] = [low, high]
249                             dbLimits['db1'] = [low, high]
250                             ...
251                             dbLimits['db7'] = [low, high] 
252         
253         @type period: number
254         @param period: The time gap between packet inejctions given in milliseconds
255         @type writesPerFuzz: integer
256         @param writesPerFuzz: This will be the number of times that each randomly generated packet will be injected onto the bus
257                               before a new packet is generated
258         @type Fuzzes: integer
259         @param Fuzzes: The number of packets to be generated and injected onto bus
260         
261         @rtype: None
262         @return: This method does not return anything
263                          
264         """
265         #print "Fuzzing on standard ID: %d" %standardId
266         self.client.serInit()
267         self.spitSetup(freq)
268         packet = [0,0,0x00,0x00,0x08,0,0,0,0,0,0,0,0] #empty template
269         #form a basic packet
270         
271 #        #### split SID into different regs
272 #        SIDlow = (standardIds[0] & 0x07) << 5;  # get SID bits 2:0, rotate them to bits 7:5
273 #        SIDhigh = (standardIds[0] >> 3) & 0xFF; # get SID bits 10:3, rotate them to bits 7:0
274 #        
275 #        packet = [SIDhigh, SIDlow, 0x00,0x00, # pad out EID regs
276 #                  0x08, # bit 6 must be set to 0 for data frame (1 for RTR) 
277 #                  # lower nibble is DLC                   
278 #                 packetTemp[0],packetTemp[1],packetTemp[2],packetTemp[3],packetTemp[4],packetTemp[5],packetTemp[6],packetTemp[7]]
279 #        
280         
281         #get folder information (based on today's date)
282         now = datetime.datetime.now()
283         datestr = now.strftime("%Y%m%d")
284         path = self.DATA_LOCATION+"InjectedData/"+datestr+"_GenerationFuzzedPackets.csv"
285         filename = path
286         outfile = open(filename,'a');
287         dataWriter = csv.writer(outfile,delimiter=',');
288         #dataWriter.writerow(['# Time     Error        Bytes 1-13']);
289         #dataWriter.writerow(['#' + description])
290             
291         numIds = len(standardIDs)
292         fuzzNumber = 0; #: counts the number of packets we have generated
293         while( fuzzNumber < Fuzzes):
294             id_new = standardIDs[random.randint(0,numIds-1)]
295             print id_new
296             #### split SID into different regs
297             SIDhigh = (id_new >> 3) & 0xFF; # get SID bits 10:3, rotate them to bits 7:0
298             SIDlow = (id_new & 0x07) << 5;  # get SID bits 2:0, rotate them to bits 7:5
299             packet[0] = SIDhigh
300             packet[1] = SIDlow
301             
302             #generate a fuzzed packet
303             for i in range(0,8): # for each data byte, fuzz it
304                 idx = "db%d"%i
305                 limits = dbLimits[idx]
306                 value = random.randint(limits[0],limits[1]) #generate pseudo-random integer value
307                 packet[i+5] = value
308             print packet
309             #put a rough time stamp on the data and get all the data bytes    
310             row = [time.time(), id_new,8]
311             msg = "Injecting: "
312             for i in range(5,13):
313                 row.append(packet[i])
314                 msg += " %d"%packet[i]
315             #print msg
316             dataWriter.writerow(row)
317             self.client.txpacket(packet)
318             time.sleep(period/1000)
319             
320             #inject the packet the given number of times. 
321             for i in range(1,writesPerFuzz):
322                 self.client.MCPrts(TXB0=True)
323                 time.sleep(period/1000)
324             fuzzNumber += 1
325         print "Fuzzing Complete"   
326         outfile.close()
327             
328     
329 #    def generationFuzzRandomID(self, freq, standardIDs, dbLimits, period, writesPerFuzz, Fuzzes):
330 #        print "Fuzzing on standard ID: %d" %standardId
331 #        self.client.serInit()
332 #        self.spitSetup(freq)
333 #        packetTemp = [0,0,0,0,0,0,0,0]
334 #        #form a basic packet
335 #        
336 #        #### split SID into different regs
337 #        SIDlow = (standardId & 0x07) << 5;  # get SID bits 2:0, rotate them to bits 7:5
338 #        SIDhigh = (standardId >> 3) & 0xFF; # get SID bits 10:3, rotate them to bits 7:0
339 #        
340 #        packet = [SIDhigh, SIDlow, 0x00,0x00, # pad out EID regs
341 #                  0x08, # bit 6 must be set to 0 for data frame (1 for RTR) 
342 #                  # lower nibble is DLC                   
343 #                 packetTemp[0],packetTemp[1],packetTemp[2],packetTemp[3],packetTemp[4],packetTemp[5],packetTemp[6],packetTemp[7]]
344 #        
345 #        
346 #        #get folder information (based on today's date)
347 #        now = datetime.datetime.now()
348 #        datestr = now.strftime("%Y%m%d")
349 #        path = self.DATA_LOCATION+"InjectedData/"+datestr+"_GenerationFuzzedPackets.csv"
350 #        filename = path
351 #        outfile = open(filename,'a');
352 #        dataWriter = csv.writer(outfile,delimiter=',');
353 #        #dataWriter.writerow(['# Time     Error        Bytes 1-13']);
354 #        #dataWriter.writerow(['#' + description])
355 #            
356 #        numIds = len(standardIDs)
357 #        fuzzNumber = 0;
358 #        while( fuzzNumber < Fuzzes):
359 #            id_new = standsardIDs[random.randint(0,numIds-1)]
360 #            #### split SID into different regs
361 #            SIDlow = (id_new & 0x07) << 5;  # get SID bits 2:0, rotate them to bits 7:5
362 #            SIDhigh = (id_new >> 3) & 0xFF; # get SID bits 10:3, rotate them to bits 7:0
363 #            packet[0] = SIDhigh
364 #            packet[1] = SIDlow
365 #            
366 #            #generate a fuzzed packet
367 #            for i in range(0,8): # for each databyte, fuzz it
368 #                idx = "db%d"%i
369 #                limits = dbLimits[idx]
370 #                value = random.randint(limits[0],limits[1]) #generate pseudo-random integer value
371 #                packet[i+5] = value
372 #            
373 #            #put a rough time stamp on the data and get all the data bytes    
374 #            row = [time.time(), standardId,8]
375 #            msg = "Injecting: "
376 #            for i in range(5,13):
377 #                row.append(packet[i])
378 #                msg += " %d"%packet[i]
379 #            #print msg
380 #            dataWriter.writerow(row)
381 #            self.client.txpacket(packet)
382 #            #inject the packet repeatily 
383 #            for i in range(1,writesPerFuzz):
384 #                self.client.MCPrts(TXB0=True)
385 #                time.sleep(period/1000)
386 #            fuzzNumber += 1
387 #        print "Fuzzing Complete"   
388 #        outfile.close()