summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorToni Uhlig <matzeton@googlemail.com>2023-07-30 20:55:52 +0200
committerToni Uhlig <matzeton@googlemail.com>2023-07-30 20:55:52 +0200
commit2da24be2e8f8ffa60e371557d75183b67fd2ea24 (patch)
treeec436bf94ad75fed1e0779d35a2bd7a64833babd
parent98c665b409595238d73f550f95c7bf83ec6142da (diff)
Added UI to insert/delete bytes into a packet.
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
-rw-r--r--CMakeLists.txt10
-rw-r--r--bytewindow.cpp50
-rw-r--r--bytewindow.h31
-rw-r--r--bytewindow.ui125
-rw-r--r--mainwindow.cpp138
-rw-r--r--mainwindow.h15
-rw-r--r--mainwindow.ui6
-rw-r--r--qhexedit2/src/qhexedit.h3
8 files changed, 361 insertions, 17 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2df0801..88f7c74 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -9,7 +9,12 @@ set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
+set(CPACK_PACKAGE_CONTACT "toni@impl.cc")
+set(CPACK_DEBIAN_PACKAGE_NAME "pcap-editor")
+set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
+
include(ExternalProject)
+include(CPack)
find_package(QT NAMES Qt6 REQUIRED COMPONENTS Core Widgets)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core Widgets)
@@ -42,10 +47,15 @@ endif()
set(PROJECT_SOURCES
main.cpp
+
mainwindow.cpp
mainwindow.h
mainwindow.ui
+ bytewindow.cpp
+ bytewindow.h
+ bytewindow.ui
+
pcapplusplus.cpp
pcapplusplus.h
diff --git a/bytewindow.cpp b/bytewindow.cpp
new file mode 100644
index 0000000..444399b
--- /dev/null
+++ b/bytewindow.cpp
@@ -0,0 +1,50 @@
+#include "bytewindow.h"
+#include "ui_bytewindow.h"
+
+ByteWindow::ByteWindow(QWidget *parent) :
+ QDialog(parent),
+ ui(new Ui::ByteWindow)
+{
+ ui->setupUi(this);
+}
+
+ByteWindow::~ByteWindow()
+{
+ delete ui;
+}
+
+void ByteWindow::set(ByteWindowOption option, int offset, int size)
+{
+ switch (option) {
+ case ByteWindowOption::BWO_UNKNOWN:
+ case ByteWindowOption::BWO_INSERT:
+ ui->radioButtonInsert->click();
+ break;
+ case ByteWindowOption::BWO_DELETE:
+ ui->radioButtonDelete->click();
+ break;
+ }
+
+ ui->spinBoxIndex->setValue(offset);
+ ui->spinBoxSize->setValue(size);
+}
+
+ByteWindowOption ByteWindow::getOption()
+{
+ if (ui->radioButtonInsert->isChecked())
+ return ByteWindowOption::BWO_INSERT;
+ if (ui->radioButtonDelete->isChecked())
+ return ByteWindowOption::BWO_DELETE;
+
+ return ByteWindowOption::BWO_UNKNOWN;
+}
+
+int ByteWindow::getOffset()
+{
+ return ui->spinBoxIndex->value();
+}
+
+int ByteWindow::getSize()
+{
+ return ui->spinBoxSize->value();
+}
diff --git a/bytewindow.h b/bytewindow.h
new file mode 100644
index 0000000..e087a1e
--- /dev/null
+++ b/bytewindow.h
@@ -0,0 +1,31 @@
+#ifndef BYTEWINDOW_H
+#define BYTEWINDOW_H
+
+#include <QDialog>
+
+QT_BEGIN_NAMESPACE
+namespace Ui { class ByteWindow; }
+QT_END_NAMESPACE
+
+enum class ByteWindowOption {
+ BWO_UNKNOWN,
+ BWO_INSERT,
+ BWO_DELETE
+};
+
+class ByteWindow : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit ByteWindow(QWidget *parent = nullptr);
+ ~ByteWindow();
+ void set(ByteWindowOption option, int offset, int size);
+ ByteWindowOption getOption();
+ int getOffset();
+ int getSize();
+
+private:
+ Ui::ByteWindow *ui;
+};
+#endif // BYTEWINDOW_H
diff --git a/bytewindow.ui b/bytewindow.ui
new file mode 100644
index 0000000..011207a
--- /dev/null
+++ b/bytewindow.ui
@@ -0,0 +1,125 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ByteWindow</class>
+ <widget class="QDialog" name="ByteWindow">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>300</width>
+ <height>200</height>
+ </rect>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>300</width>
+ <height>200</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>16777215</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="windowTitle">
+ <string>Bytes Modification</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="2" column="1">
+ <widget class="QSpinBox" name="spinBoxSize">
+ <property name="minimum">
+ <number>1</number>
+ </property>
+ <property name="maximum">
+ <number>65535</number>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="labelSize">
+ <property name="text">
+ <string>Size:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QDialogButtonBox" name="buttonOk">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QSpinBox" name="spinBoxIndex">
+ <property name="maximum">
+ <number>65535</number>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="labelIndex">
+ <property name="text">
+ <string>Index:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QRadioButton" name="radioButtonInsert">
+ <property name="text">
+ <string>Insert</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QRadioButton" name="radioButtonDelete">
+ <property name="text">
+ <string>Delete</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonOk</sender>
+ <signal>accepted()</signal>
+ <receiver>ByteWindow</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>248</x>
+ <y>254</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>157</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonOk</sender>
+ <signal>rejected()</signal>
+ <receiver>ByteWindow</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>316</x>
+ <y>260</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>286</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/mainwindow.cpp b/mainwindow.cpp
index a375d91..0060fe4 100644
--- a/mainwindow.cpp
+++ b/mainwindow.cpp
@@ -4,6 +4,8 @@
#include <exception>
#include <qfiledialog.h>
+#include <iostream>
+
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, myHexEdit()
@@ -11,7 +13,93 @@ MainWindow::MainWindow(QWidget *parent)
, ppp(nullptr)
{
ui->setupUi(this);
- ui->gridLayout->addWidget(&myHexEdit);
+ ui->gridLayout->addWidget(&myHexEdit.editor);
+
+ const auto& enableButtons = [](MainWindow *mainwindow, bool enable, bool ui_edit_only = false) {
+ if (!ui_edit_only)
+ mainwindow->ui->actionSave->setEnabled(enable);
+ mainwindow->myHexEdit.prependBytes.setEnabled(enable);
+ mainwindow->myHexEdit.appendBytes.setEnabled(enable);
+ mainwindow->myHexEdit.deleteBytes.setEnabled(enable);
+ mainwindow->myHexEdit.deleteSelection.setEnabled(enable);
+ };
+ enableButtons(this, false);
+
+ myHexEdit.editor.setContextMenuPolicy(Qt::CustomContextMenu);
+ myHexEdit.prependBytes.setText("Prepend byte(s)..");
+ myHexEdit.appendBytes.setText("Append byte(s)..");
+ myHexEdit.deleteBytes.setText("Delete byte(s)..");
+ myHexEdit.deleteSelection.setText("Delete Selection");
+ myHexEdit.contextMenu.addAction(&myHexEdit.prependBytes);
+ myHexEdit.contextMenu.addAction(&myHexEdit.appendBytes);
+ myHexEdit.contextMenu.addAction(&myHexEdit.deleteBytes);
+ myHexEdit.contextMenu.addAction(&myHexEdit.deleteSelection);
+
+ connect(&myHexEdit.bytewindow, &QDialog::finished, this, [&](int result) {
+ if (result == 0)
+ return;
+
+ const auto option = myHexEdit.bytewindow.getOption();
+ const auto offset = myHexEdit.bytewindow.getOffset();
+ const auto size = myHexEdit.bytewindow.getSize();
+
+ const auto &selected = ui->tableWidget->selectedItems();
+ if (selected.empty())
+ return;
+ auto &rawPacket = ppp->getRawPacket(selected.last()->row());
+
+ switch (option) {
+ case ByteWindowOption::BWO_UNKNOWN:
+ case ByteWindowOption::BWO_INSERT: {
+ uint8_t *new_bytes = new uint8_t[size];
+ if (!new_bytes)
+ break;
+ memset(new_bytes, 0, size);
+ rawPacket.insertData(offset, new_bytes, size);
+ myHexEdit.editor.setData(QByteArray::fromRawData(reinterpret_cast<const char *>(rawPacket.getRawData()), rawPacket.getRawDataLen()));
+ delete[] new_bytes;
+ break;
+ }
+ case ByteWindowOption::BWO_DELETE:
+ rawPacket.removeData(offset, size);
+ myHexEdit.editor.setData(QByteArray::fromRawData(reinterpret_cast<const char *>(rawPacket.getRawData()), rawPacket.getRawDataLen()));
+ break;
+ }
+ });
+
+ connect(&myHexEdit.editor, &QAbstractScrollArea::customContextMenuRequested, this, [&](const QPoint& pos){
+ myHexEdit.deleteSelection.setEnabled(myHexEdit.editor.selectedData().size() != 0);
+
+ auto globalPos = myHexEdit.editor.mapToGlobal(pos);
+ auto selectedItem = myHexEdit.contextMenu.exec(globalPos);
+ const auto cursorPos = myHexEdit.editor.cursorPosition() / 2;
+ const auto& selectedLength = myHexEdit.editor.getSelectionEnd() - myHexEdit.editor.getSelectionBegin();
+ const auto& showByteWindow = [&](const ByteWindowOption& opt, int offset) {
+ myHexEdit.bytewindow.set(opt, offset, selectedLength > 0 ? selectedLength : 1);
+ myHexEdit.bytewindow.show();
+ };
+
+ if (selectedItem == &myHexEdit.prependBytes) {
+ showByteWindow(ByteWindowOption::BWO_INSERT, cursorPos);
+ } else if (selectedItem == &myHexEdit.appendBytes) {
+ showByteWindow(ByteWindowOption::BWO_INSERT, cursorPos + 1);
+ } else if (selectedItem == &myHexEdit.deleteBytes) {
+ showByteWindow(ByteWindowOption::BWO_DELETE, cursorPos);
+ } else if (selectedItem == &myHexEdit.deleteSelection) {
+ const auto &selected = ui->tableWidget->selectedItems();
+ if (selected.empty())
+ return;
+
+ auto &rawPacket = ppp->getRawPacket(selected.last()->row());
+ if (selectedLength == rawPacket.getRawDataLen())
+ return;
+
+ rawPacket.removeData(myHexEdit.editor.getSelectionBegin(), selectedLength);
+ myHexEdit.editor.setData(QByteArray::fromRawData(reinterpret_cast<const char *>(rawPacket.getRawData()), rawPacket.getRawDataLen()));
+ } else if (selectedItem) {
+ throw std::runtime_error("Unknown context menu " + selectedItem->text().toStdString());
+ }
+ });
connect(ui->actionOpen, &QAction::triggered, this, [&](bool){
QString fileName = QFileDialog::getOpenFileName(this, tr("Open PCAP File"), "", tr("PCAP Files (*.pcap)"));
@@ -20,6 +108,9 @@ MainWindow::MainWindow(QWidget *parent)
delete ppp;
ppp = nullptr;
}
+ ui->lineEdit->clear();
+ ui->tableWidget->clear();
+ ui->tableWidget->setRowCount(0);
ppp = new PcapPlusPlus(fileName.toStdString());
if (ppp) emit processPcap();
}
@@ -44,14 +135,16 @@ MainWindow::MainWindow(QWidget *parent)
});
connect(ui->actionSave, &QAction::triggered, this, [&](bool){
- QString fileName = QFileDialog::getSaveFileName(this, tr("Save PCAP File"), "", tr("PCAP Files (*.pcap)"));
- if (fileName.length() > 0) {
- pcpp::PcapFileWriterDevice pcapWriter(fileName.toStdString(), pcpp::LINKTYPE_ETHERNET);
- if (!pcapWriter.open())
- throw std::runtime_error("Could not open file " + fileName.toStdString() + " for writing.");
- {
- for (auto rawPacket = ppp->rawPacketsBegin(); rawPacket < ppp->rawPacketsEnd(); rawPacket++) {
- pcapWriter.writePacket(*rawPacket);
+ if (ppp) {
+ QString fileName = QFileDialog::getSaveFileName(this, tr("Save PCAP File"), "", tr("PCAP Files (*.pcap)"));
+ if (fileName.length() > 0) {
+ pcpp::PcapFileWriterDevice pcapWriter(fileName.toStdString(), pcpp::LINKTYPE_ETHERNET);
+ if (!pcapWriter.open())
+ throw std::runtime_error("Could not open file " + fileName.toStdString() + " for writing.");
+ {
+ for (auto rawPacket = ppp->rawPacketsBegin(); rawPacket < ppp->rawPacketsEnd(); rawPacket++) {
+ pcapWriter.writePacket(*rawPacket);
+ }
}
}
}
@@ -128,15 +221,34 @@ MainWindow::MainWindow(QWidget *parent)
connect(ui->tableWidget, &QTableWidget::cellPressed, this, [&] {
const auto &selected = ui->tableWidget->selectedItems();
+ if (selected.empty())
+ return;
+
const auto &rawPacket = ppp->getRawPacket(selected.last()->row());
- myHexEdit.setData(QByteArray::fromRawData(reinterpret_cast<const char *>(rawPacket.getRawData()), rawPacket.getRawDataLen()));
+ myHexEdit.editor.setData(QByteArray::fromRawData(reinterpret_cast<const char *>(rawPacket.getRawData()), rawPacket.getRawDataLen()));
+ });
+
+ connect(ui->tableWidget, &QTableWidget::itemSelectionChanged, this, [&]() {
+ enableButtons(this, ui->tableWidget->selectedItems().size() > 0, true);
+ if (ui->tableWidget->selectedItems().size() == 0 && myHexEdit.editor.data().size() > 0)
+ myHexEdit.editor.setData(QByteArray());
});
- connect(&myHexEdit, &QHexEdit::dataChanged, this, [&] {
+ connect(&myHexEdit.editor, &QHexEdit::dataChanged, this, [&] {
const auto &selected = ui->tableWidget->selectedItems();
+ if (selected.empty())
+ return;
+
auto &rawPacket = ppp->getRawPacket(selected.last()->row());
- const auto cursorPos = myHexEdit.cursorPosition() / 2;
- const auto cursorData = myHexEdit.dataAt(cursorPos, 1);
+ const auto& cursorPos = myHexEdit.editor.cursorPosition() / 2;
+ auto cursorData = myHexEdit.editor.dataAt(cursorPos, 1);
+ if (myHexEdit.editor.cursorPosition() % 2 != 0 && myHexEdit.editor.cursorPosition() / 2 == myHexEdit.editor.data().size() - 1) {
+ const uint8_t new_byte = 0x00;
+ rawPacket.insertData(rawPacket.getRawDataLen(), &new_byte, sizeof(new_byte));
+ myHexEdit.editor.setData(QByteArray::fromRawData(reinterpret_cast<const char *>(rawPacket.getRawData()), rawPacket.getRawDataLen()));
+ myHexEdit.editor.setCursorPosition(cursorPos * 2 + 1);
+ }
+
rawPacket.removeData(cursorPos, 1);
rawPacket.insertData(cursorPos, reinterpret_cast<const uint8_t *>(cursorData.data()), cursorData.size());
});
diff --git a/mainwindow.h b/mainwindow.h
index 242ca65..74a6376 100644
--- a/mainwindow.h
+++ b/mainwindow.h
@@ -1,10 +1,12 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
+#include "bytewindow.h"
#include "pcapplusplus.h"
#include "qhexedit2/src/qhexedit.h"
#include <QMainWindow>
+#include <QMenu>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
@@ -19,10 +21,19 @@ public:
~MainWindow();
private:
- QHexEdit myHexEdit;
+ struct {
+ QMenu contextMenu;
+ QAction prependBytes;
+ QAction appendBytes;
+ QAction deleteBytes;
+ QAction deleteSelection;
+ QHexEdit editor;
+ ByteWindow bytewindow;
+ } myHexEdit;
+
time_t firstPacketTs;
- Ui::MainWindow *ui;
PcapPlusPlus *ppp;
+ Ui::MainWindow *ui;
signals:
void processPcap();
diff --git a/mainwindow.ui b/mainwindow.ui
index a0829c8..340b454 100644
--- a/mainwindow.ui
+++ b/mainwindow.ui
@@ -53,9 +53,15 @@ border-style: outset;
border-width: 2px;
border-color: beige;</string>
</property>
+ <property name="selectionMode">
+ <enum>QAbstractItemView::ExtendedSelection</enum>
+ </property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
+ <property name="cornerButtonEnabled">
+ <bool>true</bool>
+ </property>
<property name="columnCount">
<number>12</number>
</property>
diff --git a/qhexedit2/src/qhexedit.h b/qhexedit2/src/qhexedit.h
index 1a5801a..f06aac5 100644
--- a/qhexedit2/src/qhexedit.h
+++ b/qhexedit2/src/qhexedit.h
@@ -365,7 +365,7 @@ public:
QColor selectionColor();
void setSelectionColor(const QColor &color);
-protected:
+public:
// Handle events
void keyPressEvent(QKeyEvent *event);
void mouseMoveEvent(QMouseEvent * event);
@@ -373,7 +373,6 @@ protected:
void paintEvent(QPaintEvent *event);
void resizeEvent(QResizeEvent *);
virtual bool focusNextPrevChild(bool next);
-private:
// Handle selections
void resetSelection(qint64 pos); // set selectionStart and selectionEnd to pos
void resetSelection(); // set selectionEnd to selectionStart