4 import csv, time, argparse;
7 from random import randrange
9 from GoodFETMCPCAN import GoodFETMCPCAN;
10 from GoodFETMCPCANCommunication import GoodFETMCPCANCommunication
11 from intelhex import IntelHex;
18 class experiments(GoodFETMCPCANCommunication):
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/}
25 def __init__(self, data_location):
28 @type datalocation: string
29 @param datalocation: path to the folder where data will be stored
31 GoodFETMCPCANCommunication.__init__(self, data_location)
32 #super(experiments,self).__init(self)
36 def filterStdSweep(self, freq, low, high, time = 5):
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.
45 @param freq: The frequency at which the bus is communicating
47 @param low: The low end of the id sweep
49 @param high: The high end of the id sweep
51 @param time: Sniff time for each trial. Default is 5 seconds
53 @rtype: list of numbers
54 @return: A list of all IDs found during the sweep.
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])
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])
76 def sweepRandom(self, freq, number = 5, time = 5):
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.
84 @param freq: The frequency at which the bus is communicating
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
88 @param time: Sniff time for each trial. Default is 5 seconds
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
93 msgIDs = [] #standard IDs that we have observed during run
94 ids = [] #standard IDs that have been tried
96 self.client.MCPsetup()
97 for i in range(0,number+1,6):
99 comment = "sweepFilter: "
100 for j in range(0,6,1):
102 #comment += "_%d" % id
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)
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])
119 def rtrSweep(self,freq,lowID,highID, attempts = 1,duration = 1, verbose = True):
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.
127 @param freq: The frequency at which the bus is communicating
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
140 @return: Does not return anything
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"
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)])
153 #self.client.serInit()
154 #self.spitSetup(freq)
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
161 standardid = [i, i, i, i]
162 self.addFilter(standardid, verbose = True)
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
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
172 packet1 = self.client.rxpacket();
173 packet2 = self.client.rxpacket();
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()
183 # we have sniffed a packet, save it
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
191 row.append("%02x"%ord(byte));
192 dataWriter.writerow(row)
193 print self.client.packet2parsedstr(packet)
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();
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
213 row.append("%02x"%ord(byte));
214 dataWriter.writerow(row)
215 print self.client.packet2parsedstr(packet)
217 print "sweep complete"
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]
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):
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]
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::
248 dbLimits['db0'] = [low, high]
249 dbLimits['db1'] = [low, high]
251 dbLimits['db7'] = [low, high]
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
262 @return: This method does not return anything
265 #print "Fuzzing on standard ID: %d" %standardId
266 self.client.serInit()
268 packet = [0,0,0x00,0x00,0x08,0,0,0,0,0,0,0,0] #empty template
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
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]]
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"
286 outfile = open(filename,'a');
287 dataWriter = csv.writer(outfile,delimiter=',');
288 #dataWriter.writerow(['# Time Error Bytes 1-13']);
289 #dataWriter.writerow(['#' + description])
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)]
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
302 #generate a fuzzed packet
303 for i in range(0,8): # for each data byte, fuzz it
305 limits = dbLimits[idx]
306 value = random.randint(limits[0],limits[1]) #generate pseudo-random integer value
309 #put a rough time stamp on the data and get all the data bytes
310 row = [time.time(), id_new,8]
312 for i in range(5,13):
313 row.append(packet[i])
314 msg += " %d"%packet[i]
316 dataWriter.writerow(row)
317 self.client.txpacket(packet)
318 time.sleep(period/1000)
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)
325 print "Fuzzing Complete"
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
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
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]]
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"
351 # outfile = open(filename,'a');
352 # dataWriter = csv.writer(outfile,delimiter=',');
353 # #dataWriter.writerow(['# Time Error Bytes 1-13']);
354 # #dataWriter.writerow(['#' + description])
356 # numIds = len(standardIDs)
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
366 # #generate a fuzzed packet
367 # for i in range(0,8): # for each databyte, fuzz it
369 # limits = dbLimits[idx]
370 # value = random.randint(limits[0],limits[1]) #generate pseudo-random integer value
371 # packet[i+5] = value
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]
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)
387 # print "Fuzzing Complete"