1 #include "fuzzingwindow.h"
2 #include "ui_fuzzingwindow.h"
3 #include "utility.h"
4 #include <QDebug>
5 #include <QRandomGenerator>
6 #include "mainwindow.h"
7 #include "helpwindow.h"
8 #include "connections/canconmanager.h"
9 #include "filterutility.h"
10 
FuzzingWindow(const QVector<CANFrame> * frames,QWidget * parent)11 FuzzingWindow::FuzzingWindow(const QVector<CANFrame> *frames, QWidget *parent) :
12     QDialog(parent),
13     ui(new Ui::FuzzingWindow)
14 {
15     ui->setupUi(this);
16     setWindowFlags(Qt::Window);
17 
18     modelFrames = frames;
19 
20     fuzzTimer = new QTimer();
21 
22     connect(ui->btnStartStop, &QPushButton::clicked, this, &FuzzingWindow::toggleFuzzing);
23     connect(ui->btnAllFilters, &QPushButton::clicked, this, &FuzzingWindow::setAllFilters);
24     connect(ui->btnNoFilters, &QPushButton::clicked, this, &FuzzingWindow::clearAllFilters);
25     connect(fuzzTimer, &QTimer::timeout, this, &FuzzingWindow::timerTriggered);
26     connect(ui->spinTiming, SIGNAL(valueChanged(int)), this, SLOT(changePlaybackSpeed(int)));
27     connect(ui->listID, &QListWidget::itemChanged, this, &FuzzingWindow::idListChanged);
28     connect(ui->spinBytes, SIGNAL(valueChanged(int)), this, SLOT(changedNumDataBytes(int)));
29     connect(ui->bitfield, SIGNAL(gridClicked(int,int)), this, SLOT(bitfieldClicked(int,int)));
30     connect(ui->txtByte0, &QLineEdit::returnPressed, this, [=](){changedDataByteText(0, ui->txtByte0->text());});
31     connect(ui->txtByte1, &QLineEdit::returnPressed, this, [=](){changedDataByteText(1, ui->txtByte1->text());});
32     connect(ui->txtByte2, &QLineEdit::returnPressed, this, [=](){changedDataByteText(2, ui->txtByte2->text());});
33     connect(ui->txtByte3, &QLineEdit::returnPressed, this, [=](){changedDataByteText(3, ui->txtByte3->text());});
34     connect(ui->txtByte4, &QLineEdit::returnPressed, this, [=](){changedDataByteText(4, ui->txtByte4->text());});
35     connect(ui->txtByte5, &QLineEdit::returnPressed, this, [=](){changedDataByteText(5, ui->txtByte5->text());});
36     connect(ui->txtByte6, &QLineEdit::returnPressed, this, [=](){changedDataByteText(6, ui->txtByte6->text());});
37     connect(ui->txtByte7, &QLineEdit::returnPressed, this, [=](){changedDataByteText(7, ui->txtByte7->text());});
38 
39 
40     connect(MainWindow::getReference(), SIGNAL(framesUpdated(int)), this, SLOT(updatedFrames(int)));
41 
42     refreshIDList();
43 
44     currentlyFuzzing = false;
45 
46     for (int j = 0; j < 64; j++) bitGrid[j] = 1;
47     numBits = 64;
48     bitAccum = 0;
49     redrawGrid();
50 
51     fuzzTimer->setInterval(ui->spinTiming->value());
52 
53     int numBuses = CANConManager::getInstance()->getNumBuses();
54     for (int n = 0; n < numBuses; n++) ui->cbBuses->addItem(QString::number(n));
55     ui->cbBuses->addItem(tr("All"));
56 
57     // Prevent annoying accidental horizontal scrolling when filter list is populated with long interpreted message names
58     ui->listID->horizontalScrollBar()->setEnabled(false);
59 
60     installEventFilter(this);
61 }
62 
~FuzzingWindow()63 FuzzingWindow::~FuzzingWindow()
64 {
65     removeEventFilter(this);
66     delete ui;
67 }
68 
eventFilter(QObject * obj,QEvent * event)69 bool FuzzingWindow::eventFilter(QObject *obj, QEvent *event)
70 {
71     if (event->type() == QEvent::KeyRelease) {
72         QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
73         switch (keyEvent->key())
74         {
75         case Qt::Key_F1:
76             HelpWindow::getRef()->showHelp("fuzzingwindow.html");
77             break;
78         }
79         return true;
80     } else {
81         // standard event processing
82         return QObject::eventFilter(obj, event);
83     }
84     return false;
85 }
86 
updatedFrames(int numFrames)87 void FuzzingWindow::updatedFrames(int numFrames)
88 {
89     int id;
90     if (numFrames == -1) //all frames deleted. Kill the display
91     {
92         ui->listID->clear();
93         foundIDs.clear();
94         refreshIDList();
95     }
96     else if (numFrames == -2) //all new set of frames. Reset
97     {
98         ui->listID->clear();
99         foundIDs.clear();
100         refreshIDList();
101     }
102     else //just got some new frames. See if they are relevant.
103     {
104         if (numFrames > modelFrames->count()) return;
105         for (int i = modelFrames->count() - numFrames; i < modelFrames->count(); i++)
106         {
107             id = modelFrames->at(i).frameId();
108             if (!foundIDs.contains(id))
109             {
110                 foundIDs.append(id);
111                 selectedIDs.append(id);
112                 FilterUtility::createCheckableFilterItem(id, true, ui->listID);
113             }
114         }
115     }
116 }
117 
changePlaybackSpeed(int newSpeed)118 void FuzzingWindow::changePlaybackSpeed(int newSpeed)
119 {
120     fuzzTimer->setInterval(newSpeed);
121 }
122 
changedDataByteText(int which,QString valu)123 void FuzzingWindow::changedDataByteText(int which, QString valu)
124 {
125     int startBit = which * 8;
126     int byt = valu.toInt(nullptr, 16);
127 
128     for (int i = 0; i < 8; i++)
129     {
130         bitGrid[startBit + i] = (byt & (1 << i)) ? 2 : 0;
131     }
132 
133     redrawGrid();
134 }
135 
changedNumDataBytes(int newVal)136 void FuzzingWindow::changedNumDataBytes(int newVal)
137 {
138     qDebug() << "new num bytes: " << newVal;
139 
140     ui->txtByte0->setEnabled((newVal > 0) ? true : false);
141     ui->txtByte1->setEnabled((newVal > 1) ? true : false);
142     ui->txtByte2->setEnabled((newVal > 2) ? true : false);
143     ui->txtByte3->setEnabled((newVal > 3) ? true : false);
144     ui->txtByte4->setEnabled((newVal > 4) ? true : false);
145     ui->txtByte5->setEnabled((newVal > 5) ? true : false);
146     ui->txtByte6->setEnabled((newVal > 6) ? true : false);
147     ui->txtByte7->setEnabled((newVal > 7) ? true : false);
148 
149     int byt;
150     for (int i = 0; i < 64; i++)
151     {
152         byt = i / 8;
153         if (byt >= newVal)
154         {
155             bitGrid[i] = 3;
156         }
157         else
158         {
159             if (bitGrid[i] == 3) bitGrid[i] = 1;
160         }
161     }
162 
163     redrawGrid();
164 }
165 
timerTriggered()166 void FuzzingWindow::timerTriggered()
167 {
168     static uint64_t lastByteUpdate = 0;
169     CANFrame thisFrame;
170     sendingBuffer.clear();
171     //Every 250ms update the text fields to show our progress and what's going on.
172     if (QDateTime::currentMSecsSinceEpoch() - lastByteUpdate > 250)
173     {
174         ui->txtByte0->setText(QString::number(currentBytes[0], 16));
175         ui->txtByte1->setText(QString::number(currentBytes[1], 16));
176         ui->txtByte2->setText(QString::number(currentBytes[2], 16));
177         ui->txtByte3->setText(QString::number(currentBytes[3], 16));
178         ui->txtByte4->setText(QString::number(currentBytes[4], 16));
179         ui->txtByte5->setText(QString::number(currentBytes[5], 16));
180         ui->txtByte6->setText(QString::number(currentBytes[6], 16));
181         ui->txtByte7->setText(QString::number(currentBytes[7], 16));
182     }
183     int buses = ui->cbBuses->currentIndex();
184     for (int count = 0; count < ui->spinBurst->value(); count++)
185     {
186         thisFrame.setFrameId(currentID);
187         QByteArray bytes(ui->spinBytes->value(), 0);
188         for (int i = 0; i < bytes.length(); i++) bytes[i] = currentBytes[i];
189         thisFrame.setPayload(bytes);
190         if (currentID > 0x7FF) thisFrame.setExtendedFrameFormat(true);
191         else thisFrame.setExtendedFrameFormat(false);
192         thisFrame.bus = 0; //hard coded for now. TODO: do not hard code
193 
194         if (buses < (ui->cbBuses->count() - 1))
195         {
196             thisFrame.bus = buses;
197             sendingBuffer.append(thisFrame);
198         }
199         else //fuzz all the buses! HACK THE PLANET! Er, something...
200         {
201             for (int j = 0; j < ui->cbBuses->count() - 1; j++)
202             {
203                 thisFrame.bus = j;
204                 sendingBuffer.append(thisFrame);
205             }
206         }
207 
208         calcNextID();
209         calcNextBitPattern();
210         numSentFrames++;
211     }
212     CANConManager::getInstance()->sendFrames(sendingBuffer);
213     ui->lblNumFrames->setText("# of sent frames: " + QString::number(numSentFrames));
214 }
215 
clearAllFilters()216 void FuzzingWindow::clearAllFilters()
217 {
218     for (int i = 0; i < ui->listID->count(); i++)
219     {
220         ui->listID->item(i)->setCheckState(Qt::Unchecked);
221     }
222 }
223 
setAllFilters()224 void FuzzingWindow::setAllFilters()
225 {
226     for (int i = 0; i < ui->listID->count(); i++)
227     {
228         ui->listID->item(i)->setCheckState(Qt::Checked);
229     }
230 }
231 
calcNextID()232 void FuzzingWindow::calcNextID()
233 {
234     if (seqIDScan)
235     {
236         if (rangeIDSelect)
237         {
238             currentID++;
239             if (currentID > endID) currentID = startID;
240         }
241         else //IDs by filter. So, select the first filter
242         {
243             currentIdx++;
244             if (currentIdx >= selectedIDs.length()) currentIdx = 0;
245             currentID = selectedIDs[currentIdx];
246             qDebug() << "idx id: " << currentID;
247         }
248     }
249     else //random IDs
250     {
251         if (rangeIDSelect)
252         {
253             int range = endID - startID + 1;
254             if (range != 1) currentID = startID + QRandomGenerator::global()->bounded(range);
255             else currentID = startID;
256         }
257         else //IDs by filter so pick a random selected ID from the filter list
258         {
259             currentIdx = QRandomGenerator::global()->bounded(selectedIDs.length());
260             currentID = selectedIDs[currentIdx];
261         }
262     }
263 }
264 
calcNextBitPattern()265 void FuzzingWindow::calcNextBitPattern()
266 {
267     uint64_t accum;
268 
269     switch (bitSequenceType)
270     {
271     case BitSequenceType::Random:
272         int thisBit;
273         for (int byt = 0; byt < ui->spinBytes->value(); byt++)
274         {
275             currentBytes[byt] = 0;
276             for (int bit = 0; bit < 8; bit++)
277             {
278                 thisBit = bitGrid[byt * 8 + bit];
279                 if (thisBit == 1)
280                 {
281                     if ((QRandomGenerator::global()->bounded(2)) == 1) currentBytes[byt] |= (1 << bit);
282                 }
283                 if (thisBit == 2) currentBytes[byt] |= (1 << bit);
284             }
285         }
286         break;
287     case BitSequenceType::Sequential:
288         bitAccum++;
289         bitAccum &= ((1 << numBits) - 1);
290         accum = bitAccum;
291         for (int byt = 0; byt < ui->spinBytes->value(); byt++)
292         {
293             currentBytes[byt] = 0;
294             for (int bit = 0; bit < 8; bit++)
295             {
296                 thisBit = bitGrid[byt * 8 + bit];
297                 if (thisBit == 1)
298                 {
299                     if (accum & 1) currentBytes[byt] |= (1 << bit);
300                     accum >>= 1;
301                 }
302                 if (thisBit == 2) currentBytes[byt] |= (1 << bit);
303             }
304         }
305         break;
306     case BitSequenceType::Sweeping:
307         qDebug() << "Start " << bitAccum;
308         accum = bitAccum;
309 
310         int offset;
311         for (int i = 1; i < 64; i++)
312         {
313             offset = (i + bitAccum) % 64;
314             if (bitGrid[offset] == 1)
315             {
316                 bitAccum = offset;
317                 qDebug() << "End " << bitAccum;
318                 break;
319             }
320         }
321 
322         for (int byt = 0; byt < ui->spinBytes->value(); byt++)
323         {
324             currentBytes[byt] = 0;
325             for (int bit = 0; bit < 8; bit++)
326             {
327                 thisBit = bitGrid[byt * 8 + bit];
328                 if ( (thisBit == 1) && (unsigned int)(byt * 8 + bit) == bitAccum)
329                 {
330                     currentBytes[byt] |= (1 << bit);
331                 }
332                 if (thisBit == 2) currentBytes[byt] |= (1 << bit);
333             }
334         }
335         break;
336     }
337 }
338 
toggleFuzzing()339 void FuzzingWindow::toggleFuzzing()
340 {
341     if (currentlyFuzzing) //stop it then
342     {
343         ui->btnStartStop->setText("Start Fuzzing");
344         currentlyFuzzing = false;
345         fuzzTimer->stop();
346     }
347     else //start it then
348     {
349         ui->btnStartStop->setText("Stop Fuzzing");
350         currentlyFuzzing = true;
351 
352         startID = Utility::ParseStringToNum(ui->txtStartID->text());
353         endID = Utility::ParseStringToNum(ui->txtEndID->text());
354 
355         seqIDScan = ui->rbSequentialID->isChecked();
356         rangeIDSelect = ui->rbRangeIDSel->isChecked();
357         if (ui->rbSequentialBits->isChecked()) bitSequenceType = BitSequenceType::Sequential;
358         if (ui->rbRandomBits->isChecked())
359         {
360             bitSequenceType = BitSequenceType::Random;
361             bitAccum = 0;
362         }
363         if (ui->rbSweep->isChecked())
364         {
365             bitSequenceType = BitSequenceType::Sweeping;
366             for (int i = 0; i < 64; i++)
367             {
368                 if (bitGrid[i] == 1)
369                 {
370                     bitAccum = i;
371                     break;
372                 }
373             }
374         }
375 
376         numSentFrames = 0;
377 
378         if (seqIDScan)
379         {
380             if (rangeIDSelect)
381             {
382                 currentID = startID;
383             }
384             else //IDs by filter. So, select the first filter
385             {
386                 currentIdx = 0;
387                 currentID = selectedIDs[currentIdx];
388             }
389         }
390         else //random IDs
391         {
392             if (rangeIDSelect)
393             {
394                 int range = endID - startID;
395                 if (range != 0) currentID = startID + QRandomGenerator::global()->bounded(range);
396                 else currentID = startID;
397             }
398             else //IDs by filter so pick a random selected ID from the filter list
399             {
400                 currentIdx = QRandomGenerator::global()->bounded(selectedIDs.length());
401                 currentID = selectedIDs[currentIdx];
402             }
403         }
404 
405         calcNextBitPattern();
406 
407         fuzzTimer->start();
408     }
409 }
410 
refreshIDList()411 void FuzzingWindow::refreshIDList()
412 {
413     ui->listID->clear();
414     foundIDs.clear();
415 
416     int id;
417     for (int i = 0; i < modelFrames->count(); i++)
418     {
419         CANFrame thisFrame = modelFrames->at(i);
420         id = thisFrame.frameId();
421         if (!foundIDs.contains(id))
422         {
423             foundIDs.append(id);
424             selectedIDs.append(id);
425             FilterUtility::createCheckableFilterItem(id, true, ui->listID);
426         }
427     }
428     //default is to sort in ascending order
429     ui->listID->sortItems();
430 }
431 
idListChanged(QListWidgetItem * item)432 void FuzzingWindow::idListChanged(QListWidgetItem *item)
433 {
434     int id = FilterUtility::getIdAsInt(item);
435     if (item->checkState() == Qt::Checked)
436     {
437         if (!selectedIDs.contains(id))
438         {
439             qDebug() << "adding " << id << " to list of selected IDs";
440             selectedIDs.append(id);
441         }
442     }
443     else
444     {
445         qDebug() << "removing " << id << " from the list of selected ids";
446         selectedIDs.removeOne(id);
447     }
448 }
449 
450 /*
451 bitGrid stores the state of all 64 bits.
452 The grid is capable of showing the following colors:
453 White = not used (left as 0)
454 Gray = past the end of the valid bits (because of # of data bytes requested)
455 Green = fuzz it
456 black = always keep it set to 1
457 */
bitfieldClicked(int x,int y)458 void FuzzingWindow::bitfieldClicked(int x, int y)
459 {
460     qDebug() << "X: " << x << " Y: " << y;
461     int bit = (7 - x) + (y * 8);
462     if (bitGrid[bit] == 3) return; //naughty!
463     bitGrid[bit]++;
464     if (bitGrid[bit] > 2) bitGrid[bit] = 0;
465 
466     redrawGrid();
467 }
468 
redrawGrid()469 void FuzzingWindow::redrawGrid()
470 {
471     //now update the bits in the bitfield control
472     uint8_t refBytes[8];
473     uint8_t dataBytes[8];
474     uint8_t usedBytes[8];
475 
476     for (int j = 0; j < 8; j++)
477     {
478         refBytes[j] = 0;
479         dataBytes[j] = 0;
480         usedBytes[j] = 0;
481     }
482 
483     numBits = 0;
484 
485     for (int i = 0; i < 64; i++)
486     {
487         int byt = i / 8;
488         int bit = i % 8;
489         switch (bitGrid[i])
490         {
491         case 0: //white, keep this bit off always
492             break;
493         case 1: //Green, fuzz this bit
494             dataBytes[byt] |= (1 << bit);
495             numBits++;
496             break;
497         case 2: //black, bit always set
498             dataBytes[byt] |= (1 << bit);
499             refBytes[byt] |= (1 << bit);
500             break;
501         case 3: //gray, this bit doesn't exist
502             usedBytes[byt] |= (1 << bit);
503             break;
504         }
505     }
506 
507     ui->bitfield->setUsed(usedBytes, false);
508     ui->bitfield->setReference(refBytes, false);
509     ui->bitfield->updateData(dataBytes, true);
510 }
511