summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorToni Uhlig <matzeton@googlemail.com>2023-07-09 10:52:21 +0200
committerToni Uhlig <matzeton@googlemail.com>2023-07-10 10:52:21 +0200
commit84a337ae0a4b8f60220ac08d04222bbaff6143d0 (patch)
treea078350200483786002cdcd6258a7979809ba87e
parentfebaef00017278ac65cb7e285564ebc9d5dadfe5 (diff)
MVP
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
-rw-r--r--.gitattributes1
-rw-r--r--.gitmodules3
-rw-r--r--CMakeLists.txt60
m---------PcapPlusPlus0
-rw-r--r--mainwindow.cpp114
-rw-r--r--mainwindow.h10
-rw-r--r--mainwindow.ui49
-rw-r--r--pcapplusplus.cpp153
-rw-r--r--pcapplusplus.h33
-rw-r--r--qhexedit2/src/QHexEditPlugin.h6
10 files changed, 412 insertions, 17 deletions
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..2420b0f
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1 @@
+qhexedit2/* linguist-vendored
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..33b156f
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "PcapPlusPlus"]
+ path = PcapPlusPlus
+ url = https://github.com/seladb/PcapPlusPlus.git
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3d462a4..2df0801 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -9,14 +9,52 @@ set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
-find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets)
-find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets)
+include(ExternalProject)
+
+find_package(QT NAMES Qt6 REQUIRED COMPONENTS Core Widgets)
+find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core Widgets)
+find_library(PCAP_LIBRARY NAMES pcap wpcap)
+
+set_property(SOURCE qhexedit2_init.cpp PROPERTY SKIP_AUTOGEN ON)
+
+qt_add_plugin(qhexedit2
+ STATIC
+ qhexedit2/src/chunks.cpp
+ qhexedit2/src/chunks.h
+ qhexedit2/src/commands.cpp
+ qhexedit2/src/commands.h
+ qhexedit2/src/qhexedit.cpp
+ qhexedit2/src/qhexedit.h
+ qhexedit2/src/QHexEditPlugin.cpp
+ qhexedit2/src/QHexEditPlugin.h
+)
+
+ExternalProject_Add(PcapPlusPlus SOURCE_DIR ${CMAKE_SOURCE_DIR}/PcapPlusPlus
+ CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR}/ppp-install
+ -DBUILD_SHARED_LIBS=OFF -DPCAPPP_BUILD_COVERAGE=OFF
+ -DPCAPPP_BUILD_EXAMPLES=OFF -DPCAPPP_BUILD_TESTS=OFF)
+
+option(ENABLE_SANITIZER "Enable ASAN/LSAN/UBSAN." OFF)
+if(ENABLE_SANITIZER)
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fsanitize=undefined -fno-sanitize=alignment -fsanitize=enum -fsanitize=leak")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fsanitize=undefined -fno-sanitize=alignment -fsanitize=enum -fsanitize=leak")
+endif()
set(PROJECT_SOURCES
main.cpp
mainwindow.cpp
mainwindow.h
mainwindow.ui
+
+ pcapplusplus.cpp
+ pcapplusplus.h
+
+ qhexedit2/src/chunks.cpp
+ qhexedit2/src/chunks.h
+ qhexedit2/src/commands.cpp
+ qhexedit2/src/commands.h
+ qhexedit2/src/qhexedit.cpp
+ qhexedit2/src/qhexedit.h
)
if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
@@ -24,17 +62,12 @@ if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
MANUAL_FINALIZATION
${PROJECT_SOURCES}
)
-# Define target properties for Android with Qt 6 as:
-# set_property(TARGET pcap-editor APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR
-# ${CMAKE_CURRENT_SOURCE_DIR}/android)
-# For more information, see https://doc.qt.io/qt-6/qt-add-executable.html#target-creation
+ qt_import_plugins(pcap-editor INCLUDE qhexedit2)
else()
if(ANDROID)
add_library(pcap-editor SHARED
${PROJECT_SOURCES}
)
-# Define properties for Android with Qt 5 after find_package() calls as:
-# set(ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android")
else()
add_executable(pcap-editor
${PROJECT_SOURCES}
@@ -42,7 +75,14 @@ else()
endif()
endif()
-target_link_libraries(pcap-editor PRIVATE Qt${QT_VERSION_MAJOR}::Widgets)
+add_dependencies(pcap-editor PcapPlusPlus)
+
+target_include_directories(pcap-editor PRIVATE ${CMAKE_BINARY_DIR}/ppp-install/include/pcapplusplus)
+target_link_libraries(pcap-editor PRIVATE Qt${QT_VERSION_MAJOR}::Widgets
+ ${CMAKE_BINARY_DIR}/ppp-install/lib/libPcap++.a
+ ${CMAKE_BINARY_DIR}/ppp-install/lib/libPacket++.a
+ ${CMAKE_BINARY_DIR}/ppp-install/lib/libCommon++.a
+ ${PCAP_LIBRARY})
set_target_properties(pcap-editor PROPERTIES
MACOSX_BUNDLE_GUI_IDENTIFIER my.example.com
@@ -59,3 +99,5 @@ install(TARGETS pcap-editor
if(QT_VERSION_MAJOR EQUAL 6)
qt_finalize_executable(pcap-editor)
endif()
+
+message(STATUS "QT_DIR: ${QT_DIR}")
diff --git a/PcapPlusPlus b/PcapPlusPlus
new file mode 160000
+Subproject e96b741df40aa5e397916ab3c107ec2a57d37b8
diff --git a/mainwindow.cpp b/mainwindow.cpp
index a06fc8e..2ff457a 100644
--- a/mainwindow.cpp
+++ b/mainwindow.cpp
@@ -1,15 +1,125 @@
#include "mainwindow.h"
-#include "./ui_mainwindow.h"
+#include "ui_mainwindow.h"
+
+#include <exception>
+#include <qfiledialog.h>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
+ , myHexEdit()
, ui(new Ui::MainWindow)
+ , ppp(nullptr)
{
ui->setupUi(this);
+ ui->gridLayout->addWidget(&myHexEdit);
+
+ connect(ui->actionOpen, &QAction::triggered, this, [&](bool){
+ QString fileName = QFileDialog::getOpenFileName(this, tr("Open PCAP File"), "", tr("PCAP Files (*.pcap)"));
+ if (fileName.length() > 0) {
+ if (ppp) {
+ delete ppp;
+ ppp = nullptr;
+ }
+ ppp = new PcapPlusPlus(fileName.toStdString());
+ if (ppp) emit processPcap();
+ }
+ });
+
+ 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);
+ }
+ }
+ }
+ });
+
+ connect(this, &MainWindow::processPcap, this, [&](){
+ pcpp::Packet packet;
+ if (!ppp) throw std::runtime_error("PcapPlusPlus was not initialized.");
+ firstPacketTs = 0;
+ while (ppp->processPacket(packet)) {
+ if (!firstPacketTs)
+ firstPacketTs = packet.getRawPacket()->getPacketTimeStamp().tv_sec;
+ emit onPacketAvailable(packet);
+ }
+ });
+
+ connect(this, &MainWindow::onPacketAvailable, this, [&](const pcpp::Packet& packet) {
+ ui->tableWidget->insertRow(ui->tableWidget->rowCount());
+
+ QTableWidgetItem* itemRelativeTime =new QTableWidgetItem(tr("%1").arg(packet.getRawPacket()->getPacketTimeStamp().tv_sec - firstPacketTs));
+ ui->tableWidget->setItem(ui->tableWidget->rowCount() - 1, 0, itemRelativeTime);
+
+ QTableWidgetItem* itemFrameLength = new QTableWidgetItem(tr("%1").arg(packet.getRawPacket()->getFrameLength()));
+ ui->tableWidget->setItem(ui->tableWidget->rowCount() - 1, 1, itemFrameLength);
+
+ const auto *firstLayer = PcapPlusPlus::getFirstLayer(packet);
+
+ QTableWidgetItem* itemFirstLayerProtocol = new QTableWidgetItem(tr("%1").arg(firstLayer ? PcapPlusPlus::getProtocolTypeAsString(firstLayer->getProtocol()) : ""));
+ ui->tableWidget->setItem(ui->tableWidget->rowCount() - 1, 2, itemFirstLayerProtocol);
+
+ const auto *secondLayer = firstLayer ? firstLayer->getNextLayer() : nullptr;
+
+ QTableWidgetItem* itemSecondLayerProtocol = new QTableWidgetItem(tr("%1").arg(secondLayer ? PcapPlusPlus::getProtocolTypeAsString(secondLayer->getProtocol()) : ""));
+ ui->tableWidget->setItem(ui->tableWidget->rowCount() - 1, 3, itemSecondLayerProtocol);
+
+ const auto *thirdLayer = secondLayer ? secondLayer->getNextLayer() : nullptr;
+
+ QTableWidgetItem* itemThirdLayerProtocol = new QTableWidgetItem(tr("%1").arg(thirdLayer ? PcapPlusPlus::getProtocolTypeAsString(thirdLayer->getProtocol()) : ""));
+ ui->tableWidget->setItem(ui->tableWidget->rowCount() - 1, 4, itemThirdLayerProtocol);
+
+ const auto ethTuple = PcapPlusPlus::getEthTuple(packet);
+
+ QTableWidgetItem* itemSrcMac = new QTableWidgetItem(tr("%1").arg(std::get<0>(ethTuple)));
+ ui->tableWidget->setItem(ui->tableWidget->rowCount() - 1, 5, itemSrcMac);
+
+ QTableWidgetItem* itemDstMac = new QTableWidgetItem(tr("%1").arg(std::get<1>(ethTuple)));
+ ui->tableWidget->setItem(ui->tableWidget->rowCount() - 1, 6, itemDstMac);
+
+ const auto ipTuple = PcapPlusPlus::getIpTuple(packet);
+
+ QTableWidgetItem* itemSrcIp = new QTableWidgetItem(tr("%1").arg(std::get<0>(ipTuple)));
+ ui->tableWidget->setItem(ui->tableWidget->rowCount() - 1, 7, itemSrcIp);
+
+ QTableWidgetItem* itemDstIp = new QTableWidgetItem(tr("%1").arg(std::get<1>(ipTuple)));
+ ui->tableWidget->setItem(ui->tableWidget->rowCount() - 1, 8, itemDstIp);
+
+ const auto l4Tuple = PcapPlusPlus::getLayer4Tuple(packet);
+
+ QTableWidgetItem* itemSrcPort = new QTableWidgetItem(tr("%1").arg(std::get<0>(l4Tuple)));
+ ui->tableWidget->setItem(ui->tableWidget->rowCount() - 1, 9, itemSrcPort);
+
+ QTableWidgetItem* itemDstPort = new QTableWidgetItem(tr("%1").arg(std::get<1>(l4Tuple)));
+ ui->tableWidget->setItem(ui->tableWidget->rowCount() - 1, 10, itemDstPort);
+
+ QTableWidgetItem* itemDesc = new QTableWidgetItem(tr("%1").arg(QString::fromStdString(thirdLayer ? thirdLayer->toString() : (secondLayer ? secondLayer->toString() : (firstLayer ? firstLayer->toString() : "")))));
+ ui->tableWidget->setItem(ui->tableWidget->rowCount() - 1, 11, itemDesc);
+ });
+
+ connect(ui->tableWidget, &QTableWidget::cellPressed, this, [&] {
+ const auto &selected = ui->tableWidget->selectedItems();
+ const auto &rawPacket = ppp->getRawPacket(selected.last()->row());
+ myHexEdit.setData(QByteArray::fromRawData(reinterpret_cast<const char *>(rawPacket.getRawData()), rawPacket.getRawDataLen()));
+ });
+
+ connect(&myHexEdit, &QHexEdit::dataChanged, this, [&] {
+ const auto &selected = ui->tableWidget->selectedItems();
+ auto &rawPacket = ppp->getRawPacket(selected.last()->row());
+ const auto cursorPos = myHexEdit.cursorPosition() / 2;
+ const auto cursorData = myHexEdit.dataAt(cursorPos, 1);
+ rawPacket.removeData(cursorPos, 1);
+ rawPacket.insertData(cursorPos, reinterpret_cast<const uint8_t *>(cursorData.data()), cursorData.size());
+ });
}
MainWindow::~MainWindow()
{
delete ui;
+ delete ppp;
}
-
diff --git a/mainwindow.h b/mainwindow.h
index 4643e32..242ca65 100644
--- a/mainwindow.h
+++ b/mainwindow.h
@@ -1,6 +1,9 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
+#include "pcapplusplus.h"
+#include "qhexedit2/src/qhexedit.h"
+
#include <QMainWindow>
QT_BEGIN_NAMESPACE
@@ -16,6 +19,13 @@ public:
~MainWindow();
private:
+ QHexEdit myHexEdit;
+ time_t firstPacketTs;
Ui::MainWindow *ui;
+ PcapPlusPlus *ppp;
+
+signals:
+ void processPcap();
+ void onPacketAvailable(const pcpp::Packet& packet);
};
#endif // MAINWINDOW_H
diff --git a/mainwindow.ui b/mainwindow.ui
index 7797e9f..ea1b3a3 100644
--- a/mainwindow.ui
+++ b/mainwindow.ui
@@ -42,7 +42,42 @@
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
- <widget class="QListWidget" name="listWidget"/>
+ <widget class="QTableWidget" name="tableWidget">
+ <property name="styleSheet">
+ <string notr="true">color: black;
+background-color: white;
+selection-color: yellow;
+selection-background-color: grey;
+
+border-style: outset;
+border-width: 2px;
+border-color: beige;</string>
+ </property>
+ <property name="selectionBehavior">
+ <enum>QAbstractItemView::SelectRows</enum>
+ </property>
+ <property name="columnCount">
+ <number>12</number>
+ </property>
+ <attribute name="horizontalHeaderCascadingSectionResizes">
+ <bool>false</bool>
+ </attribute>
+ <attribute name="horizontalHeaderStretchLastSection">
+ <bool>true</bool>
+ </attribute>
+ <column/>
+ <column/>
+ <column/>
+ <column/>
+ <column/>
+ <column/>
+ <column/>
+ <column/>
+ <column/>
+ <column/>
+ <column/>
+ <column/>
+ </widget>
</item>
</layout>
</item>
@@ -61,10 +96,22 @@
<property name="title">
<string>File</string>
</property>
+ <addaction name="actionOpen"/>
+ <addaction name="actionSave"/>
</widget>
<addaction name="menuFile"/>
</widget>
<widget class="QStatusBar" name="statusbar"/>
+ <action name="actionOpen">
+ <property name="text">
+ <string>Open</string>
+ </property>
+ </action>
+ <action name="actionSave">
+ <property name="text">
+ <string>Save</string>
+ </property>
+ </action>
</widget>
<resources/>
<connections/>
diff --git a/pcapplusplus.cpp b/pcapplusplus.cpp
new file mode 100644
index 0000000..894d799
--- /dev/null
+++ b/pcapplusplus.cpp
@@ -0,0 +1,153 @@
+#include "pcapplusplus.h"
+
+#include <EthLayer.h>
+#include <exception>
+#include <IPLayer.h>
+#include <TcpLayer.h>
+#include <tuple>
+#include <UdpLayer.h>
+
+PcapPlusPlus::PcapPlusPlus(std::string fileName) : rawPackets(), parsedPackets()
+{
+ reader = pcpp::IFileReaderDevice::getReader(fileName);
+
+ if (!reader) {
+ std::string err("PCAP Reader failed for: " + fileName);
+ throw std::runtime_error(err);
+ }
+
+ if (!reader->open()) {
+ std::string err("PCAP Open failed for: " + fileName);
+ throw std::runtime_error(err);
+ }
+}
+
+PcapPlusPlus::~PcapPlusPlus()
+{
+ if (reader) {
+ reader->close();
+ delete reader;
+ reader = nullptr;
+ }
+}
+
+bool PcapPlusPlus::processPacket(pcpp::Packet & packet)
+{
+ pcpp::RawPacket rawPacket;
+
+ if (!reader)
+ return false;
+
+ if (reader->getNextPacket(rawPacket)) {
+ rawPackets.emplace_back(std::move(rawPacket));
+
+ pcpp::Packet parsedPacket(&rawPackets.back(), false);
+ packet = parsedPacket;
+
+ parsedPackets.emplace_back(std::move(parsedPacket));
+
+ return true;
+ }
+
+ return false;
+}
+
+pcpp::RawPacket &PcapPlusPlus::getRawPacket(size_t index)
+{
+ return rawPackets.at(index);
+}
+
+pcpp::Packet &PcapPlusPlus::getParsedPacket(size_t index)
+{
+ return parsedPackets.at(index);
+}
+
+std::vector<pcpp::RawPacket>::iterator PcapPlusPlus::rawPacketsBegin()
+{
+ return rawPackets.begin();
+}
+
+std::vector<pcpp::RawPacket>::iterator PcapPlusPlus::rawPacketsEnd()
+{
+ return rawPackets.end();
+}
+
+std::vector<pcpp::Packet>::iterator PcapPlusPlus::parsedPacketsBegin()
+{
+ return parsedPackets.begin();
+}
+
+std::vector<pcpp::Packet>::iterator PcapPlusPlus::parsedPacketsEnd()
+{
+ return parsedPackets.end();
+}
+
+const pcpp::Layer *PcapPlusPlus::getFirstLayer(const pcpp::Packet & packet)
+{
+ const auto *curLayer = packet.getFirstLayer();
+
+ if (curLayer == nullptr)
+ {
+ throw std::runtime_error("No Ethernet Layer found.");
+ return nullptr;
+ }
+
+ return curLayer;
+}
+
+QString PcapPlusPlus::getProtocolTypeAsString(pcpp::ProtocolType protocolType)
+{
+ switch (protocolType)
+ {
+ case pcpp::Ethernet:
+ return "Ethernet";
+ case pcpp::ARP:
+ return "ARP";
+ case pcpp::IPv4:
+ return "IPv4";
+ case pcpp::IPv6:
+ return "IPv6";
+ case pcpp::TCP:
+ return "TCP";
+ case pcpp::UDP:
+ return "UDP";
+ case pcpp::HTTPRequest:
+ case pcpp::HTTPResponse:
+ return "HTTP";
+ case pcpp::UnknownProtocol:
+ return "Unknown Protocol";
+ default:
+ return "Unknown";
+ }
+}
+
+std::tuple<QString, QString> PcapPlusPlus::getEthTuple(const pcpp::Packet & packet)
+{
+ const auto *ethernetLayer = packet.getLayerOfType<pcpp::EthLayer>();
+ if (ethernetLayer == NULL)
+ return std::make_tuple("", "");
+
+ return std::make_tuple(QString::fromStdString(ethernetLayer->getSourceMac().toString()), QString::fromStdString(ethernetLayer->getDestMac().toString()));
+}
+
+std::tuple<QString, QString> PcapPlusPlus::getIpTuple(const pcpp::Packet & packet)
+{
+ const auto *ipLayer = packet.getLayerOfType<pcpp::IPLayer>();
+ if (ipLayer == NULL)
+ return std::make_tuple("", "");
+
+ return std::make_tuple(QString::fromStdString(ipLayer->getSrcIPAddress().toString()), QString::fromStdString(ipLayer->getDstIPAddress().toString()));
+}
+
+std::tuple<uint16_t, uint16_t> PcapPlusPlus::getLayer4Tuple(const pcpp::Packet & packet)
+{
+ const auto *udpLayer = packet.getLayerOfType<pcpp::UdpLayer>();
+ if (udpLayer != NULL)
+ return std::make_tuple(udpLayer->getSrcPort(), udpLayer->getDstPort());
+
+ const auto *tcpLayer = packet.getLayerOfType<pcpp::TcpLayer>();
+ if (tcpLayer != NULL)
+ return std::make_tuple(tcpLayer->getSrcPort(), tcpLayer->getDstPort());
+
+ return std::make_tuple(0, 0);
+}
diff --git a/pcapplusplus.h b/pcapplusplus.h
new file mode 100644
index 0000000..4afc7ff
--- /dev/null
+++ b/pcapplusplus.h
@@ -0,0 +1,33 @@
+#ifndef PCAPPLUSPLUS_H
+#define PCAPPLUSPLUS_H
+
+#include <Packet.h>
+#include <PcapFileDevice.h>
+#include <qstring.h>
+
+class PcapPlusPlus
+{
+public:
+ explicit PcapPlusPlus(std::string fileName);
+ ~PcapPlusPlus();
+ bool processPacket(pcpp::Packet & packet);
+ pcpp::RawPacket &getRawPacket(size_t index);
+ pcpp::Packet &getParsedPacket(size_t index);
+ std::vector<pcpp::RawPacket>::iterator rawPacketsBegin();
+ std::vector<pcpp::RawPacket>::iterator rawPacketsEnd();
+ std::vector<pcpp::Packet>::iterator parsedPacketsBegin();
+ std::vector<pcpp::Packet>::iterator parsedPacketsEnd();
+
+ static const pcpp::Layer *getFirstLayer(const pcpp::Packet & packet);
+ static QString getProtocolTypeAsString(pcpp::ProtocolType protocolType);
+ static std::tuple<QString, QString> getEthTuple(const pcpp::Packet & packet);
+ static std::tuple<QString, QString> getIpTuple(const pcpp::Packet & packet);
+ static std::tuple<uint16_t, uint16_t> getLayer4Tuple(const pcpp::Packet & packet);
+
+private:
+ pcpp::IFileReaderDevice *reader;
+ std::vector<pcpp::RawPacket> rawPackets;
+ std::vector<pcpp::Packet> parsedPackets;
+};
+
+#endif // PCAPPLUSPLUS_H
diff --git a/qhexedit2/src/QHexEditPlugin.h b/qhexedit2/src/QHexEditPlugin.h
index a67179c..a1a55bb 100644
--- a/qhexedit2/src/QHexEditPlugin.h
+++ b/qhexedit2/src/QHexEditPlugin.h
@@ -3,11 +3,7 @@
#include <QObject>
-#if QT_VERSION < QT_VERSION_CHECK(5,0,0)
-#include <QtDesigner/QDesignerCustomWidgetInterface>
-#else
-#include <QDesignerCustomWidgetInterface>
-#endif
+#include <QtUiPlugin/QDesignerCustomWidgetInterface>
class QHexEditPlugin : public QObject, public QDesignerCustomWidgetInterface
{