now for both changed bit and signal views. There are still some graphical tweaks to do. Probably doesn't quite work in fuzzing window just yet.
511 lines
15 KiB
C++
511 lines
15 KiB
C++
#include "fuzzingwindow.h"
|
|
#include "ui_fuzzingwindow.h"
|
|
#include "utility.h"
|
|
#include <QDebug>
|
|
#include <QRandomGenerator>
|
|
#include "mainwindow.h"
|
|
#include "helpwindow.h"
|
|
#include "connections/canconmanager.h"
|
|
#include "filterutility.h"
|
|
|
|
FuzzingWindow::FuzzingWindow(const QVector<CANFrame> *frames, QWidget *parent) :
|
|
QDialog(parent),
|
|
ui(new Ui::FuzzingWindow)
|
|
{
|
|
ui->setupUi(this);
|
|
setWindowFlags(Qt::Window);
|
|
|
|
modelFrames = frames;
|
|
|
|
fuzzTimer = new QTimer();
|
|
|
|
connect(ui->btnStartStop, &QPushButton::clicked, this, &FuzzingWindow::toggleFuzzing);
|
|
connect(ui->btnAllFilters, &QPushButton::clicked, this, &FuzzingWindow::setAllFilters);
|
|
connect(ui->btnNoFilters, &QPushButton::clicked, this, &FuzzingWindow::clearAllFilters);
|
|
connect(fuzzTimer, &QTimer::timeout, this, &FuzzingWindow::timerTriggered);
|
|
connect(ui->spinTiming, SIGNAL(valueChanged(int)), this, SLOT(changePlaybackSpeed(int)));
|
|
connect(ui->listID, &QListWidget::itemChanged, this, &FuzzingWindow::idListChanged);
|
|
connect(ui->spinBytes, SIGNAL(valueChanged(int)), this, SLOT(changedNumDataBytes(int)));
|
|
connect(ui->bitfield, SIGNAL(gridClicked(int)), this, SLOT(bitfieldClicked(int)));
|
|
connect(ui->txtByte0, &QLineEdit::returnPressed, this, [=](){changedDataByteText(0, ui->txtByte0->text());});
|
|
connect(ui->txtByte1, &QLineEdit::returnPressed, this, [=](){changedDataByteText(1, ui->txtByte1->text());});
|
|
connect(ui->txtByte2, &QLineEdit::returnPressed, this, [=](){changedDataByteText(2, ui->txtByte2->text());});
|
|
connect(ui->txtByte3, &QLineEdit::returnPressed, this, [=](){changedDataByteText(3, ui->txtByte3->text());});
|
|
connect(ui->txtByte4, &QLineEdit::returnPressed, this, [=](){changedDataByteText(4, ui->txtByte4->text());});
|
|
connect(ui->txtByte5, &QLineEdit::returnPressed, this, [=](){changedDataByteText(5, ui->txtByte5->text());});
|
|
connect(ui->txtByte6, &QLineEdit::returnPressed, this, [=](){changedDataByteText(6, ui->txtByte6->text());});
|
|
connect(ui->txtByte7, &QLineEdit::returnPressed, this, [=](){changedDataByteText(7, ui->txtByte7->text());});
|
|
|
|
|
|
connect(MainWindow::getReference(), SIGNAL(framesUpdated(int)), this, SLOT(updatedFrames(int)));
|
|
|
|
refreshIDList();
|
|
|
|
currentlyFuzzing = false;
|
|
|
|
for (int j = 0; j < 512; j++) bitGrid[j] = 1;
|
|
numBits = 64;
|
|
bitAccum = 0;
|
|
redrawGrid();
|
|
|
|
fuzzTimer->setInterval(ui->spinTiming->value());
|
|
|
|
int numBuses = CANConManager::getInstance()->getNumBuses();
|
|
for (int n = 0; n < numBuses; n++) ui->cbBuses->addItem(QString::number(n));
|
|
ui->cbBuses->addItem(tr("All"));
|
|
|
|
// Prevent annoying accidental horizontal scrolling when filter list is populated with long interpreted message names
|
|
ui->listID->horizontalScrollBar()->setEnabled(false);
|
|
|
|
installEventFilter(this);
|
|
}
|
|
|
|
FuzzingWindow::~FuzzingWindow()
|
|
{
|
|
removeEventFilter(this);
|
|
delete ui;
|
|
}
|
|
|
|
bool FuzzingWindow::eventFilter(QObject *obj, QEvent *event)
|
|
{
|
|
if (event->type() == QEvent::KeyRelease) {
|
|
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
|
|
switch (keyEvent->key())
|
|
{
|
|
case Qt::Key_F1:
|
|
HelpWindow::getRef()->showHelp("fuzzingwindow.md");
|
|
break;
|
|
}
|
|
return true;
|
|
} else {
|
|
// standard event processing
|
|
return QObject::eventFilter(obj, event);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void FuzzingWindow::updatedFrames(int numFrames)
|
|
{
|
|
int id;
|
|
if (numFrames == -1) //all frames deleted. Kill the display
|
|
{
|
|
ui->listID->clear();
|
|
foundIDs.clear();
|
|
refreshIDList();
|
|
}
|
|
else if (numFrames == -2) //all new set of frames. Reset
|
|
{
|
|
ui->listID->clear();
|
|
foundIDs.clear();
|
|
refreshIDList();
|
|
}
|
|
else //just got some new frames. See if they are relevant.
|
|
{
|
|
if (numFrames > modelFrames->count()) return;
|
|
for (int i = modelFrames->count() - numFrames; i < modelFrames->count(); i++)
|
|
{
|
|
id = modelFrames->at(i).frameId();
|
|
if (!foundIDs.contains(id))
|
|
{
|
|
foundIDs.append(id);
|
|
selectedIDs.append(id);
|
|
FilterUtility::createCheckableFilterItem(id, true, ui->listID);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FuzzingWindow::changePlaybackSpeed(int newSpeed)
|
|
{
|
|
fuzzTimer->setInterval(newSpeed);
|
|
}
|
|
|
|
void FuzzingWindow::changedDataByteText(int which, QString valu)
|
|
{
|
|
int startBit = which * 8;
|
|
int byt = valu.toInt(nullptr, 16);
|
|
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
bitGrid[startBit + i] = (byt & (1 << i)) ? 2 : 0;
|
|
}
|
|
|
|
redrawGrid();
|
|
}
|
|
|
|
void FuzzingWindow::changedNumDataBytes(int newVal)
|
|
{
|
|
qDebug() << "new num bytes: " << newVal;
|
|
|
|
ui->txtByte0->setEnabled((newVal > 0) ? true : false);
|
|
ui->txtByte1->setEnabled((newVal > 1) ? true : false);
|
|
ui->txtByte2->setEnabled((newVal > 2) ? true : false);
|
|
ui->txtByte3->setEnabled((newVal > 3) ? true : false);
|
|
ui->txtByte4->setEnabled((newVal > 4) ? true : false);
|
|
ui->txtByte5->setEnabled((newVal > 5) ? true : false);
|
|
ui->txtByte6->setEnabled((newVal > 6) ? true : false);
|
|
ui->txtByte7->setEnabled((newVal > 7) ? true : false);
|
|
|
|
ui->bitfield->setBytesToDraw(newVal);
|
|
|
|
int byt;
|
|
for (int i = 0; i < 511; i++)
|
|
{
|
|
byt = i / 8;
|
|
if (byt >= newVal)
|
|
{
|
|
bitGrid[i] = 3;
|
|
}
|
|
else
|
|
{
|
|
if (bitGrid[i] == 3) bitGrid[i] = 1;
|
|
}
|
|
}
|
|
|
|
redrawGrid();
|
|
}
|
|
|
|
void FuzzingWindow::timerTriggered()
|
|
{
|
|
static uint64_t lastByteUpdate = 0;
|
|
CANFrame thisFrame;
|
|
sendingBuffer.clear();
|
|
//Every 250ms update the text fields to show our progress and what's going on.
|
|
if (QDateTime::currentMSecsSinceEpoch() - lastByteUpdate > 250)
|
|
{
|
|
ui->txtByte0->setText(QString::number(currentBytes[0], 16));
|
|
ui->txtByte1->setText(QString::number(currentBytes[1], 16));
|
|
ui->txtByte2->setText(QString::number(currentBytes[2], 16));
|
|
ui->txtByte3->setText(QString::number(currentBytes[3], 16));
|
|
ui->txtByte4->setText(QString::number(currentBytes[4], 16));
|
|
ui->txtByte5->setText(QString::number(currentBytes[5], 16));
|
|
ui->txtByte6->setText(QString::number(currentBytes[6], 16));
|
|
ui->txtByte7->setText(QString::number(currentBytes[7], 16));
|
|
}
|
|
int buses = ui->cbBuses->currentIndex();
|
|
for (int count = 0; count < ui->spinBurst->value(); count++)
|
|
{
|
|
thisFrame.setFrameId(currentID);
|
|
QByteArray bytes(ui->spinBytes->value(), 0);
|
|
for (int i = 0; i < bytes.length(); i++) bytes[i] = currentBytes[i];
|
|
thisFrame.setPayload(bytes);
|
|
if (currentID > 0x7FF) thisFrame.setExtendedFrameFormat(true);
|
|
else thisFrame.setExtendedFrameFormat(false);
|
|
thisFrame.bus = 0; //hard coded for now. TODO: do not hard code
|
|
|
|
if (buses < (ui->cbBuses->count() - 1))
|
|
{
|
|
thisFrame.bus = buses;
|
|
sendingBuffer.append(thisFrame);
|
|
}
|
|
else //fuzz all the buses! HACK THE PLANET! Er, something...
|
|
{
|
|
for (int j = 0; j < ui->cbBuses->count() - 1; j++)
|
|
{
|
|
thisFrame.bus = j;
|
|
sendingBuffer.append(thisFrame);
|
|
}
|
|
}
|
|
|
|
calcNextID();
|
|
calcNextBitPattern();
|
|
numSentFrames++;
|
|
}
|
|
CANConManager::getInstance()->sendFrames(sendingBuffer);
|
|
ui->lblNumFrames->setText("# of sent frames: " + QString::number(numSentFrames));
|
|
}
|
|
|
|
void FuzzingWindow::clearAllFilters()
|
|
{
|
|
for (int i = 0; i < ui->listID->count(); i++)
|
|
{
|
|
ui->listID->item(i)->setCheckState(Qt::Unchecked);
|
|
}
|
|
}
|
|
|
|
void FuzzingWindow::setAllFilters()
|
|
{
|
|
for (int i = 0; i < ui->listID->count(); i++)
|
|
{
|
|
ui->listID->item(i)->setCheckState(Qt::Checked);
|
|
}
|
|
}
|
|
|
|
void FuzzingWindow::calcNextID()
|
|
{
|
|
if (seqIDScan)
|
|
{
|
|
if (rangeIDSelect)
|
|
{
|
|
currentID++;
|
|
if (currentID > endID) currentID = startID;
|
|
}
|
|
else //IDs by filter. So, select the first filter
|
|
{
|
|
currentIdx++;
|
|
if (currentIdx >= selectedIDs.length()) currentIdx = 0;
|
|
currentID = selectedIDs[currentIdx];
|
|
qDebug() << "idx id: " << currentID;
|
|
}
|
|
}
|
|
else //random IDs
|
|
{
|
|
if (rangeIDSelect)
|
|
{
|
|
int range = endID - startID + 1;
|
|
if (range != 1) currentID = startID + QRandomGenerator::global()->bounded(range);
|
|
else currentID = startID;
|
|
}
|
|
else //IDs by filter so pick a random selected ID from the filter list
|
|
{
|
|
currentIdx = QRandomGenerator::global()->bounded(selectedIDs.length());
|
|
currentID = selectedIDs[currentIdx];
|
|
}
|
|
}
|
|
}
|
|
|
|
void FuzzingWindow::calcNextBitPattern()
|
|
{
|
|
uint64_t accum;
|
|
|
|
switch (bitSequenceType)
|
|
{
|
|
case BitSequenceType::Random:
|
|
int thisBit;
|
|
for (int byt = 0; byt < ui->spinBytes->value(); byt++)
|
|
{
|
|
currentBytes[byt] = 0;
|
|
for (int bit = 0; bit < 8; bit++)
|
|
{
|
|
thisBit = bitGrid[byt * 8 + bit];
|
|
if (thisBit == 1)
|
|
{
|
|
if ((QRandomGenerator::global()->bounded(2)) == 1) currentBytes[byt] |= (1 << bit);
|
|
}
|
|
if (thisBit == 2) currentBytes[byt] |= (1 << bit);
|
|
}
|
|
}
|
|
break;
|
|
case BitSequenceType::Sequential:
|
|
bitAccum++;
|
|
bitAccum &= ((1 << numBits) - 1);
|
|
accum = bitAccum;
|
|
for (int byt = 0; byt < ui->spinBytes->value(); byt++)
|
|
{
|
|
currentBytes[byt] = 0;
|
|
for (int bit = 0; bit < 8; bit++)
|
|
{
|
|
thisBit = bitGrid[byt * 8 + bit];
|
|
if (thisBit == 1)
|
|
{
|
|
if (accum & 1) currentBytes[byt] |= (1 << bit);
|
|
accum >>= 1;
|
|
}
|
|
if (thisBit == 2) currentBytes[byt] |= (1 << bit);
|
|
}
|
|
}
|
|
break;
|
|
case BitSequenceType::Sweeping:
|
|
qDebug() << "Start " << bitAccum;
|
|
accum = bitAccum;
|
|
|
|
int offset;
|
|
for (int i = 1; i < 64; i++)
|
|
{
|
|
offset = (i + bitAccum) % 64;
|
|
if (bitGrid[offset] == 1)
|
|
{
|
|
bitAccum = offset;
|
|
qDebug() << "End " << bitAccum;
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (int byt = 0; byt < ui->spinBytes->value(); byt++)
|
|
{
|
|
currentBytes[byt] = 0;
|
|
for (int bit = 0; bit < 8; bit++)
|
|
{
|
|
thisBit = bitGrid[byt * 8 + bit];
|
|
if ( (thisBit == 1) && (unsigned int)(byt * 8 + bit) == bitAccum)
|
|
{
|
|
currentBytes[byt] |= (1 << bit);
|
|
}
|
|
if (thisBit == 2) currentBytes[byt] |= (1 << bit);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void FuzzingWindow::toggleFuzzing()
|
|
{
|
|
if (currentlyFuzzing) //stop it then
|
|
{
|
|
ui->btnStartStop->setText("Start Fuzzing");
|
|
currentlyFuzzing = false;
|
|
fuzzTimer->stop();
|
|
}
|
|
else //start it then
|
|
{
|
|
ui->btnStartStop->setText("Stop Fuzzing");
|
|
currentlyFuzzing = true;
|
|
|
|
startID = Utility::ParseStringToNum(ui->txtStartID->text());
|
|
endID = Utility::ParseStringToNum(ui->txtEndID->text());
|
|
|
|
seqIDScan = ui->rbSequentialID->isChecked();
|
|
rangeIDSelect = ui->rbRangeIDSel->isChecked();
|
|
if (ui->rbSequentialBits->isChecked()) bitSequenceType = BitSequenceType::Sequential;
|
|
if (ui->rbRandomBits->isChecked())
|
|
{
|
|
bitSequenceType = BitSequenceType::Random;
|
|
bitAccum = 0;
|
|
}
|
|
if (ui->rbSweep->isChecked())
|
|
{
|
|
bitSequenceType = BitSequenceType::Sweeping;
|
|
for (int i = 0; i < 64; i++)
|
|
{
|
|
if (bitGrid[i] == 1)
|
|
{
|
|
bitAccum = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
numSentFrames = 0;
|
|
|
|
if (seqIDScan)
|
|
{
|
|
if (rangeIDSelect)
|
|
{
|
|
currentID = startID;
|
|
}
|
|
else //IDs by filter. So, select the first filter
|
|
{
|
|
currentIdx = 0;
|
|
currentID = selectedIDs[currentIdx];
|
|
}
|
|
}
|
|
else //random IDs
|
|
{
|
|
if (rangeIDSelect)
|
|
{
|
|
int range = endID - startID;
|
|
if (range != 0) currentID = startID + QRandomGenerator::global()->bounded(range);
|
|
else currentID = startID;
|
|
}
|
|
else //IDs by filter so pick a random selected ID from the filter list
|
|
{
|
|
currentIdx = QRandomGenerator::global()->bounded(selectedIDs.length());
|
|
currentID = selectedIDs[currentIdx];
|
|
}
|
|
}
|
|
|
|
calcNextBitPattern();
|
|
|
|
fuzzTimer->start();
|
|
}
|
|
}
|
|
|
|
void FuzzingWindow::refreshIDList()
|
|
{
|
|
ui->listID->clear();
|
|
foundIDs.clear();
|
|
|
|
int id;
|
|
for (int i = 0; i < modelFrames->count(); i++)
|
|
{
|
|
CANFrame thisFrame = modelFrames->at(i);
|
|
id = thisFrame.frameId();
|
|
if (!foundIDs.contains(id))
|
|
{
|
|
foundIDs.append(id);
|
|
selectedIDs.append(id);
|
|
FilterUtility::createCheckableFilterItem(id, true, ui->listID);
|
|
}
|
|
}
|
|
//default is to sort in ascending order
|
|
ui->listID->sortItems();
|
|
}
|
|
|
|
void FuzzingWindow::idListChanged(QListWidgetItem *item)
|
|
{
|
|
int id = FilterUtility::getIdAsInt(item);
|
|
if (item->checkState() == Qt::Checked)
|
|
{
|
|
if (!selectedIDs.contains(id))
|
|
{
|
|
qDebug() << "adding " << id << " to list of selected IDs";
|
|
selectedIDs.append(id);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
qDebug() << "removing " << id << " from the list of selected ids";
|
|
selectedIDs.removeOne(id);
|
|
}
|
|
}
|
|
|
|
/*
|
|
bitGrid stores the state of all bits.
|
|
The grid is capable of showing the following colors:
|
|
White = not used (left as 0)
|
|
Gray = past the end of the valid bits (because of # of data bytes requested)
|
|
Green = fuzz it
|
|
black = always keep it set to 1
|
|
*/
|
|
void FuzzingWindow::bitfieldClicked(int bitPos)
|
|
{
|
|
if (bitGrid[bitPos] == 3) return; //naughty!
|
|
bitGrid[bitPos]++;
|
|
if (bitGrid[bitPos] > 2) bitGrid[bitPos] = 0;
|
|
|
|
redrawGrid();
|
|
}
|
|
|
|
void FuzzingWindow::redrawGrid()
|
|
{
|
|
//now update the bits in the bitfield control
|
|
uint8_t refBytes[64];
|
|
uint8_t dataBytes[64];
|
|
uint8_t usedBytes[64];
|
|
|
|
for (int j = 0; j < 64; j++)
|
|
{
|
|
refBytes[j] = 0;
|
|
dataBytes[j] = 0;
|
|
usedBytes[j] = 0;
|
|
}
|
|
|
|
numBits = 0;
|
|
|
|
for (int i = 0; i < 512; i++)
|
|
{
|
|
int byt = i / 8;
|
|
int bit = i % 8;
|
|
switch (bitGrid[i])
|
|
{
|
|
case 0: //white, keep this bit off always
|
|
break;
|
|
case 1: //Green, fuzz this bit
|
|
dataBytes[byt] |= (1 << bit);
|
|
numBits++;
|
|
break;
|
|
case 2: //black, bit always set
|
|
dataBytes[byt] |= (1 << bit);
|
|
refBytes[byt] |= (1 << bit);
|
|
break;
|
|
case 3: //gray, this bit doesn't exist
|
|
usedBytes[byt] |= (1 << bit);
|
|
break;
|
|
}
|
|
}
|
|
|
|
ui->bitfield->setUsed(usedBytes, false);
|
|
ui->bitfield->setReference(refBytes, false);
|
|
ui->bitfield->updateData(dataBytes, true);
|
|
}
|