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