From b11cbe2d194da99f2d615b7dbc301b1b15b7a9df Mon Sep 17 00:00:00 2001 From: geoffrey Date: Fri, 1 May 2026 15:19:10 +0200 Subject: [PATCH] First commit --- .gitignore | 1 + README.md | 2 + RElicAnalysis-0.1.0 | Bin 0 -> 270280 bytes RElicAnalysis/CMakeLists.txt | 130 ++++++ RElicAnalysis/CMakeLists.txt.user | 418 +++++++++++++++++++ RElicAnalysis/centralwidget.cpp | 40 ++ RElicAnalysis/centralwidget.h | 25 ++ RElicAnalysis/data.h | 110 +++++ RElicAnalysis/dataDisas.h | 22 + RElicAnalysis/dialogabout.cpp | 81 ++++ RElicAnalysis/dialogabout.h | 19 + RElicAnalysis/disasasm.cpp | 156 +++++++ RElicAnalysis/disasasm.h | 28 ++ RElicAnalysis/dockmenu.cpp | 56 +++ RElicAnalysis/dockmenu.h | 15 + RElicAnalysis/elf.cpp | 233 +++++++++++ RElicAnalysis/elf/dialogelfheader.cpp | 84 ++++ RElicAnalysis/elf/dialogelfheader.h | 20 + RElicAnalysis/elf/dialogelfident.cpp | 62 +++ RElicAnalysis/elf/dialogelfident.h | 20 + RElicAnalysis/elf/dialogelfprogram.cpp | 82 ++++ RElicAnalysis/elf/dialogelfprogram.h | 32 ++ RElicAnalysis/elf/dialogelfsection.cpp | 85 ++++ RElicAnalysis/elf/dialogelfsection.h | 33 ++ RElicAnalysis/elf/dialogelfsymbol.cpp | 30 ++ RElicAnalysis/elf/dialogelfsymbol.h | 21 + RElicAnalysis/elf/mdielf.cpp | 506 +++++++++++++++++++++++ RElicAnalysis/elf/mdielf.h | 65 +++ RElicAnalysis/elf/mdielfdisassembled.cpp | 108 +++++ RElicAnalysis/elf/mdielfdisassembled.h | 36 ++ RElicAnalysis/elf/mdielfhandler.cpp | 31 ++ RElicAnalysis/elf/mdielfhandler.h | 25 ++ RElicAnalysis/elf/mdielfview.cpp | 107 +++++ RElicAnalysis/elf/mdielfview.h | 48 +++ RElicAnalysis/elfReading_en_GB.ts | 3 + RElicAnalysis/elfdata.cpp | 351 ++++++++++++++++ RElicAnalysis/elfdata.h | 71 ++++ RElicAnalysis/fileinfo.cpp | 83 ++++ RElicAnalysis/fileinfo.h | 28 ++ RElicAnalysis/functions.cpp | 48 +++ RElicAnalysis/functions.h | 12 + RElicAnalysis/handleproject.cpp | 87 ++++ RElicAnalysis/handleproject.h | 41 ++ RElicAnalysis/main.cpp | 23 ++ RElicAnalysis/mainwindow.cpp | 27 ++ RElicAnalysis/mainwindow.h | 28 ++ RElicAnalysis/mainwindow.ui | 31 ++ RElicAnalysis/menubar.cpp | 81 ++++ RElicAnalysis/menubar.h | 31 ++ RElicAnalysis/modelwidgetelf.cpp | 49 +++ RElicAnalysis/modelwidgetelf.h | 28 ++ RElicAnalysis/parsingelf.cpp | 442 ++++++++++++++++++++ RElicAnalysis/parsingelf.h | 52 +++ RElicAnalysis/project.cpp | 21 + RElicAnalysis/project.h | 29 ++ RElicAnalysis/projectelf.cpp | 10 + RElicAnalysis/projectelf.h | 20 + RElicAnalysis/stylesheet.h | 11 + 58 files changed, 4338 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100755 RElicAnalysis-0.1.0 create mode 100644 RElicAnalysis/CMakeLists.txt create mode 100644 RElicAnalysis/CMakeLists.txt.user create mode 100644 RElicAnalysis/centralwidget.cpp create mode 100644 RElicAnalysis/centralwidget.h create mode 100644 RElicAnalysis/data.h create mode 100644 RElicAnalysis/dataDisas.h create mode 100644 RElicAnalysis/dialogabout.cpp create mode 100644 RElicAnalysis/dialogabout.h create mode 100644 RElicAnalysis/disasasm.cpp create mode 100644 RElicAnalysis/disasasm.h create mode 100644 RElicAnalysis/dockmenu.cpp create mode 100644 RElicAnalysis/dockmenu.h create mode 100644 RElicAnalysis/elf.cpp create mode 100644 RElicAnalysis/elf/dialogelfheader.cpp create mode 100644 RElicAnalysis/elf/dialogelfheader.h create mode 100644 RElicAnalysis/elf/dialogelfident.cpp create mode 100644 RElicAnalysis/elf/dialogelfident.h create mode 100644 RElicAnalysis/elf/dialogelfprogram.cpp create mode 100644 RElicAnalysis/elf/dialogelfprogram.h create mode 100644 RElicAnalysis/elf/dialogelfsection.cpp create mode 100644 RElicAnalysis/elf/dialogelfsection.h create mode 100644 RElicAnalysis/elf/dialogelfsymbol.cpp create mode 100644 RElicAnalysis/elf/dialogelfsymbol.h create mode 100644 RElicAnalysis/elf/mdielf.cpp create mode 100644 RElicAnalysis/elf/mdielf.h create mode 100644 RElicAnalysis/elf/mdielfdisassembled.cpp create mode 100644 RElicAnalysis/elf/mdielfdisassembled.h create mode 100644 RElicAnalysis/elf/mdielfhandler.cpp create mode 100644 RElicAnalysis/elf/mdielfhandler.h create mode 100644 RElicAnalysis/elf/mdielfview.cpp create mode 100644 RElicAnalysis/elf/mdielfview.h create mode 100644 RElicAnalysis/elfReading_en_GB.ts create mode 100644 RElicAnalysis/elfdata.cpp create mode 100644 RElicAnalysis/elfdata.h create mode 100644 RElicAnalysis/fileinfo.cpp create mode 100644 RElicAnalysis/fileinfo.h create mode 100644 RElicAnalysis/functions.cpp create mode 100644 RElicAnalysis/functions.h create mode 100644 RElicAnalysis/handleproject.cpp create mode 100644 RElicAnalysis/handleproject.h create mode 100644 RElicAnalysis/main.cpp create mode 100644 RElicAnalysis/mainwindow.cpp create mode 100644 RElicAnalysis/mainwindow.h create mode 100644 RElicAnalysis/mainwindow.ui create mode 100644 RElicAnalysis/menubar.cpp create mode 100644 RElicAnalysis/menubar.h create mode 100644 RElicAnalysis/modelwidgetelf.cpp create mode 100644 RElicAnalysis/modelwidgetelf.h create mode 100644 RElicAnalysis/parsingelf.cpp create mode 100644 RElicAnalysis/parsingelf.h create mode 100644 RElicAnalysis/project.cpp create mode 100644 RElicAnalysis/project.h create mode 100644 RElicAnalysis/projectelf.cpp create mode 100644 RElicAnalysis/projectelf.h create mode 100644 RElicAnalysis/stylesheet.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dd07bff --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +build-* diff --git a/README.md b/README.md new file mode 100644 index 0000000..70e8347 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# RElicAnalysis +RElicAnalysis, for Reverse Engineering, relicAnalysis, it's an opensource application based on [Qt](https://www.qt.io/) for analysis binary such ELF file. diff --git a/RElicAnalysis-0.1.0 b/RElicAnalysis-0.1.0 new file mode 100755 index 0000000000000000000000000000000000000000..dd2f3b5176ef7cf1e32db62a5b56a84ce5d07dd4 GIT binary patch literal 270280 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 + + + + + EnvironmentId + {7576b28b-0509-4f2e-9112-d416507c0bdd} + + + ProjectExplorer.Project.ActiveTarget + 0 + + + ProjectExplorer.Project.EditorSettings + + true + false + true + + Cpp + + CppGlobal + + + + QmlJS + + QmlJSGlobal + + + 2 + UTF-8 + false + 4 + false + 80 + true + true + 1 + 0 + false + true + false + 0 + true + true + 0 + 8 + true + false + 1 + true + true + true + *.md, *.MD, Makefile + false + true + true + + + + ProjectExplorer.Project.PluginSettings + + + true + false + true + true + true + true + + + 0 + true + + true + true + Builtin.DefaultTidyAndClazy + 2 + true + + + + true + + + + + ProjectExplorer.Project.Target.0 + + Desktop + Desktop + Desktop + {4f4e56a4-b54b-4c8a-9576-1e917c71a3c7} + 0 + 0 + 0 + + Debug + 2 + false + + -DCMAKE_GENERATOR:STRING=Unix Makefiles +-DCMAKE_BUILD_TYPE:STRING=Debug +-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake +-DQT_QMAKE_EXECUTABLE:STRING=%{Qt:qmakeExecutable} +-DCMAKE_PREFIX_PATH:STRING=%{Qt:QT_INSTALL_PREFIX} +-DCMAKE_C_COMPILER:STRING=%{Compiler:Executable:C} +-DCMAKE_CXX_COMPILER:STRING=%{Compiler:Executable:Cxx} +-DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG} + 0 + /home/geoffrey/Documents/GIT/cryptoshield/RElicAnalysis/build-RElicAnalysis-Desktop-Debug + + + + + all + + false + + true + Build + CMakeProjectManager.MakeStep + + 1 + Build + Build + ProjectExplorer.BuildSteps.Build + + + + + + clean + + false + + true + Build + CMakeProjectManager.MakeStep + + 1 + Clean + Clean + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + Debug + CMakeProjectManager.CMakeBuildConfiguration + + + Release + 2 + false + + -DCMAKE_GENERATOR:STRING=Unix Makefiles +-DCMAKE_BUILD_TYPE:STRING=Release +-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake +-DQT_QMAKE_EXECUTABLE:STRING=%{Qt:qmakeExecutable} +-DCMAKE_PREFIX_PATH:STRING=%{Qt:QT_INSTALL_PREFIX} +-DCMAKE_C_COMPILER:STRING=%{Compiler:Executable:C} +-DCMAKE_CXX_COMPILER:STRING=%{Compiler:Executable:Cxx} +-DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG} + /home/geoffrey/Documents/GIT/cryptoshield/RElicAnalysis/build-RElicAnalysis-Desktop-Release + + + + + all + + false + + true + CMakeProjectManager.MakeStep + + 1 + Build + Build + ProjectExplorer.BuildSteps.Build + + + + + + clean + + false + + true + CMakeProjectManager.MakeStep + + 1 + Clean + Clean + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + Release + CMakeProjectManager.CMakeBuildConfiguration + + + RelWithDebInfo + 2 + false + + -DCMAKE_GENERATOR:STRING=Unix Makefiles +-DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo +-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake +-DQT_QMAKE_EXECUTABLE:STRING=%{Qt:qmakeExecutable} +-DCMAKE_PREFIX_PATH:STRING=%{Qt:QT_INSTALL_PREFIX} +-DCMAKE_C_COMPILER:STRING=%{Compiler:Executable:C} +-DCMAKE_CXX_COMPILER:STRING=%{Compiler:Executable:Cxx} +-DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG} + /home/geoffrey/Documents/GIT/cryptoshield/RElicAnalysis/build-RElicAnalysis-Desktop-RelWithDebInfo + + + + + all + + false + + true + CMakeProjectManager.MakeStep + + 1 + Build + Build + ProjectExplorer.BuildSteps.Build + + + + + + clean + + false + + true + CMakeProjectManager.MakeStep + + 1 + Clean + Clean + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + Release with Debug Information + CMakeProjectManager.CMakeBuildConfiguration + + + RelWithDebInfo + 2 + false + + -DCMAKE_GENERATOR:STRING=Unix Makefiles +-DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo +-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake +-DQT_QMAKE_EXECUTABLE:STRING=%{Qt:qmakeExecutable} +-DCMAKE_PREFIX_PATH:STRING=%{Qt:QT_INSTALL_PREFIX} +-DCMAKE_C_COMPILER:STRING=%{Compiler:Executable:C} +-DCMAKE_CXX_COMPILER:STRING=%{Compiler:Executable:Cxx} +-DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG} + 0 + /home/geoffrey/Documents/GIT/cryptoshield/RElicAnalysis/build-RElicAnalysis-Desktop-Profile + + + + + all + + false + + true + CMakeProjectManager.MakeStep + + 1 + Build + Build + ProjectExplorer.BuildSteps.Build + + + + + + clean + + false + + true + CMakeProjectManager.MakeStep + + 1 + Clean + Clean + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + Profile + CMakeProjectManager.CMakeBuildConfiguration + + + MinSizeRel + 2 + false + + -DCMAKE_GENERATOR:STRING=Unix Makefiles +-DCMAKE_BUILD_TYPE:STRING=MinSizeRel +-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake +-DQT_QMAKE_EXECUTABLE:STRING=%{Qt:qmakeExecutable} +-DCMAKE_PREFIX_PATH:STRING=%{Qt:QT_INSTALL_PREFIX} +-DCMAKE_C_COMPILER:STRING=%{Compiler:Executable:C} +-DCMAKE_CXX_COMPILER:STRING=%{Compiler:Executable:Cxx} +-DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG} + /home/geoffrey/Documents/GIT/cryptoshield/RElicAnalysis/build-RElicAnalysis-Desktop-MinSizeRel + + + + + all + + false + + true + CMakeProjectManager.MakeStep + + 1 + Build + Build + ProjectExplorer.BuildSteps.Build + + + + + + clean + + false + + true + CMakeProjectManager.MakeStep + + 1 + Clean + Clean + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + Minimum Size Release + CMakeProjectManager.CMakeBuildConfiguration + + 5 + + + 0 + Deploy + Deploy + ProjectExplorer.BuildSteps.Deploy + + 1 + + false + ProjectExplorer.DefaultDeployConfiguration + + 1 + + true + true + 0 + true + + + 2 + + false + + ProjectExplorer.CustomExecutableRunConfiguration + + false + true + true + + 1 + + + + ProjectExplorer.Project.TargetCount + 1 + + + ProjectExplorer.Project.Updater.FileVersion + 22 + + + Version + 22 + + diff --git a/RElicAnalysis/centralwidget.cpp b/RElicAnalysis/centralwidget.cpp new file mode 100644 index 0000000..ca05668 --- /dev/null +++ b/RElicAnalysis/centralwidget.cpp @@ -0,0 +1,40 @@ +#include "centralwidget.h" + + +CentralWidget::CentralWidget(MainWindow *mainWindow): QMdiArea() { + m_mainWindow = mainWindow; + setViewMode(QMdiArea::TabbedView); + setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + setAttribute(Qt::WA_Resized); + //setTabPosition(QTabWidget::West); + + /*setStyleSheet("QTabWidget::pane {" + "border: 1px solid lightgray; " + "top:-1px; " + "color: #FFF;" + "background-color: #656565;} " + "QTabBar::tab {padding:15px;background-color: #656565;}" + "QTabBar::tab::hover {background-color: #aaa;}" + "QTabBar::tab::selected {background-color: #353535;border-left:3px solid #000;color:#aaa;font-weight:bold;}"); + */ + //this->_addWelcomeTab(); +} +void CentralWidget::closeCurrentProject(){ + +} +void CentralWidget::_addWelcomeTab(){ + QMdiSubWindow *window = new QMdiSubWindow; + window->setWindowTitle("Welcome"); + window->resize(this->width(), this->height()); + window->setWindowFlags(Qt::FramelessWindowHint); + + QWidget *mainWidget = new QWidget; + QVBoxLayout *layout = new QVBoxLayout; + + + mainWidget->setLayout(layout); + window->setWidget(mainWidget); + + addSubWindow(window)->show(); +} +CentralWidget::~CentralWidget(){} diff --git a/RElicAnalysis/centralwidget.h b/RElicAnalysis/centralwidget.h new file mode 100644 index 0000000..725fbc8 --- /dev/null +++ b/RElicAnalysis/centralwidget.h @@ -0,0 +1,25 @@ +#ifndef CENTRALWIDGET_H +#define CENTRALWIDGET_H + +#include +#include +#include +#include +#include "elf/mdielfhandler.h" + + +class MainWindow; +class CentralWidget: public QMdiArea { + Q_OBJECT + public: + CentralWidget(MainWindow *); + ~CentralWidget(); + void closeCurrentProject(); + + private: + MainWindow *m_mainWindow; + + void _addWelcomeTab(); +}; + +#endif // CENTRALWIDGET_H diff --git a/RElicAnalysis/data.h b/RElicAnalysis/data.h new file mode 100644 index 0000000..ac0cc18 --- /dev/null +++ b/RElicAnalysis/data.h @@ -0,0 +1,110 @@ +#ifndef DATA_H +#define DATA_H + +#include + +/* Signature file */ +#define SIGNATURE_UNKNOWN 0 +#define SIGNATURE_ELF 1 +#define SIGNATURE_PE 2 +#define SIGNATURE_PNG 3 + +/*** ELF ***/ +// quint16 -> unsigned short -> 16bits -> 2 bytes +// quint32 -> unsigned int -> 32bits -> 4 bytes +// quint64 -> unsigned long long int -> 64bits -> 8 bytes + +struct elfIdent{ + QByteArray signature[4]; + QByteArray arch; + QByteArray endianess; // LSB = 1; MSB = 2 + QByteArray version; + QByteArray abi; + QByteArray vers_abi; + QByteArray padding[7]; +}; + +struct elfHdr{ + QByteArray e_type; + QByteArray e_machine; + QByteArray e_version; + QByteArray e_entry; + QByteArray e_phoff; + QByteArray e_shoff; + QByteArray e_flags; + QByteArray e_ehsize; + QByteArray e_phentsize; + QByteArray e_phnum; + QByteArray e_shentsize; + QByteArray e_shnum; + QByteArray e_shstrndx; +}; +struct elf32Hdr{ + /* + * For type + * https://github.com/torvalds/linux/blob/master/include/uapi/linux/elf.h + */ + quint16 e_type; + quint16 e_machine; + quint32 e_version; + quint32 e_entry; + quint32 e_phoff; + quint32 e_shoff; + quint32 e_flags; + quint16 e_ehsize; + quint16 e_phentsize; + quint16 e_phnum; + quint16 e_shentsize; + quint16 e_shnum; + quint16 e_shstrndx; +}; +struct elfProgram { + QByteArray p_type; + QByteArray p_flags; + QByteArray p_offset; + QByteArray p_vaddr; + QByteArray p_paddr; + QByteArray p_filesz; + QByteArray p_memsz; + QByteArray p_align; +}; +struct elfSection { + QByteArray sh_name; + QByteArray sh_type; + QByteArray sh_flags; + QByteArray sh_addr; + QByteArray sh_offset; + QByteArray sh_size; + QByteArray sh_link; + QByteArray sh_info; + QByteArray sh_addralign; + QByteArray sh_entsize; +}; + +struct elfObjectDisas { + QByteArray cmd; // Store the command + //char *hexCmd; // Store the command in hex + QByteArray hexCmd; + //char *hexCmd2; + size_t octets; + size_t addr; // Command start at +}; + +struct elfSymbol { + QByteArray st_name; + QByteArray st_value; + QByteArray st_size; + unsigned char st_info; + unsigned char st_other; + QByteArray st_shndx; + char *data; + uint64_t size; + int countEntries; + int entry; +}; + +struct elfHash { + +}; + +#endif // DATA_H diff --git a/RElicAnalysis/dataDisas.h b/RElicAnalysis/dataDisas.h new file mode 100644 index 0000000..40596df --- /dev/null +++ b/RElicAnalysis/dataDisas.h @@ -0,0 +1,22 @@ +#ifndef DATADISAS_H +#define DATADISAS_H + +#include + + +#define NUMBER_STRUCT_ENTRIES 128 +#define SIZE_OBJECT_CMD 64 + +struct object_asm { + char cmd[SIZE_OBJECT_CMD]; // Store the command + char *hexCmd; // Store the command in hex + size_t octets; + size_t addr; // Command start at +}; + +struct dataDisas { + struct object_asm *s_object_asm; + int countEntries; +}; + +#endif // DATADISAS_H diff --git a/RElicAnalysis/dialogabout.cpp b/RElicAnalysis/dialogabout.cpp new file mode 100644 index 0000000..c6cf9f0 --- /dev/null +++ b/RElicAnalysis/dialogabout.cpp @@ -0,0 +1,81 @@ +#include "dialogabout.h" + +DialogAbout::DialogAbout(): QDialog() { + setWindowTitle("About RElicAnalysis"); + //setStyleSheet("QDialog::border: 1px solid #afafaf;"); + + QVBoxLayout *mainLayout = new QVBoxLayout; + + QLabel *labelTitle = new QLabel("RElicAnalysis 0.1.0"); + QFont fontTitle; + fontTitle.setPointSize(15); + fontTitle.setBold(true); + labelTitle->setFont(fontTitle); + mainLayout->addWidget(labelTitle); + + QString basedOn = "Based on Qt " + QByteArray(QT_VERSION_STR); + QLabel *labelBasedOn = new QLabel(basedOn); + mainLayout->addWidget(labelBasedOn); + + QLabel *labelAuthor = new QLabel("Author: Bucchino Geoffrey"); + mainLayout->addWidget(labelAuthor); + + QLabel *labelLicense = new QLabel("Under the licence GPL"); + mainLayout->addWidget(labelLicense); + + QLabel *labelWebsite = new QLabel("Visit https://cryptoshield.bucchino.org/ for more information"); + mainLayout->addWidget(labelWebsite); + + QHBoxLayout *layoutButtons = new QHBoxLayout; + QPushButton *buttonQuit = new QPushButton("Close"); + buttonQuit->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + connect(buttonQuit, SIGNAL(clicked()), this, SLOT(close())); + layoutButtons->addStretch(1); + layoutButtons->addWidget(buttonQuit, 0, Qt::AlignRight); + + mainLayout->addLayout(layoutButtons); + mainLayout->addStretch(); + setLayout(mainLayout); +} + +/*DialogAbout::DialogAbout() : QDialog() { + QVBoxLayout *layout = new QVBoxLayout; + + // Informations + QGridLayout *layoutAbout = new QGridLayout; + QLabel *labelLeftName = new QLabel("Nom du programme"); + QLabel *labelRightName = new QLabel("Editeur LaTex"); + QLabel *labelLeftVersion = new QLabel("Version"); + QLabel *labelRightVersion = new QLabel("1.0"); + QLabel *labelLeftLicence = new QLabel("Licence"); + QLabel *labelRightLicence = new QLabel("GPL"); + QLabel *labelLeftAuthor= new QLabel("Auteur"); + QLabel *labelRightAuthor = new QLabel("BUCCHINO Geoffrey"); + QLabel *labelLeftURL = new QLabel("Adresse Internet"); + QLabel *labelRightURL = new QLabel("www.geoffreybucchino.com"); + + layoutAbout->addWidget(labelLeftName, 0,0); + layoutAbout->addWidget(labelRightName, 0,1); + layoutAbout->addWidget(labelLeftVersion,1,0); + layoutAbout->addWidget(labelRightVersion,1,1); + layoutAbout->addWidget(labelLeftLicence,2,0); + layoutAbout->addWidget(labelRightLicence,2,1); + layoutAbout->addWidget(labelLeftAuthor,3,0); + layoutAbout->addWidget(labelRightAuthor, 3,1); + layoutAbout->addWidget(labelLeftURL,4,0); + layoutAbout->addWidget(labelRightURL,4,1); + + // Button + QHBoxLayout *layoutButton = new QHBoxLayout; + layoutButton->setAlignment(Qt::AlignRight); + QPushButton *buttonOk = new QPushButton("Ok"); + connect(buttonOk, SIGNAL(clicked()), this, SLOT(close())); + + layoutButton->addWidget(buttonOk); + + layout->addLayout(layoutAbout); + layout->addLayout(layoutButton); + + setLayout(layout); +}*/ + diff --git a/RElicAnalysis/dialogabout.h b/RElicAnalysis/dialogabout.h new file mode 100644 index 0000000..68604fd --- /dev/null +++ b/RElicAnalysis/dialogabout.h @@ -0,0 +1,19 @@ +#ifndef DIALOGABOUT_H +#define DIALOGABOUT_H + +#include +#include +#include +#include +#include +#include + +class DialogAbout : public QDialog +{ +public: + DialogAbout(); + + +}; + +#endif // DIALOGABOUT_H diff --git a/RElicAnalysis/disasasm.cpp b/RElicAnalysis/disasasm.cpp new file mode 100644 index 0000000..173f958 --- /dev/null +++ b/RElicAnalysis/disasasm.cpp @@ -0,0 +1,156 @@ +#include "disasasm.h" +#include + + +static int dump_asm(void *stream, const char *fmt, ...){ + struct dataDisas *sData = (struct dataDisas*)stream; + size_t index = sData->countEntries; + + va_list args; + va_start(args, fmt); + char str[SIZE_OBJECT_CMD]; + vsprintf(str, fmt, args); + + va_end(args); + + char tmp[SIZE_OBJECT_CMD]; + memset(tmp, 0, SIZE_OBJECT_CMD); + if (strlen(sData->s_object_asm[index].cmd) > 0){ + memcpy(tmp, sData->s_object_asm[index].cmd, strlen(sData->s_object_asm[index].cmd)); + //strlcat(tmp, str, strlen(str)); + strcat(tmp, str); + } + else + memcpy(tmp, str, strlen(str)); + + memcpy(sData->s_object_asm[index].cmd, tmp, strlen(tmp)); + return 0; +} +/* + * Cf: https://github.com/bpftrace/bpftrace/blob/master/src/bfd-disasm.cpp#L49C1-L55C2 + */ +/*static int dump_asm_styled(void *out __attribute__((unused)), enum disassembler_style s __attribute__((unused)), + const char *fmt __attribute__((unused)), ...) +{ + struct dataDisas *sData = (struct dataDisas*)out; + size_t index = sData->countEntries; + + va_list args; + va_start(args, fmt); + char str[SIZE_OBJECT_CMD]; + vsprintf(str, fmt, args); + + va_end(args); + + char tmp[SIZE_OBJECT_CMD]; + memset(tmp, 0, SIZE_OBJECT_CMD); + if (strlen(sData->s_object_asm[index].cmd) > 0){ + memcpy(tmp, sData->s_object_asm[index].cmd, strlen(sData->s_object_asm[index].cmd)); + //strlcat(tmp, str, strlen(str)); + strcat(tmp, str); + } + else + memcpy(tmp, str, strlen(str)); + + memcpy(sData->s_object_asm[index].cmd, tmp, strlen(tmp)); + return 0; +}*/ + struct dataDisas *disas_asm(unsigned char *buffer, uint64_t startAddress, size_t length){ + struct disassemble_info disasm_info; + + /* + * We are going to allocate our struct dataDisas which will contains our assembly code + * We are unallocated in parsing Elf file + */ + struct dataDisas *sData = (struct dataDisas*)malloc(sizeof(struct dataDisas)); + if (sData == NULL){ + qDebug() << "Failed to allocate"; + return NULL; + } + + sData->s_object_asm = (struct object_asm*)malloc(sizeof(struct object_asm) * NUMBER_STRUCT_ENTRIES); + if (sData->s_object_asm == NULL){ + qDebug() << "Failed to allocate"; + free(sData); + return NULL; + } + memset(sData->s_object_asm, 0, sizeof(&sData->s_object_asm)); + for (int i = 0; i < NUMBER_STRUCT_ENTRIES; i++) + memset(sData->s_object_asm[i].cmd, 0, SIZE_OBJECT_CMD); + + sData->countEntries = 0; + + /* + * Init out disassemble_info struct and provide our sData structure as a stream + * Find in the file dis-asm.h: + * Defined in https://github.com/redox-os/binutils-gdb/blob/master/include/dis-asm.h + * For the architecture: https://ftp.gnu.org/old-gnu/Manuals/bfd-2.9.1/html_node/bfd_29.html + */ + // TODO: Need to identify the version of the libbinutils, for old system, it's with three arguments + //qDebug() << BFD_VERSION; + //qDebug() << bfd_version_string(); + + /* + * https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git/commit/?id=600b7b26c07a070d0153daa76b3806c1e52c9e00 + * https://patchwork.yoctoproject.org/project/arm/patch/20220824025819.4888-1-jon.mason@arm.com/#5104 + */ + //init_disassemble_info (&disasm_info, sData, dump_asm, dump_asm_styled); + init_disassemble_info (&disasm_info, sData, dump_asm); + disasm_info.arch = bfd_arch_i386; + disasm_info.mach = bfd_mach_x86_64; + disasm_info.read_memory_func = buffer_read_memory; + disasm_info.buffer = buffer; + disasm_info.buffer_vma = 0; + disasm_info.buffer_length = length; + disassemble_init_for_target(&disasm_info); + + disassembler_ftype disas; + disas = disassembler(bfd_arch_i386, false, bfd_mach_x86_64, NULL); + + if (!disas){ + qDebug() << "Cannot be disassembled"; + return NULL; + } + + size_t i = 0; + int count = 0; + int relocating = 1; + int nextStart = startAddress; + int bufferIndex = 0; + while (i < length){ + size_t octets = disas(i, &disasm_info); + sData->s_object_asm[sData->countEntries].octets = octets; + sData->s_object_asm[sData->countEntries].addr = nextStart; + + /* + * hexCmd will contain the command in hex + * We free in the parsingElf file + */ + /* hexCmd object will be freed in parsingElf file */ + sData->s_object_asm[sData->countEntries].hexCmd = (char*)malloc(sizeof(char) * octets); + memcpy(sData->s_object_asm[sData->countEntries].hexCmd, + &buffer[bufferIndex], + octets); + i += octets; + nextStart += octets; + bufferIndex += octets; + + sData->countEntries++; + count++; + + // If we have an out of memory, we extend the buffer + if (count == NUMBER_STRUCT_ENTRIES){ + relocating++; + sData->s_object_asm = (struct object_asm *)realloc(sData->s_object_asm, sizeof(struct object_asm) * (NUMBER_STRUCT_ENTRIES * relocating)); + + // Reinit ? + + count = 0; + } + // Init the next structure + memset(&sData->s_object_asm[sData->countEntries], 0, sizeof(&sData->s_object_asm)); + memset(sData->s_object_asm[sData->countEntries].cmd, 0, SIZE_OBJECT_CMD); + }; + + return sData; +} diff --git a/RElicAnalysis/disasasm.h b/RElicAnalysis/disasasm.h new file mode 100644 index 0000000..7c2b6bc --- /dev/null +++ b/RElicAnalysis/disasasm.h @@ -0,0 +1,28 @@ +#ifndef DISASASM_H +#define DISASASM_H + +#include +#include +#include +#include +#include +#include +#include + +#include +#include "bfd.h" +#include "dataDisas.h" + +/* + * References + * - https://dwarfstd.org/doc/Debugging%20using%20DWARF-2012.pdf + * - https://ftp.gnu.org/old-gnu/Manuals/bfd-2.9.1/html_chapter/bfd_1.html + */ + +// +static bfd_boolean disassemble; + +static int dump_asm(void *, const char *,...); + struct dataDisas *disas_asm(unsigned char *, uint64_t, size_t); + +#endif // DISASASM_H diff --git a/RElicAnalysis/dockmenu.cpp b/RElicAnalysis/dockmenu.cpp new file mode 100644 index 0000000..f4174dc --- /dev/null +++ b/RElicAnalysis/dockmenu.cpp @@ -0,0 +1,56 @@ +#include "dockmenu.h" + +QDockMenu::QDockMenu() { + // Sidebar widget + QWidget *sidebar = new QWidget; + QVBoxLayout *layout = new QVBoxLayout(sidebar); + layout->setSpacing(10); + layout->setContentsMargins(5, 5, 5, 5); + + // Create buttons + QToolButton *btnWelcome = new QToolButton; + btnWelcome->setIcon(QIcon(":/icons/welcome.png")); // your icon + btnWelcome->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); + btnWelcome->setText("Welcome"); + + QToolButton *btnEdit = new QToolButton; + btnEdit->setIcon(QIcon(":/icons/edit.png")); + btnEdit->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); + btnEdit->setText("Edit"); + + QToolButton *btnDesign = new QToolButton; + btnDesign->setIcon(QIcon(":/icons/design.png")); + btnDesign->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); + btnDesign->setText("Design"); + + QToolButton *btnDebug = new QToolButton; + btnDebug->setIcon(QIcon(":/icons/debug.png")); + btnDebug->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); + btnDebug->setText("Debug"); + + QToolButton *btnProjects = new QToolButton; + btnProjects->setIcon(QIcon(":/icons/projects.png")); + btnProjects->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); + btnProjects->setText("Projects"); + + QToolButton *btnHelp = new QToolButton; + btnHelp->setIcon(QIcon(":/icons/help.png")); + btnHelp->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); + btnHelp->setText("Help"); + + // Add buttons to layout + layout->addWidget(btnWelcome); + layout->addWidget(btnEdit); + layout->addWidget(btnDesign); + layout->addWidget(btnDebug); + layout->addWidget(btnProjects); + layout->addWidget(btnHelp); + layout->addStretch(); // push buttons to top + + // Set sidebar as dock widget or fixed widget + //window.setCentralWidget(new QWidget); // placeholder for main area + //window.addDockWidget(Qt::LeftDockWidgetArea, new QDockWidget("Sidebar", &window)); + //window.findChild()->setWidget(sidebar); + //window.findChild()->setFeatures(QDockWidget::NoDockWidgetFeatures); // fix it + +} diff --git a/RElicAnalysis/dockmenu.h b/RElicAnalysis/dockmenu.h new file mode 100644 index 0000000..271b237 --- /dev/null +++ b/RElicAnalysis/dockmenu.h @@ -0,0 +1,15 @@ +#ifndef DOCKMENU_H +#define DOCKMENU_H + +#include +#include +#include +#include + +class QDockMenu +{ +public: + QDockMenu(); +}; + +#endif // DOCKMENU_H diff --git a/RElicAnalysis/elf.cpp b/RElicAnalysis/elf.cpp new file mode 100644 index 0000000..957618d --- /dev/null +++ b/RElicAnalysis/elf.cpp @@ -0,0 +1,233 @@ +#include +#include +#include +#include +#include +#include +#include + +struct elf_ident{ + unsigned char arch; + unsigned char endianess; + unsigned char version; + unsigned char abi; + unsigned char vers_abi; + unsigned char padding[7]; + unsigned char len; +} __attribute__((packed)); + +static int getSignature(int); +static void elf32(int, off_t, Elf32_Ehdr*); +static void elf64(int, off_t, Elf64_Ehdr*); +static void elf64ProgramHdr(int, Elf64_Off, off_t); +static void elf32ProgramHdr(int, Elf32_Off, off_t); +static void elf64SectionHdr(int, Elf64_Off, off_t); +static void elf32SectionHdr(int, Elf32_Off, off_t); +int main(int argc, char *argv[]){ + struct stat st; + struct elf_ident *sElfIdent = NULL; + char buffer_ident[12] = {0}; + int fd_r; + size_t length; + int res; + if (argc < 2){ + printf("Please, specify the ELF file\n"); + exit(-1); + } + + /* Get stat information */ + stat(argv[1], &st); + + if ((fd_r = open(argv[1], O_RDONLY)) == -1){ + printf("Failed to read the file %s\n", argv[1]); + exit(-1); + } + /* Get th 4 first bytes to get the signature */ + res = getSignature(fd_r); + if (res == -1 || res == 0){ + printf("Failed to get the signature of the file\n"); + exit(-1); + } + + /* Now, we get the 12 first bytes for the ELF identification */ + length = read(fd_r, buffer_ident, 12); + + sElfIdent = (struct elf_ident*)buffer_ident; + + printf("ELF header information:\n"); + printf("Arch:\t\t%d\n", sElfIdent->arch); + printf("Endianess:\t%d\n", sElfIdent->endianess); + printf("Version:\t%d\n", sElfIdent->version); + printf("ABI:\t\t%d\n", sElfIdent->abi); + printf("Version ABI:\t%d\n", sElfIdent->vers_abi); + /*printf("Padding: "); + for(int i = 0; i < 7; i++) + printf("%d ", sElfIdent->padding[i]); + printf("\n");*/ + printf("Length:\t\t%d\n", sElfIdent->len); + + // Now, get all the Elf header + lseek(fd_r, 0, SEEK_SET); + + if (sElfIdent->arch == 1) { // Arch x82 + char buffer[sizeof(Elf32_Ehdr)]; // 52 bytes + length = read(fd_r, buffer, 52); + elf32(fd_r, st.st_size, (Elf32_Ehdr*)buffer); + + }else if(sElfIdent->arch == 2) { // Arch x64 + char buffer[sizeof(Elf64_Ehdr)]; // 64 bytes + length = read(fd_r, buffer, 64); + elf64(fd_r, st.st_size, (Elf64_Ehdr*)buffer); + } + + + close(fd_r); +} +/* + * This function get the signature to be sure if the file is a ELF + */ +static int getSignature(int fd) { + char buffer[4]; + size_t length = read(fd, buffer, 4); + + if (length == -1) + return -1; + + /*if (strcmp(buffer, "\x7f\x45\x4c\x46") == 0){ + printf("ELF\n"); + }*/ + if (buffer[0] == 0x7f && buffer[1] == 0x45 && buffer[2] == 0x4c && buffer[3] == 0x46) + return 1; + return 0; +} +static void elf64(int fd, off_t fileLen, Elf64_Ehdr *ehdr){ + size_t len = fileLen - sizeof(Elf64_Ehdr); + char buffer[len]; + + printf("Type: %d\n", ehdr->e_type); + printf("Machine: %d\n", ehdr->e_machine); + printf("Version: %d\n", ehdr->e_version); + printf("entry: %d\n", ehdr->e_entry); + printf("Phoff: %d\n", ehdr->e_phoff); + printf("Shoff: %d\n", ehdr->e_shoff); + printf("Flags: %d\n", ehdr->e_flags); + printf("Ehsize: %d\n", ehdr->e_ehsize); + printf("Phentsize: %d\n", ehdr->e_phentsize); + printf("Phnum: %d\n", ehdr->e_phnum); + printf("ShEntSize: %d\n", ehdr->e_shentsize); + printf("ShNum: %d\n", ehdr->e_shnum); + printf("Shstrndx: %d\n", ehdr->e_shstrndx); + + // Read the rest of the file + //printf("%d\n", len); + //read(fd, buffer, len); + /*for (int i = 0; i < len; i++) + printf("%02x ", buffer[i]);*/ + + // Get program headers + elf64ProgramHdr(fd, ehdr->e_phoff, ehdr->e_phnum); + + // Get section headers + elf64SectionHdr(fd, ehdr->e_shoff, ehdr->e_phnum); +} +static void elf32(int fd, off_t fileLen, Elf32_Ehdr *ehdr){ + size_t len = fileLen - sizeof(Elf32_Ehdr); + char buffer[len]; + + printf("Type: %d\n", ehdr->e_type); + printf("Machine: %d\n", ehdr->e_machine); + printf("Version: %d\n", ehdr->e_version); + printf("entry: %d\n", ehdr->e_entry); + printf("Phoff: %d\n", ehdr->e_phoff); + printf("Shoff: %d\n", ehdr->e_shoff); + printf("Flags: %d\n", ehdr->e_flags); + printf("Ehsize: %d\n", ehdr->e_ehsize); + printf("Phentsize: %d\n", ehdr->e_phentsize); + printf("Phnum: %d\n", ehdr->e_phnum); + printf("ShEntSize: %d\n", ehdr->e_shentsize); + printf("ShNum: %d\n", ehdr->e_shnum); + printf("Shstrndx: %d\n", ehdr->e_shstrndx); + + // Get program headers + elf32ProgramHdr(fd, ehdr->e_phoff, ehdr->e_phnum); +} +static void elf64ProgramHdr(int fd, Elf64_Off phoff, off_t phnum){ + printf("%ld\n", sizeof(Elf64_Phdr)); // 56 bytes + + size_t len = sizeof(Elf64_Phdr); + char buf[len]; + + // Go to the program header part + lseek(fd, phoff, SEEK_SET); + + for (int i = 0; i < phnum; i++){ + read(fd, buf, len); + Elf64_Phdr *phdr = (Elf64_Phdr*)buf; + printf("\nProgram header\n"); + printf("Type: %d\n", phdr->p_type); + printf("Offset: %d\n", phdr->p_offset); + printf("Vaddr: %d\n", phdr->p_vaddr); + printf("Paddr: %d\n", phdr->p_paddr); + printf("File size: %d\n", phdr->p_filesz); + printf("Mem size: %d\n", phdr->p_memsz); + printf("Flags: %d\n", phdr->p_flags); + printf("Align: %d\n", phdr->p_align); + + memset(buf, 0, len); + } +} +static void elf32ProgramHdr(int fd, Elf32_Off phoff, off_t phnum){ + size_t len = sizeof(Elf32_Phdr); + char buf[len]; + + // Go to the program header part + lseek(fd, phoff, SEEK_SET); + + for (int i = 0; i < phnum; i++){ + read(fd, buf, len); + Elf32_Phdr *phdr = (Elf32_Phdr*)buf; + printf("\nProgram header\n"); + printf("Type: %d\n", phdr->p_type); + printf("Offset: %d\n", phdr->p_offset); + printf("Vaddr: %d\n", phdr->p_vaddr); + printf("Paddr: %d\n", phdr->p_paddr); + printf("File size: %d\n", phdr->p_filesz); + printf("Mem size: %d\n", phdr->p_memsz); + printf("Flags: %d\n", phdr->p_flags); + printf("Align: %d\n", phdr->p_align); + + memset(buf, 0, len); + } +} +static void elf64SectionHdr(int fd, Elf64_Off phoff, off_t shnum){ + size_t len = sizeof(Elf64_Shdr); + char buf[len]; + + // Go to the program header part + lseek(fd, phoff, SEEK_SET); + + for (int i = 0; i < shnum; i++){ + read(fd, buf, len); + Elf64_Shdr *shdr = (Elf64_Shdr*)buf; + printf("\nSection header\n"); + printf("Name: %d\n", shdr->sh_name); + printf("Type: %d\n", shdr->sh_type); + + memset(buf, 0, len); + } +} +static void elf32SectionHdr(int fd, Elf32_Off phoff, off_t shnum){ + size_t len = sizeof(Elf32_Shdr); + char buf[len]; + + // Go to the program header part + lseek(fd, phoff, SEEK_SET); + + for (int i = 0; i < shnum; i++){ + read(fd, buf, len); + Elf32_Shdr *shdr = (Elf32_Shdr*)buf; + printf("\nSection header\n"); + + memset(buf, 0, len); + } +} diff --git a/RElicAnalysis/elf/dialogelfheader.cpp b/RElicAnalysis/elf/dialogelfheader.cpp new file mode 100644 index 0000000..4e98495 --- /dev/null +++ b/RElicAnalysis/elf/dialogelfheader.cpp @@ -0,0 +1,84 @@ +#include "dialogelfheader.h" + +DialogElfHeader::DialogElfHeader(ElfData *elfData): QDialog() { + struct elfHdr *sElfHdr = elfData->getHeader(); + setWindowTitle("ELF Header"); + setFixedSize(350, 250); + + QVBoxLayout *mainLayout = new QVBoxLayout; + + QGridLayout *layoutData = new QGridLayout; + layoutData->setSpacing(0); + layoutData->setContentsMargins(0, 0, 0, 0); + QLabel *labelType = new QLabel("Type: "); + QLabel *labelTypeHex = new QLabel( + elfData->mapTypeElf(hexToInt(sElfHdr->e_type.toHex())) + + " (" + printHex(sElfHdr->e_type.toHex()) + ")" + ); + QLabel *labelMachine = new QLabel("Machine: "); + QLabel *labelMachineHex = new QLabel( + elfData->mapMachineType(hexToInt(sElfHdr->e_machine.toHex())) + + " (" + printHex(sElfHdr->e_machine.toHex()) + ")" + ); + QLabel *labelVersion = new QLabel("Version: "); + QLabel *labelVersionHex = new QLabel(printHex(sElfHdr->e_version.toHex())); + QLabel *labelEntryAddres = new QLabel("Entry address: "); + QLabel *labelEntryAddressHex = new QLabel(printHex(sElfHdr->e_entry.toHex())); + QLabel *labelProgHdrOff = new QLabel("Program Header offset: "); + QLabel *labelProgHdrOffHex = new QLabel(printHex(sElfHdr->e_phoff.toHex())); + QLabel *labelSectionHdrOff = new QLabel("Section Header Offset: "); + QLabel *labelSectionHdrOffHex = new QLabel(printHex(sElfHdr->e_shoff.toHex())); + QLabel *labelFlags = new QLabel("Flags: "); + QLabel *labelFlagsHex = new QLabel(printHex(sElfHdr->e_flags.toHex())); + QLabel *labelElfHdrSize = new QLabel("ELF Header size: "); + QLabel *labelElfHdrSizeHex = new QLabel(printHex(sElfHdr->e_ehsize.toHex())); + QLabel *labelProgEntrySize = new QLabel("Program entry size: "); + QLabel *labelProgEntrySizeHex = new QLabel(printHex(sElfHdr->e_phentsize.toHex())); + QLabel *labelProgNum = new QLabel("Number of program entry: "); + QLabel *labelProgNumHex = new QLabel(printHex(sElfHdr->e_phnum.toHex())); + QLabel *labelSectionHdrSize = new QLabel("Section Header size: "); + QLabel *labelSectionHdrSizeHex = new QLabel(printHex(sElfHdr->e_shentsize.toHex())); + QLabel *labelSectionNum = new QLabel("Number of section entry: "); + QLabel *labelSectionNumHex = new QLabel(printHex(sElfHdr->e_shnum.toHex())); + QLabel *labelSectionStrIndex = new QLabel("Section string index: "); + QLabel *labelSectionStrIndexHex = new QLabel(printHex(sElfHdr->e_shentsize.toHex())); + + layoutData->addWidget(labelType, 0, 0); + layoutData->addWidget(labelTypeHex, 0, 1); + layoutData->addWidget(labelMachine, 1, 0); + layoutData->addWidget(labelMachineHex, 1, 1); + layoutData->addWidget(labelVersion, 2, 0); + layoutData->addWidget(labelVersionHex, 2, 1); + layoutData->addWidget(labelEntryAddres, 3, 0); + layoutData->addWidget(labelEntryAddressHex, 3, 1); + layoutData->addWidget(labelProgHdrOff, 4, 0); + layoutData->addWidget(labelProgHdrOffHex, 4, 1); + layoutData->addWidget(labelSectionHdrOff, 5, 0); + layoutData->addWidget(labelSectionHdrOffHex, 5, 1); + layoutData->addWidget(labelFlags, 6, 0); + layoutData->addWidget(labelFlagsHex, 6, 1); + layoutData->addWidget(labelElfHdrSize, 7, 0); + layoutData->addWidget(labelElfHdrSizeHex, 7, 1); + layoutData->addWidget(labelProgEntrySize, 8, 0); + layoutData->addWidget(labelProgEntrySizeHex, 8, 1); + layoutData->addWidget(labelProgNum, 9, 0); + layoutData->addWidget(labelProgNumHex, 9, 1); + layoutData->addWidget(labelSectionHdrSize, 10, 0); + layoutData->addWidget(labelSectionHdrSizeHex, 10, 1); + layoutData->addWidget(labelSectionNum, 11, 0); + layoutData->addWidget(labelSectionNumHex, 11, 1); + layoutData->addWidget(labelSectionStrIndex, 12, 0); + layoutData->addWidget(labelSectionStrIndexHex, 12, 1); + + QHBoxLayout *layoutButton = new QHBoxLayout; + layoutButton->setAlignment(Qt::AlignRight); + QPushButton *buttonClose = new QPushButton("Close"); + connect(buttonClose, SIGNAL(clicked()), this, SLOT(close())); + + layoutButton->addWidget(buttonClose); + + mainLayout->addLayout(layoutData); + mainLayout->addLayout(layoutButton); + + setLayout(mainLayout); +} diff --git a/RElicAnalysis/elf/dialogelfheader.h b/RElicAnalysis/elf/dialogelfheader.h new file mode 100644 index 0000000..a653ac8 --- /dev/null +++ b/RElicAnalysis/elf/dialogelfheader.h @@ -0,0 +1,20 @@ +#ifndef DIALOGELFHEADER_H +#define DIALOGELFHEADER_H + +#include +#include +#include +#include +#include +#include +#include "../data.h" +#include "../elfdata.h" +#include "../functions.h" + + +class DialogElfHeader: public QDialog { + public: + DialogElfHeader(ElfData *); +}; + +#endif // DIALOGELFHEADER_H diff --git a/RElicAnalysis/elf/dialogelfident.cpp b/RElicAnalysis/elf/dialogelfident.cpp new file mode 100644 index 0000000..b11c9b4 --- /dev/null +++ b/RElicAnalysis/elf/dialogelfident.cpp @@ -0,0 +1,62 @@ +#include "dialogelfident.h" + +DialogElfIdent::DialogElfIdent(ElfData *elfData): QDialog() { + struct elfIdent *sElfIdent = elfData->getIdentification(); + + setWindowTitle("ELF Identification"); + setFixedSize(300, 150); + QVBoxLayout *mainLayout = new QVBoxLayout; + + QGridLayout *layoutData = new QGridLayout; + layoutData->setSpacing(0); + layoutData->setContentsMargins(0, 0, 0, 0); + QLabel *labelSignature = new QLabel("Signature: "); + QLabel *labelSignatureHex = new QLabel(sElfIdent->signature[0] + + sElfIdent->signature[1] + + sElfIdent->signature[2] + + sElfIdent->signature[3]); + QLabel *labelArchitecture = new QLabel("Architecture: "); + QLabel *labelArchitectureHex = new QLabel( + elfData->mapArchitecture(hexToInt(sElfIdent->arch.toHex())) + + " (" + printHex(sElfIdent->arch.toHex()) + ")" + ); + QLabel *labelEndianness = new QLabel("Endianness: "); + QLabel *labelEndiannessHex = new QLabel( + elfData->mapEndianess(hexToInt(sElfIdent->endianess.toHex())) + + " (" + printHex(sElfIdent->endianess.toHex()) + ")" + ); + QLabel *labelVersion = new QLabel("Version: "); + QLabel *labelVersionHex = new QLabel(sElfIdent->version.toHex()); + QLabel *labelABI = new QLabel("ABI: "); + QLabel *labelABIHex = new QLabel( + elfData->mapABI(hexToInt(sElfIdent->abi.toHex())) + + " (" + printHex(sElfIdent->abi.toHex()) + ")" + ); + QLabel *labelVersABI = new QLabel("Version ABI: "); + QLabel *labelVersABIHex = new QLabel(sElfIdent->vers_abi.toHex()); + + layoutData->addWidget(labelSignature, 0, 0); + layoutData->addWidget(labelSignatureHex, 0, 1); + layoutData->addWidget(labelArchitecture, 1, 0); + layoutData->addWidget(labelArchitectureHex, 1, 1); + layoutData->addWidget(labelEndianness, 2, 0); + layoutData->addWidget(labelEndiannessHex, 2, 1); + layoutData->addWidget(labelVersion, 3, 0); + layoutData->addWidget(labelVersionHex, 3, 1); + layoutData->addWidget(labelABI, 4, 0); + layoutData->addWidget(labelABIHex, 4, 1); + layoutData->addWidget(labelVersABI, 5, 0); + layoutData->addWidget(labelVersABIHex, 5, 1); + + QHBoxLayout *layoutButton = new QHBoxLayout; + layoutButton->setAlignment(Qt::AlignRight); + QPushButton *buttonClose = new QPushButton("Close"); + connect(buttonClose, SIGNAL(clicked()), this, SLOT(close())); + + layoutButton->addWidget(buttonClose); + + mainLayout->addLayout(layoutData); + mainLayout->addLayout(layoutButton); + + setLayout(mainLayout); +} diff --git a/RElicAnalysis/elf/dialogelfident.h b/RElicAnalysis/elf/dialogelfident.h new file mode 100644 index 0000000..1f381b6 --- /dev/null +++ b/RElicAnalysis/elf/dialogelfident.h @@ -0,0 +1,20 @@ +#ifndef DIALOGELFIDENT_H +#define DIALOGELFIDENT_H + +#include +#include +#include +#include +#include +#include +#include "../data.h" +#include "../elfdata.h" +#include "../functions.h" + + +class DialogElfIdent: public QDialog { + public: + DialogElfIdent(ElfData *); +}; + +#endif // DIALOGELFIDENT_H diff --git a/RElicAnalysis/elf/dialogelfprogram.cpp b/RElicAnalysis/elf/dialogelfprogram.cpp new file mode 100644 index 0000000..d1e1c84 --- /dev/null +++ b/RElicAnalysis/elf/dialogelfprogram.cpp @@ -0,0 +1,82 @@ +#include "dialogelfprogram.h" + +DialogElfProgram::DialogElfProgram(): QDialog() { + setWindowTitle("ELF Program"); + setFixedSize(350, 250); + + QVBoxLayout *mainLayout = new QVBoxLayout; + + QGridLayout *layoutData = new QGridLayout; + QLabel *labelProgramType = new QLabel("Program type: "); + m_labelProgramType = new QLabel; + QLabel *labelProgFlags = new QLabel("Flags: "); + m_labelProgFlags = new QLabel; + QLabel *labelOffset = new QLabel("Offset: "); + m_labelOffset = new QLabel; + QLabel *labelVA = new QLabel("Virtual Address: "); + m_labelVA = new QLabel; + QLabel *labelPA = new QLabel("Physical Address: "); + m_labelPA = new QLabel; + QLabel *labelProgramSize = new QLabel("Program size: "); + m_labelProgramSize = new QLabel; + QLabel *labelMemSize = new QLabel("Memory size: "); + m_labelMemSize = new QLabel; + QLabel *labelAlign = new QLabel("Align: "); + m_labelAlign = new QLabel; + + layoutData->addWidget(labelProgramType, 0, 0); + layoutData->addWidget(m_labelProgramType, 0, 1); + layoutData->addWidget(labelProgFlags, 1, 0); + layoutData->addWidget(m_labelProgFlags, 1, 1); + layoutData->addWidget(labelOffset, 2, 0); + layoutData->addWidget(m_labelOffset, 2, 1); + layoutData->addWidget(labelVA, 3, 0); + layoutData->addWidget(m_labelVA, 3, 1); + layoutData->addWidget(labelPA, 4, 0); + layoutData->addWidget(m_labelPA, 4, 1); + layoutData->addWidget(labelProgramSize, 5, 0); + layoutData->addWidget(m_labelProgramSize, 5, 1); + layoutData->addWidget(labelMemSize, 6, 0); + layoutData->addWidget(m_labelMemSize, 6, 1); + layoutData->addWidget(labelAlign, 7, 0); + layoutData->addWidget(m_labelAlign, 7, 1); + + QHBoxLayout *layoutButton = new QHBoxLayout; + layoutButton->setAlignment(Qt::AlignRight); + QPushButton *buttonClose = new QPushButton("Close"); + connect(buttonClose, SIGNAL(clicked()), this, SLOT(close())); + + layoutButton->addWidget(buttonClose); + + mainLayout->addLayout(layoutData); + mainLayout->addLayout(layoutButton); + + setLayout(mainLayout); +} +/*QString foo(QByteArray b){ + QString n = QString::number(hexToInt(b.toHex())); + return "0x" + b.toHex() + " (" + n + ")10"; +}*/ +void DialogElfProgram::setData(struct elfProgram *sElfP){ + m_labelProgramType->setText(printHex(sElfP->p_type.toHex())); + //m_labelProgramType->setText(foo(sElfP->p_type)); + m_labelProgFlags->setText(printHex(sElfP->p_flags.toHex())); + //qDebug() << sElfP->p_offset.toHex(); + //m_labelOffset->setText(QString::number(hexToInt(sElfP->p_offset.toHex()))); + m_labelOffset->setText(printHex(sElfP->p_offset.toHex())); + m_labelVA->setText(printHex(sElfP->p_vaddr.toHex())); + m_labelPA->setText(printHex(sElfP->p_paddr.toHex())); + m_labelProgramSize->setText(printHex(sElfP->p_filesz.toHex())); + m_labelMemSize->setText(printHex(sElfP->p_memsz.toHex())); + m_labelAlign->setText(printHex(sElfP->p_align.toHex())); +} +DialogElfProgram::~DialogElfProgram() { + delete m_labelProgramType; + delete m_labelProgFlags; + delete m_labelOffset; + delete m_labelVA; + delete m_labelPA; + delete m_labelProgramSize; + delete m_labelMemSize; + delete m_labelAlign; +} diff --git a/RElicAnalysis/elf/dialogelfprogram.h b/RElicAnalysis/elf/dialogelfprogram.h new file mode 100644 index 0000000..f69b0e1 --- /dev/null +++ b/RElicAnalysis/elf/dialogelfprogram.h @@ -0,0 +1,32 @@ +#ifndef DIALOGELFPROGRAM_H +#define DIALOGELFPROGRAM_H + +#include +#include +#include +#include +#include +#include +#include "../elfdata.h" +#include "../data.h" +#include "../functions.h" + + +class DialogElfProgram: public QDialog { + public: + DialogElfProgram(); + ~DialogElfProgram(); + void setData(struct elfProgram *); + + private: + QLabel *m_labelProgramType; + QLabel *m_labelProgFlags; + QLabel *m_labelOffset; + QLabel *m_labelVA; + QLabel *m_labelPA; + QLabel *m_labelProgramSize; + QLabel *m_labelMemSize; + QLabel *m_labelAlign; +}; + +#endif // DIALOGELFPROGRAM_H diff --git a/RElicAnalysis/elf/dialogelfsection.cpp b/RElicAnalysis/elf/dialogelfsection.cpp new file mode 100644 index 0000000..53eb774 --- /dev/null +++ b/RElicAnalysis/elf/dialogelfsection.cpp @@ -0,0 +1,85 @@ +#include "dialogelfsection.h" + +DialogElfSection::DialogElfSection() { + setWindowTitle("ELF Section"); + setFixedSize(350, 250); + + QVBoxLayout *mainLayout = new QVBoxLayout; + + QGridLayout *layoutData = new QGridLayout; + QLabel *labelSectionName = new QLabel("Section name: "); + m_labelSectionName = new QLabel; + QLabel *labelType = new QLabel("Type: "); + m_labelType = new QLabel; + QLabel *labelFlags = new QLabel("Flags: "); + m_labelFlags = new QLabel; + QLabel *labelAddress = new QLabel("Address: "); + m_labelAddress = new QLabel; + QLabel *labelOffset = new QLabel("Offset: "); + m_labelOffset = new QLabel; + QLabel *labelSectionSize = new QLabel("Section size: "); + m_labelSectionSize = new QLabel; + QLabel *labelLink = new QLabel("Link: "); + m_labelLink = new QLabel; + QLabel *labelInfo = new QLabel("Info: "); + m_labelInfo = new QLabel; + QLabel *labelAddressAlign = new QLabel("Address Align: "); + m_labelAddressAlign = new QLabel; + QLabel *labelEntrySize = new QLabel("Entry size: "); + m_labelEntrySize = new QLabel; + + layoutData->addWidget(labelSectionName, 0, 0); + layoutData->addWidget(m_labelSectionName, 0, 1); + layoutData->addWidget(labelType, 1, 0); + layoutData->addWidget(m_labelType, 1, 1); + layoutData->addWidget(labelFlags, 2, 0); + layoutData->addWidget(m_labelFlags, 2, 1); + layoutData->addWidget(labelAddress, 3, 0); + layoutData->addWidget(m_labelAddress, 3, 1); + layoutData->addWidget(labelOffset, 4, 0); + layoutData->addWidget(m_labelOffset, 4, 1); + layoutData->addWidget(labelSectionSize, 5, 0); + layoutData->addWidget(m_labelSectionSize, 5, 1); + layoutData->addWidget(labelLink, 6, 0); + layoutData->addWidget(m_labelLink, 6, 1); + layoutData->addWidget(labelInfo, 7, 0); + layoutData->addWidget(m_labelInfo, 7, 1); + layoutData->addWidget(labelAddressAlign, 8, 0); + layoutData->addWidget(m_labelAddressAlign, 8, 1); + + QHBoxLayout *layoutButton = new QHBoxLayout; + layoutButton->setAlignment(Qt::AlignRight); + QPushButton *buttonClose = new QPushButton("Close"); + connect(buttonClose, SIGNAL(clicked()), this, SLOT(close())); + + layoutButton->addWidget(buttonClose); + + mainLayout->addLayout(layoutData); + mainLayout->addLayout(layoutButton); + + setLayout(mainLayout); +} +void DialogElfSection::setData(struct elfSection *sElfS){ + m_labelSectionName->setText(sElfS->sh_name); + m_labelType->setText(printHex(sElfS->sh_type.toHex())); + m_labelFlags->setText(sElfS->sh_flags.toHex()); + m_labelAddress->setText(printHex(sElfS->sh_addr.toHex())); + m_labelOffset->setText(printHex(sElfS->sh_offset.toHex())); + m_labelSectionSize->setText(printHex(sElfS->sh_size.toHex())); + m_labelLink->setText(printHex(sElfS->sh_link.toHex())); + m_labelInfo->setText(printHex(sElfS->sh_info.toHex())); + m_labelAddressAlign->setText(printHex(sElfS->sh_addralign.toHex())); + m_labelEntrySize->setText(printHex(sElfS->sh_entsize.toHex())); +} +DialogElfSection::~DialogElfSection() { + delete m_labelSectionName; + delete m_labelType; + delete m_labelFlags; + delete m_labelAddress; + delete m_labelOffset; + delete m_labelSectionSize; + delete m_labelLink; + delete m_labelInfo; + delete m_labelAddressAlign; + delete m_labelEntrySize; +} diff --git a/RElicAnalysis/elf/dialogelfsection.h b/RElicAnalysis/elf/dialogelfsection.h new file mode 100644 index 0000000..49336c3 --- /dev/null +++ b/RElicAnalysis/elf/dialogelfsection.h @@ -0,0 +1,33 @@ +#ifndef DIALOGELFSECTION_H +#define DIALOGELFSECTION_H + +#include +#include +#include +#include +#include +#include +#include "../data.h" +#include "../functions.h" + + +class DialogElfSection: public QDialog { + public: + DialogElfSection(); + ~DialogElfSection(); + void setData(struct elfSection *); + + private: + QLabel *m_labelSectionName; + QLabel *m_labelType; + QLabel *m_labelFlags; + QLabel *m_labelAddress; + QLabel *m_labelOffset; + QLabel *m_labelSectionSize; + QLabel *m_labelLink; + QLabel *m_labelInfo; + QLabel *m_labelAddressAlign; + QLabel *m_labelEntrySize; +}; + +#endif // DIALOGELFSECTION_H diff --git a/RElicAnalysis/elf/dialogelfsymbol.cpp b/RElicAnalysis/elf/dialogelfsymbol.cpp new file mode 100644 index 0000000..100f3a2 --- /dev/null +++ b/RElicAnalysis/elf/dialogelfsymbol.cpp @@ -0,0 +1,30 @@ +#include "dialogelfsymbol.h" + +DialogElfSymbol::DialogElfSymbol(): QDialog() { + setWindowTitle("ELF Section"); + setFixedSize(350, 250); + + QVBoxLayout *mainLayout = new QVBoxLayout; + + QGridLayout *layoutData = new QGridLayout; + + + + QHBoxLayout *layoutButton = new QHBoxLayout; + layoutButton->setAlignment(Qt::AlignRight); + QPushButton *buttonClose = new QPushButton("Close"); + connect(buttonClose, SIGNAL(clicked()), this, SLOT(close())); + + layoutButton->addWidget(buttonClose); + + mainLayout->addLayout(layoutData); + mainLayout->addLayout(layoutButton); + + setLayout(mainLayout); +} +void DialogElfSymbol::setData(struct elfSymbol *sElfS) { + +} +DialogElfSymbol::~DialogElfSymbol() { + +} diff --git a/RElicAnalysis/elf/dialogelfsymbol.h b/RElicAnalysis/elf/dialogelfsymbol.h new file mode 100644 index 0000000..e6e429d --- /dev/null +++ b/RElicAnalysis/elf/dialogelfsymbol.h @@ -0,0 +1,21 @@ +#ifndef DIALOGELFSYMBOL_H +#define DIALOGELFSYMBOL_H + +#include +#include +#include +#include +#include +#include +#include "../data.h" +#include "../functions.h" + + +class DialogElfSymbol: public QDialog { + public: + DialogElfSymbol(); + ~DialogElfSymbol(); + void setData(struct elfSymbol *); +}; + +#endif // DIALOGELFSYMBOL_H diff --git a/RElicAnalysis/elf/mdielf.cpp b/RElicAnalysis/elf/mdielf.cpp new file mode 100644 index 0000000..71c51dc --- /dev/null +++ b/RElicAnalysis/elf/mdielf.cpp @@ -0,0 +1,506 @@ +#include "mdielf.h" +#include "../stylesheet.h" + + +MdiElf::MdiElf(ProjectElf *projectElf): QMdiSubWindow() { + m_projectElf = projectElf; + m_elfData = projectElf->getElfData(); + + m_modelWidgetElf = new ModelWidgetELF; + m_dialogElfIdent = new DialogElfIdent(m_elfData); + m_dialogElfHeader = new DialogElfHeader(m_elfData); + m_dialogElfProgram = new DialogElfProgram; + m_dialogElfSection = new DialogElfSection; + m_dialogElfSymbol = new DialogElfSymbol; + + setWindowTitle("Hex dump"); + resize(this->width(), this->height()); + setWindowFlags(Qt::FramelessWindowHint); + + QWidget *mainWidget = new QWidget; + + QHBoxLayout *mainLayout = new QHBoxLayout; + QVBoxLayout *layoutDockLeft = new QVBoxLayout; + QVBoxLayout *layoutDockRight = new QVBoxLayout; + QHBoxLayout *layoutTextEdit = new QHBoxLayout; + QVBoxLayout *layoutCentral = new QVBoxLayout; + mainLayout->setContentsMargins(QMargins(0,0,0,0)); + layoutDockLeft->setContentsMargins(QMargins(0,0,0,0)); + layoutDockRight->setContentsMargins(QMargins(0,0,0,0)); + layoutTextEdit->setContentsMargins(QMargins(0,0,0,0)); + layoutCentral->setContentsMargins(QMargins(0,0,0,0)); + mainLayout->setSpacing(0); + + /**************************/ + /***** Dock left part *****/ + /**************************/ + // Widget for exploring + QWidget *barExplorer = new QWidget; + barExplorer->setStyleSheet(styleBar); + barExplorer->setMaximumHeight(20); + barExplorer->resize(100, barExplorer->height()); + barExplorer->setMaximumWidth(300); + QHBoxLayout *layoutBarExplorer = new QHBoxLayout; + layoutBarExplorer->setAlignment(Qt::AlignLeft); + layoutBarExplorer->setContentsMargins(QMargins(0,0,0,0)); + layoutBarExplorer->setSpacing(0); + + m_labelFilename = new QLabel; + m_labelFilename->setStyleSheet("QLabel{margin:0px;}QLabel{color:#fff;}"); + m_labelFilename->setFont(fontBar); + m_labelFilename->setFixedSize(250,20); + layoutBarExplorer->addWidget(m_labelFilename); + barExplorer->setLayout(layoutBarExplorer); + + // Tree view + m_treeExplorer = new QTreeWidget; + m_treeExplorer->setMaximumWidth(250); + m_treeExplorer->header()->hide(); + m_treeExplorer->setColumnCount(1); + m_treeExplorer->setStyleSheet("border:none; border-left:1 px solid #000;"); + connect(m_treeExplorer, SIGNAL(itemDoubleClicked(QTreeWidgetItem*, int)), this, SLOT(_itemClickedExplorer(QTreeWidgetItem*, int))); + + layoutDockLeft->addWidget(barExplorer); + layoutDockLeft->addWidget(m_treeExplorer); + + // Widget for caracteristics + QWidget *barCaracteristics = new QWidget; + barCaracteristics->setStyleSheet(styleBar); + barCaracteristics->setMaximumHeight(20); + barCaracteristics->resize(100, 300); + barCaracteristics->setMaximumWidth(300); + QHBoxLayout *layoutBarCaracteristics = new QHBoxLayout; + layoutBarCaracteristics->setAlignment(Qt::AlignLeft); + layoutBarCaracteristics->setContentsMargins(QMargins(0,0,0,0)); + layoutBarCaracteristics->setSpacing(0); + + QLabel *labelCaracteristics = new QLabel("Caracteristics"); + labelCaracteristics->setStyleSheet(labelBar); + labelCaracteristics->setFont(fontBar); + labelCaracteristics->setFixedSize(250,20); + layoutBarCaracteristics->addWidget(labelCaracteristics); + barCaracteristics->setLayout(layoutBarCaracteristics); + + m_textEditCaracteristics = new QTextEdit; + m_textEditCaracteristics->setMaximumWidth(250); + m_textEditCaracteristics->setReadOnly(true); + m_textEditCaracteristics->setStyleSheet("border:none; border-left:1 px solid #000;"); + + layoutDockLeft->addWidget(barCaracteristics); + layoutDockLeft->addWidget(m_textEditCaracteristics); + + // Widget for functions + QWidget *barFunctions = new QWidget; + barFunctions->setStyleSheet(styleBar); + barFunctions->setMaximumHeight(20); + barFunctions->resize(100, 300); + barFunctions->setMaximumWidth(300); + QHBoxLayout *layoutBarFunctions = new QHBoxLayout; + layoutBarFunctions->setAlignment(Qt::AlignLeft); + layoutBarFunctions->setContentsMargins(QMargins(0,0,0,0)); + layoutBarFunctions->setSpacing(0); + + QLabel *labelFunctions = new QLabel("Functions"); + labelFunctions->setStyleSheet(labelBar); + labelFunctions->setFont(fontBar); + labelFunctions->setFixedSize(250,20); + layoutBarFunctions->addWidget(labelFunctions); + barFunctions->setLayout(layoutBarFunctions); + + // Tree view + m_treeFunctions = new QTreeWidget; + m_treeFunctions->setMaximumWidth(250); + m_treeFunctions->header()->hide(); + m_treeFunctions->setColumnCount(1); + m_treeFunctions->setStyleSheet("border:none; border-left:1 px solid #000;"); + connect(m_treeFunctions, SIGNAL(itemDoubleClicked(QTreeWidgetItem*, int)), this, SLOT(_itemClickedFunctions(QTreeWidgetItem*, int))); + + layoutDockLeft->addWidget(barFunctions); + layoutDockLeft->addWidget(m_treeFunctions); + + /**************************/ + /***** Text edit part *****/ + /**************************/ + /* Hex part */ + QVBoxLayout *layoutHex = new QVBoxLayout; + layoutHex->setContentsMargins(QMargins(0,0,0,0)); + QHBoxLayout *layoutBarHex = new QHBoxLayout; + layoutBarHex->setAlignment(Qt::AlignLeft); + layoutBarHex->setContentsMargins(QMargins(0,0,0,0)); + layoutBarHex->setSpacing(0); + QLabel *labelTitleHex = new QLabel("Hex dump"); + labelTitleHex->setStyleSheet(labelBar); + labelTitleHex->setFont(fontBar); + labelTitleHex->setFixedSize(250, 20); + layoutBarHex->addWidget(labelTitleHex); + + QWidget *barHex = new QWidget; + barHex->setMinimumHeight(20); + barHex->setStyleSheet("border-bottom:1px solid #000;background-color:#656565;"); + barHex->setLayout(layoutBarHex); + + m_treeHexDump = new QTreeWidget; + m_treeHexDump->header()->hide(); + m_treeHexDump->setColumnCount(1); + m_treeHexDump->setStyleSheet("border:none; border-left:1 px solid #000;"); + + layoutHex->addWidget(barHex); + layoutHex->addWidget(m_treeHexDump); + layoutHex->setContentsMargins(QMargins(0,0,0,0)); + + layoutTextEdit->addLayout(layoutHex); + + /***************************/ + /***** Dock Right part *****/ + /***************************/ + // Globa objects + QWidget *barGlobalObjects = new QWidget; + barGlobalObjects->setStyleSheet(styleBar); + barGlobalObjects->setMaximumHeight(20); + barGlobalObjects->resize(100, barExplorer->height()); + barGlobalObjects->setMaximumWidth(300); + QHBoxLayout *layoutGlobalVariables = new QHBoxLayout; + layoutGlobalVariables->setAlignment(Qt::AlignLeft); + layoutGlobalVariables->setContentsMargins(QMargins(0,0,0,0)); + layoutGlobalVariables->setSpacing(0); + + QLabel *labelglobalObjects = new QLabel("Global objects"); + labelglobalObjects->setStyleSheet("QLabel{margin:0px;}QLabel{color:#fff;}"); + labelglobalObjects->setFont(fontBar); + labelglobalObjects->setFixedSize(250,20); + + layoutGlobalVariables->addWidget(labelglobalObjects); + barGlobalObjects->setLayout(layoutGlobalVariables); + + m_treeGlobalObjects = new QTreeWidget; + m_treeGlobalObjects->setMaximumWidth(250); + m_treeGlobalObjects->header()->hide(); + m_treeGlobalObjects->setColumnCount(1); + m_treeGlobalObjects->setStyleSheet("border:none; border-left:1 px solid #000;"); + + layoutDockRight->addWidget(barGlobalObjects); + layoutDockRight->addWidget(m_treeGlobalObjects); + + // Dyn symbols + QWidget *barDynSymbols = new QWidget; + barDynSymbols->setStyleSheet(styleBar); + barDynSymbols->setMaximumHeight(20); + barDynSymbols->resize(100, barExplorer->height()); + barDynSymbols->setMaximumWidth(300); + QHBoxLayout *layoutDynSymbols = new QHBoxLayout; + layoutDynSymbols->setAlignment(Qt::AlignLeft); + layoutDynSymbols->setContentsMargins(QMargins(0,0,0,0)); + layoutDynSymbols->setSpacing(0); + + QLabel *labelDynSymbols = new QLabel("Dyn symbols"); + labelDynSymbols->setStyleSheet("QLabel{margin:0px;}QLabel{color:#fff;}"); + labelDynSymbols->setFont(fontBar); + labelDynSymbols->setFixedSize(250,20); + + layoutDynSymbols->addWidget(labelDynSymbols); + barDynSymbols->setLayout(layoutDynSymbols); + + m_treeDynSymbols = new QTreeWidget; + m_treeDynSymbols->setMaximumWidth(250); + m_treeDynSymbols->header()->hide(); + m_treeDynSymbols->setColumnCount(1); + m_treeDynSymbols->setStyleSheet("border:none; border-left:1 px solid #000;"); + + layoutDockRight->addWidget(barDynSymbols); + layoutDockRight->addWidget(m_treeDynSymbols); + + // Hash table + QWidget *barHashTable = new QWidget; + barHashTable->setStyleSheet(styleBar); + barHashTable->setMaximumHeight(20); + barHashTable->resize(100, barExplorer->height()); + barHashTable->setMaximumWidth(300); + QHBoxLayout *layoutHashTable = new QHBoxLayout; + layoutHashTable->setAlignment(Qt::AlignLeft); + layoutHashTable->setContentsMargins(QMargins(0,0,0,0)); + layoutHashTable->setSpacing(0); + + QLabel *labelHashTable = new QLabel("Hash table"); + labelHashTable->setStyleSheet("QLabel{margin:0px;}QLabel{color:#fff;}"); + labelHashTable->setFont(fontBar); + labelHashTable->setFixedSize(250,20); + + layoutHashTable->addWidget(labelHashTable); + barHashTable->setLayout(layoutHashTable); + + m_treeHashTable = new QTreeWidget; + m_treeHashTable->setMaximumWidth(250); + m_treeHashTable->header()->hide(); + m_treeHashTable->setColumnCount(1); + m_treeHashTable->setStyleSheet("border:none; border-left:1 px solid #000;"); + + layoutDockRight->addWidget(barHashTable); + layoutDockRight->addWidget(m_treeHashTable); + + layoutCentral->addLayout(layoutTextEdit); + + mainLayout->addLayout(layoutDockLeft); + mainLayout->addLayout(layoutCentral); + mainLayout->addLayout(layoutDockRight); + mainWidget->setLayout(mainLayout); + setWidget(mainWidget); +} +/* + * In this function, we show the result of the ELF object which has been parsed + * These data are stored in the object ElfData + * This function is called from the MdiElfHandler::showMdiSubWindow() + */ +void MdiElf::addBinaryCodeElf(){ + m_labelFilename->setText(m_projectElf->getProjectName()); + + _addExplorerElf(m_elfData); + + _addCaracteristics(m_elfData); + + _addHexDumpElf(m_elfData); +} +void MdiElf::_addCaracteristics(ElfData *elfData) { + FileInfo *fileInfo = m_projectElf->getFileInfo(); + struct elfIdent *sElfId = elfData->getIdentification(); + struct elfHdr *sElfHdr = elfData->getHeader(); + + QString s = "Filename: " + fileInfo->getFilename() + "\n"; + s += "ELF architecture: " + m_elfData->mapArchitecture(hexToInt(sElfId->arch.toHex())) + "\n"; + s += "ELF Header size: " + QString::number(hexToInt(sElfHdr->e_ehsize.toHex())) + "\n"; + s += "ELF number programs: " + QString::number(hexToInt(sElfHdr->e_phnum.toHex())) + "\n"; + s += "ELF programs offset: " + QString::number(hexToInt(sElfHdr->e_phoff.toHex())) + "\n"; + s += "ELF number sections: " + QString::number(hexToInt(sElfHdr->e_shnum.toHex())) + "\n"; + s += "ELF sections offset: " + QString::number(hexToInt(sElfHdr->e_shoff.toHex())) + "\n"; + s += "String table: " + QString::number(hexToInt(sElfHdr->e_shstrndx.toHex())) + "\n"; + + m_textEditCaracteristics->setPlainText(s); +} +void MdiElf::_addExplorerElf(ElfData *elfData){ + QTreeWidgetItem *itemId = new QTreeWidgetItem(m_treeExplorer); + itemId->setText(0, "Identification"); + QTreeWidgetItem *itemHdr = new QTreeWidgetItem(m_treeExplorer); + itemHdr->setText(0, "ELF Header"); + QTreeWidgetItem *itemProg = new QTreeWidgetItem(m_treeExplorer); + itemProg->setText(0, "ELF Program headers"); + QTreeWidgetItem *itemSection = new QTreeWidgetItem(m_treeExplorer); + itemSection->setText(0, "ELF Section headers"); + + // Programs part + QList *sElfProg = elfData->getProgramHeaders(); + for (qsizetype i = 0; i < sElfProg->size(); i++){ + QTreeWidgetItem *itemChild = new QTreeWidgetItem; + struct elfProgram *sElfP = sElfProg->at(i); + bool ok; + QString text = elfData->mapProgramType(sElfP->p_type.toHex().toInt(&ok, 16)); + itemChild->setText(0, text); + itemProg->addChild(itemChild); + m_modelWidgetElf->addWidgetItem(itemChild, sElfP); + } + + // Sections part + QList *sElfSection = elfData->getSectionHeaders(); + for (qsizetype i = 0; i < sElfSection->size(); i++){ + QTreeWidgetItem *itemChild = new QTreeWidgetItem; + struct elfSection *sElfS = sElfSection->at(i); + + if (sElfS->sh_name.isEmpty()) + itemChild->setText(0, "NULL"); + else + itemChild->setText(0, sElfS->sh_name); + itemSection->addChild(itemChild); + + /* Add to the model ElfWidget, the item can be clicked and get more information regarding the section */ + m_modelWidgetElf->addWidgetItem(itemChild, sElfS); + } + + // Symbols part + QList *sElfSymbols = elfData->getSymbols(); + for (qsizetype i = 0; i < sElfSymbols->size(); i++){ + struct elfSymbol *elfS = sElfSymbols->at(i); + // st_info type-> 1 = objects + // st_info type -> 2 = function + // st_info type -> 4 = file objects + /* + * We can use macro these macros: + * ELF64_ST_BIND(elfS[i].st_info) + * ELF64_ST_TYPE(elfS[i].st_info); + * + * bind values: + * 0 -> local + * 1 -> global + * 2 -> weak + */ + unsigned char bind = (unsigned char)(elfS->st_info >> 4); + int type = (elfS->st_info) & 0xf; + + // Add functions to the TreeWidget + if (type == 2) { + QTreeWidgetItem *itemChild = new QTreeWidgetItem(m_treeFunctions); + itemChild->setText(0, elfS->st_name); + + // Should I add to a model ? We can get more information regarding the objects, + // such as the address + //m_modelWidgetElf->addWidgetItem(itemChild, elfS); + } + // Add global objects and file objects + if (type == 0 || type == 4 || type == 1) { + QTreeWidgetItem *itemChild = new QTreeWidgetItem(m_treeGlobalObjects); + //qDebug() << elfS->st_name << " " << printHex(elfS->st_value); + itemChild->setText(0, elfS->st_name); + + //m_modelWidgetElf->addWidgetItem(itemChild, elfS); + } + } + + // Dyn symbols + QList *sElfDynSymbols = elfData->getDynSymbols(); + for (qsizetype i = 0; i < sElfDynSymbols->size(); i++){ + struct elfSymbol *elfS = sElfDynSymbols->at(i); + QTreeWidgetItem *itemChild = new QTreeWidgetItem(m_treeDynSymbols); + itemChild->setText(0, elfS->st_name); + } +} +void MdiElf::_addHexDumpElf(ElfData *elfData){ + struct elfIdent *sElfIdent = elfData->getIdentification(); + struct elfHdr *sElfHdr = elfData->getHeader(); + QList *sElfProg = elfData->getProgramHeaders(); + QList *sElfSec = elfData->getSectionHeaders(); + + // Identification part + QString strId = sElfIdent->signature[0].toHex() + sElfIdent->signature[1].toHex() + sElfIdent->signature[2].toHex() + sElfIdent->signature[3].toHex(); + strId += sElfIdent->arch.toHex() + sElfIdent->endianess.toHex(); + strId += sElfIdent->version.toHex() + sElfIdent->abi.toHex() + sElfIdent->vers_abi.toHex(); + strId = printPrettyHex(strId); + + QTreeWidgetItem *itemId = new QTreeWidgetItem(m_treeHexDump); + itemId->setText(0, "Identification"); + QTreeWidgetItem *itemIdData = new QTreeWidgetItem; + itemIdData->setText(0, strId); + itemId->addChild(itemIdData); + + // ELF Header part + QString strHdr = sElfHdr->e_type.toHex() + sElfHdr->e_machine.toHex() + sElfHdr->e_version.toHex(); + strHdr += sElfHdr->e_entry.toHex() + sElfHdr->e_phoff.toHex() + sElfHdr->e_shoff.toHex(); + strHdr += sElfHdr->e_flags.toHex() + sElfHdr->e_ehsize.toHex() + sElfHdr->e_phentsize.toHex(); + strHdr += sElfHdr->e_phnum.toHex() + sElfHdr->e_shentsize.toHex() + sElfHdr->e_shnum.toHex(); + strHdr += sElfHdr->e_shstrndx.toHex(); + strHdr = printPrettyHex(strHdr); + + QTreeWidgetItem *itemElfHdr = new QTreeWidgetItem(m_treeHexDump); + itemElfHdr->setText(0, "ELF Header"); + QTreeWidgetItem *itemElfHdrData = new QTreeWidgetItem; + itemElfHdrData->setText(0, strHdr); + itemElfHdr->addChild(itemElfHdrData); + + // Elf program part + QTreeWidgetItem *itemProgram = new QTreeWidgetItem(m_treeHexDump); + itemProgram->setText(0, "ELF Programs header"); + for (qsizetype i = 0; i < sElfProg->size(); i++){ + struct elfProgram *sElfP = sElfProg->at(i); + QString strProg = sElfP->p_type.toHex() + sElfP->p_offset.toHex() + sElfP->p_vaddr.toHex(); + strProg += sElfP->p_paddr.toHex() + sElfP->p_filesz.toHex() + sElfP->p_memsz.toHex(); + strProg += sElfP->p_flags.toHex() + sElfP->p_align.toHex(); + strProg = printPrettyHex(strProg); + + QTreeWidgetItem *itemChild = new QTreeWidgetItem; + itemChild->setText(0, strProg); + itemProgram->addChild(itemChild); + } + + // Elf section part + QTreeWidgetItem *itemSection = new QTreeWidgetItem(m_treeHexDump); + itemSection->setText(0, "ELF Sections header"); + for (qsizetype i = 0; i < sElfSec->size(); i++){ + struct elfSection *sElfS = sElfSec->at(i); + + /*QString strSec = sElfS->sh_type.toHex() + " " + sElfS->sh_flags.toHex() + " "; + strSec += sElfS->sh_addr.toHex() + " " + sElfS->sh_offset.toHex() + " "; + strSec += sElfS->sh_size.toHex() + " " + sElfS->sh_link.toHex() + " "; + strSec += sElfS->sh_info.toHex() + " " + sElfS->sh_addralign.toHex() + " "; + strSec += sElfS->sh_entsize.toHex(); + strSec = printPrettyHex(strSec);*/ + QString strSec = sElfS->sh_type.toHex(' ') + " " + sElfS->sh_flags.toHex(' ') + " "; + strSec += sElfS->sh_addr.toHex(' ') + " " + sElfS->sh_offset.toHex(' ') + " "; + strSec += sElfS->sh_size.toHex(' ') + " " + sElfS->sh_link.toHex(' ') + " "; + strSec += sElfS->sh_info.toHex(' ') + " " + sElfS->sh_addralign.toHex(' ') + " "; + strSec += sElfS->sh_entsize.toHex(' '); + + QTreeWidgetItem *itemChild = new QTreeWidgetItem; + itemChild->setText(0, strSec); + itemSection->addChild(itemChild); + } +} +void MdiElf::_itemClickedExplorer(QTreeWidgetItem *item, int column){ + /* + * When the user click on the treeExplorer, we retrieve the item clicked + * and show the correct dialog containing information about the item + */ + QModelIndex index = m_treeExplorer->indexFromItem(item, column); + + // If the item has no parent + if (item->parent() == NULL) { + switch (index.row()){ + case 0: // Identification + _showElfIdent(m_elfData->getIdentification()); + break; + case 1: // ELF Header + _showElfHeader(m_elfData->getHeader()); + break; + /*case 2: // Program part + break; + case 3: // Section part + break;*/ + default: + break; + } + } + else { + // Get the parent + QTreeWidgetItem *parent = item->parent(); + QModelIndex indexP = m_treeExplorer->indexFromItem(parent, column); + + switch (indexP.row()){ + case 2: { // Program part + struct elfProgram *elfP = m_modelWidgetElf->findWidgetItemProgram(item); + _showElfProgram(elfP); + break; + } + case 3: { + struct elfSection *elfS = m_modelWidgetElf->findWidgetItemSection(item); + if (elfS != NULL) + _showElfSection(elfS); + break; + } + } + } +} +void MdiElf::_itemClickedFunctions(QTreeWidgetItem *item, int column){ + QModelIndex index = m_treeExplorer->indexFromItem(item, column); + + struct elfSymbol *sElfS = m_modelWidgetElf->findWidgetItemSymbol(item); +} +void MdiElf::_showElfIdent(struct elfIdent *sElfIdent){ + m_dialogElfIdent->exec(); +} +void MdiElf::_showElfHeader(struct elfHdr *sElfHdr){ + m_dialogElfHeader->exec(); +} +void MdiElf::_showElfProgram(struct elfProgram *sElfP){ + m_dialogElfProgram->setData(sElfP); + m_dialogElfProgram->exec(); +} +void MdiElf::_showElfSection(struct elfSection *sElfS){ + m_dialogElfSection->setData(sElfS); + m_dialogElfSection->exec(); +} +MdiElf::~MdiElf() { + delete m_treeFunctions; + delete m_treeExplorer; + delete m_treeGlobalObjects; + delete m_textEditCaracteristics; + delete m_dialogElfIdent; + delete m_dialogElfHeader; + delete m_dialogElfProgram; + delete m_dialogElfSection; + delete m_dialogElfSymbol; +} diff --git a/RElicAnalysis/elf/mdielf.h b/RElicAnalysis/elf/mdielf.h new file mode 100644 index 0000000..0590eab --- /dev/null +++ b/RElicAnalysis/elf/mdielf.h @@ -0,0 +1,65 @@ +#ifndef MDIELF_H +#define MDIELF_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../projectelf.h" +#include "../modelwidgetelf.h" +#include "dialogelfident.h" +#include "dialogelfheader.h" +#include "dialogelfprogram.h" +#include "dialogelfsection.h" +#include "dialogelfsymbol.h" + + +class MdiElf: public QMdiSubWindow { + Q_OBJECT +public: + MdiElf(ProjectElf *); + ~MdiElf(); + void addBinaryCodeElf(); + +private slots: + void _itemClickedExplorer(QTreeWidgetItem *, int); + void _itemClickedFunctions(QTreeWidgetItem *, int); + +private: + + DialogElfIdent *m_dialogElfIdent; + DialogElfHeader *m_dialogElfHeader; + DialogElfProgram *m_dialogElfProgram; + DialogElfSection *m_dialogElfSection; + DialogElfSymbol *m_dialogElfSymbol; + ElfData *m_elfData; + ModelWidgetELF *m_modelWidgetElf; + QLabel *m_labelFilename; + QTreeWidget *m_treeExplorer; + QTreeWidget *m_treeFunctions; + QTreeWidget *m_treeDynSymbols; + QTreeWidget *m_treeGlobalObjects; + QTreeWidget *m_treeHashTable; + QTreeWidget *m_treeHexDump; + QTextEdit *m_textEditCaracteristics; + QTextEdit *m_textAnalyzing; + ProjectElf *m_projectElf; + QListView *m_viewHex; + void _addHexTab(); + void _addExplorerElf(ElfData *); + void _addCaracteristics(ElfData *); + void _addHexDumpElf(ElfData *); + void _showElfIdent(struct elfIdent *); + void _showElfHeader(struct elfHdr *); + void _showElfProgram(struct elfProgram *); + void _showElfSection(struct elfSection *); +}; + +#endif // MDIELF_H diff --git a/RElicAnalysis/elf/mdielfdisassembled.cpp b/RElicAnalysis/elf/mdielfdisassembled.cpp new file mode 100644 index 0000000..645e650 --- /dev/null +++ b/RElicAnalysis/elf/mdielfdisassembled.cpp @@ -0,0 +1,108 @@ +#include "mdielfdisassembled.h" +#include + +MdiElfDisassembled::MdiElfDisassembled(ProjectElf *projectElf): QMdiSubWindow() { + m_projectElf = projectElf; + ElfData *elfData = projectElf->getElfData(); + + setWindowTitle("Disassembly"); + resize(this->width(), this->height()); + setWindowFlags(Qt::FramelessWindowHint); + + QWidget *widget = new QWidget; + + // Main layout + QHBoxLayout *mainLayout = new QHBoxLayout; + + /* + * Explorer part + * Get here all symbols, such as functions + */ + m_modelExplorer = new QStandardItemModel; + + // ListView that contains all symbols + m_viewExplorer = new QListView; + m_viewExplorer->setModel(m_modelExplorer); + m_viewExplorer->setStyleSheet("border: none"); + m_viewExplorer->setMaximumWidth(250); + connect(m_viewExplorer, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(_itemClicked(QModelIndex))); + + // ASM code + m_modelAnalyse = new QStandardItemModel; + QHash *> disasCode = elfData->getDisasCode(); + + for (const QByteArray &key : disasCode.keys()){ + // Add to the explorer + QStandardItem *itemExplorer = new QStandardItem(key); + m_modelExplorer->appendRow(itemExplorer); + + // Add to the QListView + QStandardItem *item = new QStandardItem(key); + QFont font; + font.setItalic(true); + font.setPointSize(12); + item->setFont(font); + m_modelAnalyse->appendRow(item); + + /* + * Add the modelIndex to the list. + * This list is used when the user has cliked the item + */ + m_listModelExplorer.append(m_modelAnalyse->indexFromItem(item)); + + /* Add the disas code to the QListView */ + for (qsizetype i = 0; i < disasCode[key]->size(); ++i){ + //qDebug() << "Disas: " << key << " " << disasCode[key]->at(i)->hexCmd; + QString s = sanitizeASMCode(disasCode[key]->at(i)); + m_modelAnalyse->appendRow(new QStandardItem(s)); + } + } + + // ListView that contains the ASM code + m_viewAnalyse = new QListView; + m_viewAnalyse->setModel(m_modelAnalyse); + m_viewAnalyse->setSelectionMode(QAbstractItemView::NoSelection); + //m_viewAnalyse->setStyleSheet("border: none"); + + mainLayout->addWidget(m_viewExplorer); + mainLayout->addWidget(m_viewAnalyse); + + widget->setLayout(mainLayout); + setWidget(widget); +} +/* + * This function sanitize the struct elfObjectDisas for user friendly + */ +QString MdiElfDisassembled::sanitizeASMCode(struct elfObjectDisas *obj){ + QString hex; + hex.setNum(obj->addr, 16); + QByteArray hexCmd = obj->hexCmd.toHex(' '); + size_t len = hexCmd.size(); + + int maxTab = TABS - len; + char tabs[maxTab + 1]; /* +1 for the \0 */ + memset(tabs, 0, maxTab); + size_t t; + //qDebug() << hexCmd << " " << len << " " << maxTab; + for(t = 0; t < maxTab; t++) + tabs[t] = ' '; + tabs[t] = '\t'; + tabs[t + 1] = '\0'; + return "\t" + hex + "\t" + hexCmd.toUpper() + tabs + obj->cmd; +} +void MdiElfDisassembled::_itemClicked(const QModelIndex &model){ + int row = model.row(); + if (row != -1){ + m_viewAnalyse->clearSelection(); + + QModelIndex model = m_listModelExplorer.at(row); + QItemSelectionModel *selection = m_viewAnalyse->selectionModel(); + selection->setCurrentIndex(model, QItemSelectionModel::Select); + } +} +MdiElfDisassembled::~MdiElfDisassembled(){ + delete m_modelExplorer; + delete m_modelAnalyse; + delete m_viewExplorer; + delete m_viewAnalyse; +} diff --git a/RElicAnalysis/elf/mdielfdisassembled.h b/RElicAnalysis/elf/mdielfdisassembled.h new file mode 100644 index 0000000..f7c7f30 --- /dev/null +++ b/RElicAnalysis/elf/mdielfdisassembled.h @@ -0,0 +1,36 @@ +#ifndef MDIELFDISASSEMBLED_H +#define MDIELFDISASSEMBLED_H + +#include +#include +#include +#include +#include +#include +#include +#include "../projectelf.h" + +/* Used for printing the ASM code */ +#define TABS 60 + +class MdiElfDisassembled: public QMdiSubWindow { + Q_OBJECT +public: + MdiElfDisassembled(ProjectElf *); + ~MdiElfDisassembled(); + +private: + ProjectElf *m_projectElf; + QListView *m_viewExplorer; + QListView *m_viewAnalyse; + QStandardItemModel *m_modelExplorer; + QStandardItemModel *m_modelAnalyse; + QList m_listModelExplorer; + QString sanitizeASMCode(struct elfObjectDisas *); + +private slots: + void _itemClicked(const QModelIndex &); +}; + + +#endif // MDIELFDISASSEMBLED_H diff --git a/RElicAnalysis/elf/mdielfhandler.cpp b/RElicAnalysis/elf/mdielfhandler.cpp new file mode 100644 index 0000000..8a2325c --- /dev/null +++ b/RElicAnalysis/elf/mdielfhandler.cpp @@ -0,0 +1,31 @@ +#include "mdielfhandler.h" +#include "../centralwidget.h" + +/* + * This class handle ELF project and send data to all QWidgets and children + * in the centralWidget + */ + +MdiElfHandler::MdiElfHandler(CentralWidget *centralWidget, ProjectElf *projectElf) { + m_centralWidget = centralWidget; + m_projectElf = projectElf; + m_mdiElf = new MdiElf(projectElf); + m_mdiElfView = new MdiElfView(projectElf); + m_mdiElfDisassembled = new MdiElfDisassembled(projectElf); +} +void MdiElfHandler::showMdiSubWindow(){ + m_centralWidget->addSubWindow(m_mdiElfDisassembled)->show(); + m_centralWidget->addSubWindow(m_mdiElf)->show(); + m_centralWidget->addSubWindow(m_mdiElfView)->show(); + + /* Set the MdiElf as current tab */ + m_centralWidget->setActiveSubWindow(m_mdiElf); + + // Add the result of the parse + m_mdiElf->addBinaryCodeElf(); +} +MdiElfHandler::~MdiElfHandler(){ + delete m_mdiElf; + delete m_mdiElfDisassembled; + delete m_mdiElfView; +} diff --git a/RElicAnalysis/elf/mdielfhandler.h b/RElicAnalysis/elf/mdielfhandler.h new file mode 100644 index 0000000..7b890de --- /dev/null +++ b/RElicAnalysis/elf/mdielfhandler.h @@ -0,0 +1,25 @@ +#ifndef MDIELFHANDLER_H +#define MDIELFHANDLER_H + +#include "mdielf.h" +#include "mdielfdisassembled.h" +#include "mdielfview.h" +#include "../projectelf.h" + + +class CentralWidget; +class MdiElfHandler { + public: + MdiElfHandler(CentralWidget *, ProjectElf *); + ~MdiElfHandler(); + void showMdiSubWindow(); + + private: + CentralWidget *m_centralWidget; + ProjectElf *m_projectElf; + MdiElf *m_mdiElf; + MdiElfDisassembled *m_mdiElfDisassembled; + MdiElfView *m_mdiElfView; +}; + +#endif // MDIELFHANDLER_H diff --git a/RElicAnalysis/elf/mdielfview.cpp b/RElicAnalysis/elf/mdielfview.cpp new file mode 100644 index 0000000..e7f0f46 --- /dev/null +++ b/RElicAnalysis/elf/mdielfview.cpp @@ -0,0 +1,107 @@ +#include "mdielfview.h" +#include "../functions.h" + +MdiElfView::MdiElfView(ProjectElf *projectElf): QMdiSubWindow() { + setWindowTitle("Logical view"); + m_elfData = projectElf->getElfData(); + + scroll = new QScrollArea; + scroll->setWidgetResizable(false); + r = new RenderArea; + r->setElfData(m_elfData); + scroll->setFixedSize(1000, 500); + scroll->setWidget(r); + + + setWidget(scroll); +} +MdiElfView::~MdiElfView(){ + delete r; + delete scroll; +} + +RenderArea::RenderArea(QWidget *parent): QWidget(parent){ + setBackgroundRole(QPalette::Base); + //setAutoFillBackground(true); + //QScrollArea *scroll = new QScrollArea(this); + //QVBoxLayout *layout = new QVBoxLayout; + //layout->addWidget(scroll); + //setLayout(layout); + //setFixedSize(1000, 500); + setMinimumSize(1000, 500); + //m_elfData = elfData; +} +void RenderArea::setElfData(ElfData *elfData){ + m_elfData = elfData; +} +void RenderArea::paintEvent(QPaintEvent *event){ + /* Elf header */ + QPainter painterEHdr(this); + defineRect(&painterEHdr); + painterEHdr.drawRect(50, 0, 300, 50); + + QPainter pEHdrText(this); + pEHdrText.drawText(QPoint(70, 30), "ELF Header"); + + QPainter pEHdrOffset(this); + pEHdrOffset.drawText(QPoint(360, 20), "Offset: 0x0 bytes"); + QString sizeEhdr; + + sizeEhdr = "Size: " + QString::number(m_elfData->getSizeofElf_Ehdr()) + " bytes"; + + QPainter pPHdrSize(this); + pPHdrSize.drawText(QPoint(360, 40), sizeEhdr); + + /* Program header */ + // Now, we loop for all programs + int pos = 50; + int posText = 80; + int posOffsetText = 70; + QList *sElfProg = m_elfData->getProgramHeaders(); + for (qsizetype i = 0; i < sElfProg->size(); i++){ + struct elfProgram *sElfP = sElfProg->at(i); + QString text = m_elfData->mapProgramType(hexToInt(sElfP->p_type.toHex())); + + QPainter painter(this); + defineRect(&painter); + painter.drawRect(50, pos, 300, 50); + + QPainter pProgText(this); + pProgText.drawText(QPoint(70, posText), "Prog Header: " + text); + + QPainter pPHdrOffset(this); + QString offset_hex = sElfP->p_offset.toHex(); + + //int p = getLastPositionNull(offset_hex); + //offset_hex = offset_hex.remove(0, p); + pPHdrOffset.drawText(QPoint(360, posOffsetText), "Offset: 0x" + offset_hex + " bytes"); + + QPainter pPHdrSize(this); + pPHdrSize.drawText(QPoint(360, posOffsetText + 20), "Size: 0x" + sElfP->p_filesz.toHex() + " bytes"); + //qDebug() << text << posText; + pos += 50; + posText += 50; + posOffsetText += 50; + //update(); + } + + /* Section header */ +} +void RenderArea::defineRect(QPainter *painter){ + painter->setRenderHint(QPainter::Antialiasing); + painter->setBrush(Qt::NoBrush); + /*QPen pen; + pen.setColor(QColor(0, 0, 0, 255)); + painterIHdr.setPen(pen);*/ + painter->setPen(Qt::black); +} + +int RenderArea::getLastPositionNull(QString s){ + int p = 0; + for (int i = 0; i < s.size(); i++){ + if (s[i] != '0') + break; + p++; + } + return p; +} diff --git a/RElicAnalysis/elf/mdielfview.h b/RElicAnalysis/elf/mdielfview.h new file mode 100644 index 0000000..bd73f0f --- /dev/null +++ b/RElicAnalysis/elf/mdielfview.h @@ -0,0 +1,48 @@ +#ifndef MDIELFVIEW_H +#define MDIELFVIEW_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../projectelf.h" + + +class RenderArea : public QWidget { + +public: + explicit RenderArea(QWidget * = nullptr); + void setElfData(ElfData *); +protected: + void paintEvent(QPaintEvent *event) override; +private: + void defineRect(QPainter *); + ElfData *m_elfData; + int getLastPositionNull(QString); +}; + + + +class MdiElfView: public QMdiSubWindow { + Q_OBJECT +public: + MdiElfView(ProjectElf *); + ~MdiElfView(); + + +private: + ElfData *m_elfData; + QScrollArea *scroll; + RenderArea *r; +}; + +#endif // MDIELFVIEW_H diff --git a/RElicAnalysis/elfReading_en_GB.ts b/RElicAnalysis/elfReading_en_GB.ts new file mode 100644 index 0000000..76f52a4 --- /dev/null +++ b/RElicAnalysis/elfReading_en_GB.ts @@ -0,0 +1,3 @@ + + + diff --git a/RElicAnalysis/elfdata.cpp b/RElicAnalysis/elfdata.cpp new file mode 100644 index 0000000..ba79346 --- /dev/null +++ b/RElicAnalysis/elfdata.cpp @@ -0,0 +1,351 @@ +#include "elfdata.h" +#include + +ElfData::ElfData() { + m_architecture = 0; + m_sElfId = (struct elfIdent*)malloc(sizeof(struct elfIdent)); + m_sElfHdr = (struct elfHdr*)malloc(sizeof(struct elfHdr)); + m_listProgs = new QList; + m_listSections = new QList; + m_listSymbols = new QList; + m_listTextSymbols = new QList; + m_listHashes = new QList; + m_listDynSymbols = new QList; +} + +void ElfData::addToHash(QByteArray b, struct elfObjectDisas *obj){ + /* + * Here, we add the item in the list + * We create a map with the QList, a map contain an entry which contains a pointer to the QList + */ + if (!m_hash.contains(b)){ + m_hash[b] = new QList; + } + + m_hash[b]->append(obj); +} + +QHash *> ElfData::getDisasCode(){ + return m_hash; +} + +void ElfData::setArchitecture(int a){ + m_architecture = a; +} + +int ElfData::getArchitecture(){ + return m_architecture; +} + +void ElfData::setSizeofElf_Ehdr(size_t s){ + m_sizeofElf_Ehdr = s; +} + +void ElfData::setSizeofElf_Phdr(size_t s){ + m_sizeofElf_Phdr = s; +} + +void ElfData::setSizeofElf_Shdr(size_t s){ + m_sizeofElf_Shdr = s; +} + +size_t ElfData::getSizeofElf_Ehdr(){ + return m_sizeofElf_Ehdr; +} + +size_t ElfData::getSizeofElf_Phdr(){ + return m_sizeofElf_Phdr; +} + +size_t ElfData::getSizeofElf_Shdr(){ + return m_sizeofElf_Shdr; +} + +void ElfData::setIdentification(struct elfIdent *sElfIdent){ + memcpy(m_sElfId, sElfIdent, sizeof(struct elfIdent)); +} +void ElfData::setHeader(struct elfHdr* sElfHdr){ + memcpy(m_sElfHdr, sElfHdr, sizeof(struct elfHdr)); +} +void ElfData::setProgramHeader(struct elfProgram *p){ + m_listProgs->append(p); +} +void ElfData::setSectionHeader(struct elfSection *s){ + m_listSections->append(s); +} +void ElfData::setSymbol(struct elfSymbol * s){ + m_listSymbols->append(s); +} +void ElfData::setDynSymbols(struct elfSymbol *s){ + m_listDynSymbols->append(s); +} +void ElfData::setHash(struct elfHash *h){ + m_listHashes->append(h); +} +void ElfData::setTextSymbol(struct elfSymbol *sElfS){ + m_listTextSymbols->append(sElfS); +} + +struct elfIdent *ElfData::getIdentification(){ + return m_sElfId; +} +struct elfHdr *ElfData::getHeader(){ + return m_sElfHdr; +} +QList *ElfData::getProgramHeaders(){ + return m_listProgs; +} +QList *ElfData::getSectionHeaders(){ + return m_listSections; +} +QList *ElfData::getSymbols(){ + return m_listSymbols; +} +QList *ElfData::getDynSymbols(){ + return m_listDynSymbols; +} +QList *ElfData::getTextSymbols() { + return m_listTextSymbols; +} +QList *ElfData::getHashes(){ + return m_listHashes; +} +QString ElfData::mapProgramType(int type){ + QString text; + switch(type){ + case 0x0: + text = "NULL"; + break; + case 0x1: + text = "LOAD"; + break; + case 0x2: + text = "DYNAMIC"; + break; + case 0x3: + text = "INTERP"; + break; + case 0x4: + text = "NOTE"; + break; + case 0x5: + text = "SHLIB"; + break; + case 0x6: + text = "PHDR"; + break; + case 7: + text = "LOPROC"; + break; + case 8: + text = "GNU_STACK"; + break; + case 0x6474e550: + text = "GNU_EH_FRAME"; + break; + case 0x6474e551: + text = "GNU_STACK"; + break; + case 0x6474e552: + text = "GNU_RELRO"; + break; + case 0x6474e553: + text = "GNU_PROPERTY"; + break; + default: + text = "UNKNOW"; + break; + } + return text; +} +QString ElfData::mapSectionType(int type){ + QString text; + switch(type){ + case 0x0: + text = "SHT_NULL"; + break; + case 0x1: + text = "SHT_PROGBITS"; + break; + case 0x2: + text = "SHT_SYMTAB"; + break; + case 0x3: + text = "SHT_STRTAB"; + break; + case 0x4: + text = "SHT_RELA"; + break; + case 0x5: + text = "SHT_HASH"; + break; + case 0x6: + text = "SHT_DYNAMIC"; + break; + case 0x7: + text = "SHT_NOTE"; + break; + case 0x8: + text = "SHT_NOBITS"; + break; + case 0x9: + text = "SHT_REL"; + break; + case 0xa: + text = "SHT_SHLIB"; + break; + case 0xb: + text = "SHT_DYNSYM"; + break; + case 0xc: + text = "SHT_NUM"; + break; + case 0x70000000: + text = "SHT_LOPROC"; + break; + case 0x7fffffff: + text = "SHT_HIPROC"; + break; + /*case 0x80000000: + text = "SHT_LOUSER"; + break; + case 0xffffffff: + text = "SHT_HIUSER"; + break;*/ + default: + text = "UNKNOW"; + break; + } + return text; +} +QString ElfData::mapArchitecture(int archi) { + QString s = ""; + switch(archi){ + case 0: + s = "NONE"; + break; + case 1: + s = "32 bits"; + break; + case 2: + s = "64 bits"; + break; + } + return s; +} +QString ElfData::mapEndianess(int endianess){ + QString s = ""; + switch (endianess){ + case 0: + s = "NONE"; + break; + case 1: + s = "LSB"; + break; + case 2: + s = "MSB"; + break; + } + return s; +} +QString ElfData::mapABI(int abi){ + QString s = ""; + switch(abi){ + case 0: // ELFOSABI_NONE + s = "UNIX System V"; + break; + case 1: // ELFOSABI_SYSV + s = "UNIX System V"; + break; + case 2: // ELFOSABI_HPUX + s = "HP-UX"; + break; + case 3: // ELFOSABI_NETBSD + s = "NetBSD"; + break; + case 4: // ELFOSABI_LINUX + s = "Linux"; + break; + case 5: // ELFOSABI_SOLARIS + s = "Solaris"; + break; + case 6: // ELFOSABI_IRIX + s = "IRIX"; + break; + case 7: // ELFOSABI_FREEBSD + s = "FreeBSD"; + break; + case 8: // ELFOSABI_TRU64 + s = "TRU64 UNIX"; + break; + case 9: // ELFOSABI_ARM + s = "ARM architecture"; + break; + case 10: // ELFOSABI_STANDALONE + s = "Stand-alone (embedded)"; + break; + } + + return s; +} +QString ElfData::mapTypeElf(int type){ + QString s = ""; + switch(type){ + case 0: // ET_NONE + s= "Unknown"; + break; + case 1: // ET_REL + s = "Relocatable file"; + break; + case 2: // ET_EXEC + s = "Executable file"; + break; + case 3: // ET_DYN + s = "Shared object"; + break; + case 4: // ET_CORE + s = "Core file"; + break; + } + + return s; +} +QString ElfData::mapMachineType(int machine){ + QString s; + + return s; +} +QString ElfData::mapSymbolType(int type) { + QString s; + + return s; +} +ElfData::~ElfData() { + free(m_sElfId); + free(m_sElfHdr); + + //for (qsizetype i = 0; i < m_listProgs->size(); i++) + // free(m_listProgs->at(i)); + //free(m_listProgs); + + // Crash here, why ? + //for (qsizetype i = 0; i < m_listSections->size(); i++) + // free(m_listSections->at(i)); + + for (qsizetype i = 0; i < m_listSymbols->size(); i++) + free(m_listSymbols->at(i)->data); + + // Clean from dynamic variables from parsingelf file + delete m_listProgs; + delete m_listSections; + delete m_listSymbols; + delete m_listTextSymbols; + delete m_listHashes; + delete m_listDynSymbols; + + for (const QByteArray &key : m_hash.keys()){ + for (qsizetype i = 0; i < m_hash[key]->size(); i++) + free(m_hash[key]->at(i)); + delete(m_hash[key]); + } +} diff --git a/RElicAnalysis/elfdata.h b/RElicAnalysis/elfdata.h new file mode 100644 index 0000000..f9aa5fe --- /dev/null +++ b/RElicAnalysis/elfdata.h @@ -0,0 +1,71 @@ +#ifndef ELFDATA_H +#define ELFDATA_H + +#include +#include +#include "data.h" + +enum Architecture{ + ARCH_ELF32, + ARCH_ELF64 +}; + +class ElfData { + public: + ElfData(); + ~ElfData(); + void setArchitecture(int); // Passing enum Architecture + void setIdentification(struct elfIdent *); + void setHeader(struct elfHdr *); + void setProgramHeader(struct elfProgram *); + void setSectionHeader(struct elfSection *); + void setSymbol(struct elfSymbol *); + void setDynSymbols(struct elfSymbol *); + void setHash(struct elfHash *); + void setTextSymbol(struct elfSymbol *); + void setSizeofElf_Ehdr(size_t); + void setSizeofElf_Phdr(size_t); + void setSizeofElf_Shdr(size_t); + + int getArchitecture(); + struct elfIdent *getIdentification(); + struct elfHdr *getHeader(); + QList *getProgramHeaders(); + QList *getSectionHeaders(); + QList *getSymbols(); + QList *getDynSymbols(); + QList *getHashes(); + QList *getTextSymbols(); + size_t getSizeofElf_Ehdr(); + size_t getSizeofElf_Phdr(); + size_t getSizeofElf_Shdr(); + + QString mapProgramType(int); + QString mapSectionType(int); + QString mapArchitecture(int); + QString mapEndianess(int); + QString mapABI(int); + QString mapTypeElf(int); + QString mapMachineType(int); + QString mapSymbolType(int); + + void addToHash(QByteArray, struct elfObjectDisas *); + QHash *> getDisasCode(); + + private: + int m_architecture; + size_t m_sizeofElf_Ehdr; + size_t m_sizeofElf_Phdr; + size_t m_sizeofElf_Shdr; + struct elfIdent *m_sElfId; + struct elfHdr *m_sElfHdr; + QList *m_listProgs; + QList *m_listSections; + QList *m_listSymbols; + QList *m_listDynSymbols; + QList *m_listTextSymbols; + QList *m_listHashes; + QHash *> m_hash; +}; + +#endif // ELFDATA_H diff --git a/RElicAnalysis/fileinfo.cpp b/RElicAnalysis/fileinfo.cpp new file mode 100644 index 0000000..e2d67c3 --- /dev/null +++ b/RElicAnalysis/fileinfo.cpp @@ -0,0 +1,83 @@ +#include "fileinfo.h" +#include +#include +#include + +FileInfo::FileInfo(QFile &file) { + QFileInfo info(file); + struct stat st_stat; + int fd; + int length; + + if (!info.exists()){ + return; + } + + m_sizeFile = info.size(); + m_path = info.absolutePath(); + m_filename = info.fileName(); + QString fullPath = m_path + "/" + m_filename; + m_rawData = NULL; + + /* Get signature of the file */ + //QByteArray b = file.readAll(); + //m_rawData = b.data(); + //m_rawData = (char *)malloc(m_sizeFile); + //qDebug() << "FOOO: " << b.size(); + //memcpy(m_rawData, b.data(), b.size()); + //m_rawData = b.constData(); + //m_rawData = b.data(); + //qDebug() << b[0] << b[1] << b[2] << b[3]; + //QByteArray b1 = b + 1; + //qDebug() << b1[0] << b1[1] << b1[2] << b1[3]; + + + /*QDataStream in(&file); + qint64 len = in.readRawData(m_rawData, m_sizeFile); + qDebug() << len;*/ + + /* + * I need to read the file in the old way, because with Qt functions, my buffer is corrupted + * i do not have the full data + */ + fd = open(fullPath.toLatin1().constData(), O_RDONLY); + if (fd < 0){ + qDebug() << "Failed to read the file"; + } + fstat(fd, &st_stat); + m_rawData = (char *)malloc(st_stat.st_size); + + length = read(fd, m_rawData, st_stat.st_size); + + close(fd); + + this->_getSignature(); +} +size_t FileInfo::getSizeFile(){ + return m_sizeFile; +} +QString FileInfo::getFilename(){ + return m_filename; +} +QString FileInfo::getPath(){ + return m_path; +} +int FileInfo::getFileType(){ + return m_fileType; +} +void FileInfo::_getSignature(){ + // ELF file + /*char signature[4]; + sprintf(signature, "%c%c%c%c", m_rawData[0], m_rawData[1], m_rawData[2], m_rawData[3]); + qDebug() << signature;*/ + if (m_rawData[0] == 0x7f && m_rawData[1] == 0x45 && m_rawData[2] == 0x4c && m_rawData[3] == 0x46) + m_fileType = SIGNATURE_ELF; + else + m_fileType = SIGNATURE_UNKNOWN; +} +char *FileInfo::getRawData(){ + return m_rawData; +} +FileInfo::~FileInfo(){ + free(m_rawData); +} diff --git a/RElicAnalysis/fileinfo.h b/RElicAnalysis/fileinfo.h new file mode 100644 index 0000000..003eeb7 --- /dev/null +++ b/RElicAnalysis/fileinfo.h @@ -0,0 +1,28 @@ +#ifndef FILEINFO_H +#define FILEINFO_H + +#include +#include +#include "data.h" + + +class FileInfo { + public: + FileInfo(QFile &); + ~FileInfo(); + size_t getSizeFile(); + QString getFilename(); + QString getPath(); + int getFileType(); + char *getRawData(); + private: + size_t m_sizeFile; + QString m_filename; + QString m_path; + char *m_rawData; + int m_fileType; + char *m_signature; + void _getSignature(); +}; + +#endif // FILEINFO_H diff --git a/RElicAnalysis/functions.cpp b/RElicAnalysis/functions.cpp new file mode 100644 index 0000000..1af32c0 --- /dev/null +++ b/RElicAnalysis/functions.cpp @@ -0,0 +1,48 @@ +#include "functions.h" + +int hexToInt(QByteArray b){ + bool ok; + int i = b.toInt(&ok, 16); + if (ok) + return i; + else + return 0; +} +QString printHex(QByteArray b){ + /*QString tmp = b; + QString s = "0x"; + for (int i = 0; i < tmp.size(); i++){ + if (i % 2 == 0 && i > 0){ + s += ' '; + } + s += tmp[i]; + } + return s*/ + return "0x" + b; +} +/* + * This function format the hex string into a pretty hex string + */ +QString printPrettyHex(QString s){ + QString str; + for (int i = 0; i < s.size(); i++){ + if (i % 2 == 0 && i > 0){ + str += ' '; + } + str += s[i]; + } + return str; +} + +/* + * This function cut the QByteArray b + */ +QByteArray cutByteArray(QByteArray b){ + int p = 0; + for(int i = 0; i < b.size(); i++){ + if (b[i] != 0) + break; + p++; + } + return b.remove(0, p); +} diff --git a/RElicAnalysis/functions.h b/RElicAnalysis/functions.h new file mode 100644 index 0000000..5e86984 --- /dev/null +++ b/RElicAnalysis/functions.h @@ -0,0 +1,12 @@ +#ifndef FUNCTIONS_H +#define FUNCTIONS_H + +#include +#include + +int hexToInt(QByteArray); +QString printHex(QByteArray); +QString printPrettyHex(QString); +QByteArray cutByteArray(QByteArray); + +#endif // FUNCTIONS_H diff --git a/RElicAnalysis/handleproject.cpp b/RElicAnalysis/handleproject.cpp new file mode 100644 index 0000000..96865c0 --- /dev/null +++ b/RElicAnalysis/handleproject.cpp @@ -0,0 +1,87 @@ +#include "handleproject.h" +#include "mainwindow.h" + +HandleProject::HandleProject(MainWindow *mainWindow) { + m_mainWindow = mainWindow; + m_projectElf = NULL; + m_projectOpened = false; + m_projectType = PROJECT_NONE; + m_fileInfo = NULL; +} + +void HandleProject::handleOpenFile(QFile &file){ + int signFile; + m_fileInfo = new FileInfo(file); + + signFile = m_fileInfo->getFileType(); + + if (signFile == SIGNATURE_ELF){ + m_projectType = PROJECT_ELF; + m_projectOpened = true; + m_projectElf = new ProjectElf; + m_elfData = new ElfData; + + m_projectElf->setFileInfo(m_fileInfo); + m_projectElf->setProjectName(m_fileInfo->getFilename()); + + int res = elfFile(m_fileInfo->getRawData(), m_elfData); + if (res == READ_FILE_ERROR){ + QMessageBox::critical(NULL, "Error", "Failed to read the file"); + m_projectOpened = false; + m_projectType = 0; + delete m_projectElf; + delete m_elfData; + return; + } + else if(res == READ_FILE_32BIT){ + QMessageBox::critical(NULL, "Error", "Cannot read ELF 32 bits"); + m_projectOpened = false; + m_projectType = 0; + delete m_projectElf; + delete m_elfData; + return; + } + m_projectElf->setElfData(m_elfData); + + // Create ELF Class + // And set data to this class + CentralWidget *central = m_mainWindow->getCentralWidget(); + + m_elfHandler = new MdiElfHandler(central, m_projectElf); + + m_elfHandler->showMdiSubWindow(); + } + else if (signFile == SIGNATURE_PE){ + + } + else if (signFile == SIGNATURE_PNG){ + + } + else + QMessageBox::warning(NULL, "Error", "The file specified is not recognized"); +} +void HandleProject::handleCloseFile(){ + switch(m_projectType){ + case PROJECT_ELF: + delete m_projectElf; + delete m_elfData; + delete m_elfHandler; + m_projectOpened = false; + delete m_fileInfo; + break; + } + m_projectType = PROJECT_NONE; +} +bool HandleProject::projectOpened(){ + return m_projectOpened; +} +HandleProject::~HandleProject() { + if (m_projectElf != NULL) + delete m_projectElf; + if (m_fileInfo != NULL) + delete m_fileInfo; + if (m_elfData != NULL) + delete m_elfData; + if (m_elfHandler != NULL) + delete m_elfHandler; +} diff --git a/RElicAnalysis/handleproject.h b/RElicAnalysis/handleproject.h new file mode 100644 index 0000000..508e24c --- /dev/null +++ b/RElicAnalysis/handleproject.h @@ -0,0 +1,41 @@ +#ifndef HANDLEPROJECT_H +#define HANDLEPROJECT_H + +#include +#include +#include "project.h" +#include "projectelf.h" +#include "data.h" +#include "elfdata.h" +#include "fileinfo.h" +#include "parsingelf.h" +#include "elf/mdielfhandler.h" + +/* Create our project types */ +#define PROJECT_NONE 0x0 +#define PROJECT_ELF 0x1 +#define PROJECT_PE 0x2 +#define PROJECT_PNG 0x3 + + +class MainWindow; +class HandleProject { + public: + HandleProject(MainWindow *mainWindow); + ~HandleProject(); + void handleOpenFile(QFile &); + void handleCloseFile(); + bool projectOpened(); + + private: + int m_projectType; + bool m_projectOpened; // Project open or close + MainWindow *m_mainWindow; + Project *m_project; + ProjectElf *m_projectElf; + ElfData *m_elfData; + FileInfo *m_fileInfo; + MdiElfHandler *m_elfHandler; +}; + +#endif // HANDLEPROJECT_H diff --git a/RElicAnalysis/main.cpp b/RElicAnalysis/main.cpp new file mode 100644 index 0000000..fad66e4 --- /dev/null +++ b/RElicAnalysis/main.cpp @@ -0,0 +1,23 @@ +#include "mainwindow.h" + +#include +#include +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + + QTranslator translator; + const QStringList uiLanguages = QLocale::system().uiLanguages(); + for (const QString &locale : uiLanguages) { + const QString baseName = "RElicAnalysis" + QLocale(locale).name(); + if (translator.load(":/i18n/" + baseName)) { + a.installTranslator(&translator); + break; + } + } + MainWindow w; + w.show(); + return a.exec(); +} diff --git a/RElicAnalysis/mainwindow.cpp b/RElicAnalysis/mainwindow.cpp new file mode 100644 index 0000000..e4ad82e --- /dev/null +++ b/RElicAnalysis/mainwindow.cpp @@ -0,0 +1,27 @@ +#include "mainwindow.h" + +MainWindow::MainWindow(): QMainWindow() { + m_project = NULL; + m_handleProject = new HandleProject(this); + m_centralWidget = new CentralWidget(this); + m_menuBar = new MenuBar(m_centralWidget, m_handleProject); + + setWindowTitle("RElicAnalysis"); + setWindowState(Qt::WindowMaximized); + setMinimumSize(1500, 700); + + setCentralWidget(m_centralWidget); + setMenuBar(m_menuBar); +} +void MainWindow::closeCurrentProject(){ + +} +CentralWidget *MainWindow::getCentralWidget(){ + return m_centralWidget; +} +MainWindow::~MainWindow() { + delete m_centralWidget; + delete m_menuBar; + if (m_project != NULL) + delete m_project; +} diff --git a/RElicAnalysis/mainwindow.h b/RElicAnalysis/mainwindow.h new file mode 100644 index 0000000..9a28512 --- /dev/null +++ b/RElicAnalysis/mainwindow.h @@ -0,0 +1,28 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include "menubar.h" +#include "centralwidget.h" +#include "projectelf.h" +#include "project.h" +#include "handleproject.h" + + +class MainWindow : public QMainWindow { + Q_OBJECT + + public: + MainWindow(); + ~MainWindow(); + void closeCurrentProject(); + CentralWidget *getCentralWidget(); + + private: + CentralWidget *m_centralWidget; + MenuBar *m_menuBar; + HandleProject *m_handleProject; + Project *m_project; + ProjectElf *m_projectElf; +}; +#endif // MAINWINDOW_H diff --git a/RElicAnalysis/mainwindow.ui b/RElicAnalysis/mainwindow.ui new file mode 100644 index 0000000..cd4835e --- /dev/null +++ b/RElicAnalysis/mainwindow.ui @@ -0,0 +1,31 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + MainWindow + + + + + + 0 + 0 + 800 + 22 + + + + + + + + diff --git a/RElicAnalysis/menubar.cpp b/RElicAnalysis/menubar.cpp new file mode 100644 index 0000000..709b36d --- /dev/null +++ b/RElicAnalysis/menubar.cpp @@ -0,0 +1,81 @@ +#include "menubar.h" +#include "centralwidget.h" + + +MenuBar::MenuBar(CentralWidget *centralWidget, HandleProject *project) { + m_handleProject = project; + m_centralWidget = centralWidget; + m_dialogAbout = new DialogAbout; + + // Style sheet + setStyleSheet("border-bottom:1px solid #000;"); + + // Create menu + QMenu *menuFile = new QMenu("File"); + QMenu *menuAbout = new QMenu("About"); + + // Menu file + QAction *actionOpenFile = new QAction("Open ELF file", this); + actionOpenFile->setShortcut(QKeySequence("ctrl+O")); + connect(actionOpenFile, SIGNAL(triggered()), this, SLOT(_openFile())); + + QAction *actionCloseFile = new QAction("Fermer le fichier", this); + actionCloseFile->setShortcut(QKeySequence("ctrl+W")); + connect(actionCloseFile, SIGNAL(triggered()), this, SLOT(_closeFile())); + menuFile->addSeparator(); + + QAction *actionQuit = new QAction("Quitter", this); + actionQuit->setShortcut(QKeySequence("ctrl+Q")); + connect(actionQuit, SIGNAL(triggered()), this, SLOT(_quitApp())); + + menuFile->addAction(actionOpenFile); + menuFile->addAction(actionCloseFile); + menuFile->addAction(actionQuit); + + // Menu about + QAction *actionAbout = new QAction("A propos", this); + connect(actionAbout, SIGNAL(triggered()), m_dialogAbout, SLOT(exec())); + menuAbout->addAction(actionAbout); + + // add menu to menuBar + addMenu(menuFile); + addMenu(menuAbout); +} +/* + * This function open the file + */ +void MenuBar::_openFile() { + // Check if we have a project open + // If yes, clean it + if (m_handleProject->projectOpened() == true){ + unsigned int r = QMessageBox::question(NULL, "Open binary file", "A file is open, you should close the file first", QMessageBox::Cancel); + } + else { + QString path = QFileDialog::getOpenFileName(NULL, "Open binary file", "", "File (*)"); + if(!path.isEmpty()) { + QFile file(path); + if(file.open(QIODevice::ReadWrite | QIODevice::Text)){ + m_handleProject->handleOpenFile(file); + } + else if(file.open(QIODevice::ReadOnly)) { + unsigned int r = QMessageBox::question(NULL, "Open binary file", "The file is in read-only. Wish you to open it ?", QMessageBox::Yes | QMessageBox::No); + + if(r == QMessageBox::Yes){ + m_handleProject->handleOpenFile(file); + } + } + file.close(); + } + } +} +void MenuBar::_closeFile(){ + // We can close the project + m_handleProject->handleCloseFile(); +} +void MenuBar::_quitApp(){ + m_handleProject->handleCloseFile(); + qApp->quit(); +} +MenuBar::~MenuBar() { + delete m_dialogAbout; +} diff --git a/RElicAnalysis/menubar.h b/RElicAnalysis/menubar.h new file mode 100644 index 0000000..2028320 --- /dev/null +++ b/RElicAnalysis/menubar.h @@ -0,0 +1,31 @@ +#ifndef MENUBAR_H +#define MENUBAR_H + +#include +#include +#include +#include +#include +#include "handleproject.h" +#include "dialogabout.h" + +class CentralWidget; +class MenuBar : public QMenuBar { + Q_OBJECT + + public : + MenuBar(CentralWidget *, HandleProject *); + ~MenuBar(); + + private slots: + void _openFile(); + void _closeFile(); + void _quitApp(); + + private: + HandleProject *m_handleProject; + CentralWidget *m_centralWidget; + DialogAbout *m_dialogAbout; +}; + +#endif // MENUBAR_H diff --git a/RElicAnalysis/modelwidgetelf.cpp b/RElicAnalysis/modelwidgetelf.cpp new file mode 100644 index 0000000..32f8852 --- /dev/null +++ b/RElicAnalysis/modelwidgetelf.cpp @@ -0,0 +1,49 @@ +#include "modelwidgetelf.h" + +/* + * The model + */ +ModelWidgetELF::ModelWidgetELF() { + +} +void ModelWidgetELF::addWidgetItem(QTreeWidgetItem *item, struct elfProgram *elf){ + m_hashPrograms[item] = elf; +} +void ModelWidgetELF::addWidgetItem(QTreeWidgetItem *item, struct elfSection *elf){ + m_hashSections[item] = elf; +} +void ModelWidgetELF::addWidgetItem(QTreeWidgetItem *item, struct elfSymbol *elf){ + m_hashSymbols[item] = elf; +} +struct elfProgram *ModelWidgetELF::findWidgetItemProgram(QTreeWidgetItem *item){ + struct elfProgram *elfP = NULL; + QHash ::const_iterator hash = m_hashPrograms.find(item); + while (hash != m_hashPrograms.end() && hash.key() == item){ + QTreeWidgetItem *i = hash.key(); + //qDebug() << i->text(0) << " " << m_hashPrograms.value(i); + elfP = m_hashPrograms.value(i); + hash++; + } + return elfP; +} +struct elfSection *ModelWidgetELF::findWidgetItemSection(QTreeWidgetItem *item){ + struct elfSection *elfS = NULL; + QHash ::const_iterator hash = m_hashSections.find(item); + while (hash != m_hashSections.end() && hash.key() == item){ + QTreeWidgetItem *i = hash.key(); + elfS = m_hashSections.value(i); + hash++; + } + return elfS; +} +struct elfSymbol *ModelWidgetELF::findWidgetItemSymbol(QTreeWidgetItem *item){ + struct elfSymbol *elfS = NULL; + QHash ::const_iterator hash = m_hashSymbols.find(item); + while (hash != m_hashSymbols.end() && hash.key() == item){ + QTreeWidgetItem *i = hash.key(); + elfS = m_hashSymbols.value(i); + hash++; + } + return elfS; +} +ModelWidgetELF::~ModelWidgetELF() {} diff --git a/RElicAnalysis/modelwidgetelf.h b/RElicAnalysis/modelwidgetelf.h new file mode 100644 index 0000000..0d291e2 --- /dev/null +++ b/RElicAnalysis/modelwidgetelf.h @@ -0,0 +1,28 @@ +#ifndef MODELWIDGETELF_H +#define MODELWIDGETELF_H + +#include +#include +#include "data.h" + +/* + * This class contains all ElfData and used by QTreeWidgetItem + */ +class ModelWidgetELF { + public: + ModelWidgetELF(); + ~ModelWidgetELF(); + void addWidgetItem(QTreeWidgetItem *, struct elfProgram *); + void addWidgetItem(QTreeWidgetItem *, struct elfSection *); + void addWidgetItem(QTreeWidgetItem *, struct elfSymbol *); + struct elfProgram *findWidgetItemProgram(QTreeWidgetItem *); + struct elfSection *findWidgetItemSection(QTreeWidgetItem *); + struct elfSymbol *findWidgetItemSymbol(QTreeWidgetItem *); + + private: + QHash m_hashPrograms; + QHash m_hashSections; + QHash m_hashSymbols; +}; + +#endif // MODELWIDGETELF_H diff --git a/RElicAnalysis/parsingelf.cpp b/RElicAnalysis/parsingelf.cpp new file mode 100644 index 0000000..2471304 --- /dev/null +++ b/RElicAnalysis/parsingelf.cpp @@ -0,0 +1,442 @@ +#include "parsingelf.h" +#include +#include +#include +#include +#include + + +int elfFile(const char *buffer, ElfData *elfData){ + struct elfIdent *sElfId = (struct elfIdent*)malloc(sizeof(struct elfIdent)); + if (sElfId == NULL){ + qDebug() << "Failed"; + return READ_FILE_ERROR; + } + memset(sElfId, 0, sizeof(struct elfIdent)); + + // Copy to our structure + for(int i = 0; i < 4; i ++) + sElfId->signature[i] = QByteArray(1, buffer[i]); + sElfId->arch = QByteArray(1, buffer[4]); + sElfId->endianess = QByteArray(1, buffer[5]); + sElfId->version = QByteArray(1, buffer[6]); + sElfId->abi = QByteArray(1, buffer[7]); + sElfId->vers_abi = QByteArray(1, buffer[8]); + for(int i = 0; i < 7; i++) + sElfId->padding[i] = QByteArray(1, buffer[i + 9]); + + elfData->setIdentification(sElfId); + free(sElfId); + + /* Parsing Elf Hdr */ + if (buffer[4] == ELFCLASS_64){ + elfData->setArchitecture(ARCH_ELF64); + bool res = elf64(buffer, elfData); + if (res) + return READ_FILE_NO_ERROR; + else + return READ_FILE_ERROR; + } + else if(buffer[4] == ELFCLASS_32){ + elfData->setArchitecture(ARCH_ELF32); + return READ_FILE_32BIT; + } + + return READ_FILE_NO_ERROR; +} +/*void getIdentification(struct elf_ident **sElfIdent, const char *buffer){ + // Shift +4 to avoid the ELF signature + *sElfIdent = (struct elf_ident*)(buffer + 4); +}*/ +bool elf64(const char *buffer, ElfData *elfData){ + //*ehdr = (Elf64_Ehdr*)(buffer); + struct elfHdr *elfHdr = (struct elfHdr*)malloc(sizeof(struct elfHdr)); + size_t lenEhdr = sizeof(Elf64_Ehdr); + size_t lenPhdr = sizeof(Elf64_Phdr); + size_t lenShdr = sizeof(Elf64_Shdr); + size_t offset; + Elf64_Ehdr *ehdr = NULL; + quint16 shstrndx; + bool ok, res; + int phnum, shnum; + + if (elfHdr == NULL) + return false; + + // Set ELF headers size to ElfData + elfData->setSizeofElf_Ehdr(lenEhdr); + elfData->setSizeofElf_Phdr(lenPhdr); + elfData->setSizeofElf_Shdr(lenShdr); + + memset(elfHdr, 0, sizeof(struct elfHdr)); + + ehdr = (Elf64_Ehdr*)(buffer); + + // Get ELF header + elfHdr->e_type = convertFrom16bits(ehdr->e_type); + elfHdr->e_machine = convertFrom16bits(ehdr->e_machine); + elfHdr->e_version = convertFrom32bits(ehdr->e_version); + elfHdr->e_entry = convertFrom64bits(ehdr->e_entry); + elfHdr->e_phoff = convertFrom64bits(ehdr->e_phoff); + elfHdr->e_shoff = convertFrom64bits(ehdr->e_shoff); + elfHdr->e_flags = convertFrom32bits(ehdr->e_flags); + elfHdr->e_ehsize = convertFrom16bits(ehdr->e_ehsize); + elfHdr->e_phentsize = convertFrom16bits(ehdr->e_phentsize); + elfHdr->e_phnum = convertFrom16bits(ehdr->e_phnum); + elfHdr->e_shentsize = convertFrom16bits(ehdr->e_shentsize); + elfHdr->e_shnum = convertFrom16bits(ehdr->e_shnum); + elfHdr->e_shstrndx = convertFrom16bits(ehdr->e_shstrndx); + + // Get the string table index + shstrndx = ehdr->e_shstrndx; + + elfData->setHeader(elfHdr); + free(elfHdr); + + // Get program headers + phnum = elfHdr->e_phnum.toHex().toInt(&ok, 16); + + // Get the first program header offset + offset = elfHdr->e_phoff.toHex().toInt(&ok, 16); + //qDebug() << "Offset hdr:" << offset; + + res = elf64ProgramHdr(buffer, elfData, offset, phnum); + + if (!res) + return false; + + // Get section headers + offset = elfHdr->e_shoff.toHex().toInt(&ok, 16); + //qDebug() << "Offset section: " << offset; + shnum = elfHdr->e_shnum.toHex().toInt(&ok, 16); + + res = elf64SectionHdr(buffer, elfData, offset, shstrndx, shnum); + if (!res) + return false; + + return true; +} +void elf32(int fd, off_t fileLen, Elf32_Ehdr *ehdr){ + +} +bool elf64ProgramHdr(const char *buffer, ElfData *elfData, size_t offset, int phnum){ + Elf64_Phdr *phdr = (Elf64_Phdr*)(buffer + offset); + bool ok; + + // We malloc the variable and we free it in the elfData destructor + struct elfProgram *elfP = (struct elfProgram *)malloc(phnum * sizeof(struct elfProgram)); + if (elfP == NULL) + return false; + + for (int i = 0; i < phnum; i++){ + memset(&elfP[i], 0, sizeof(struct elfProgram)); + + elfP[i].p_type = convertFrom32bits(phdr[i].p_type); + elfP[i].p_flags = convertFrom32bits(phdr[i].p_flags); + elfP[i].p_offset = convertFrom64bits(phdr[i].p_offset); + elfP[i].p_vaddr = convertFrom64bits(phdr[i].p_vaddr); + elfP[i].p_paddr = convertFrom64bits(phdr[i].p_paddr); + elfP[i].p_filesz = convertFrom64bits(phdr[i].p_filesz); + elfP[i].p_memsz = convertFrom64bits(phdr[i].p_memsz); + elfP[i].p_align = convertFrom64bits(phdr[i].p_align); + + + elfData->setProgramHeader(&elfP[i]); + } + return true; +} +void elf32ProgramHdr(int fd, Elf32_Off phoff, off_t phnum){ + +} +bool elf64SectionHdr(const char *buffer, ElfData *elfData, size_t offset, quint16 shstrndx, int shnum){ + size_t lenShdr = sizeof(Elf64_Shdr); + bool res = false; + // We malloc the variable and we free it in the elfData destructor + struct elfSection *elfS = (struct elfSection *)malloc(shnum * sizeof(struct elfSection)); + if (elfS == NULL) + return false; + + Elf64_Shdr *shdr = (Elf64_Shdr*)(buffer + offset); + + // Get the string table header + Elf64_Shdr *strtab = &shdr[shstrndx]; + Elf64_Shdr *shdrtext = NULL; + Elf64_Shdr *shdrfini = NULL; + Elf64_Shdr *shdrhash = NULL; + const char *b = buffer + strtab->sh_offset; + int indexStrTab = 0; + int indexSymbolTab = 0; + int indexDynStrTab = 0; + int indexTextTab = 0; + int indexFiniTab = 0; + int indexHashTab = 0; + int indexDynSymTab = 0; + + for (int i = 0; i < shnum; i++){ + memset(&elfS[i], 0, sizeof(struct elfSection)); + + elfS[i].sh_name = QByteArray(b + shdr[i].sh_name); + //qDebug() << "Section: " << elfS[i].sh_name; + elfS[i].sh_type = convertFrom32bits(shdr[i].sh_type); + elfS[i].sh_flags = convertFrom64bits(shdr[i].sh_flags); + elfS[i].sh_addr = convertFrom64bits(shdr[i].sh_addr); + elfS[i].sh_offset = convertFrom64bits(shdr[i].sh_offset); + elfS[i].sh_size = convertFrom64bits(shdr[i].sh_size); + elfS[i].sh_link = convertFrom32bits(shdr[i].sh_link); + elfS[i].sh_info = convertFrom32bits(shdr[i].sh_info); + elfS[i].sh_addralign = convertFrom32bits(shdr[i].sh_addralign); + elfS[i].sh_entsize = convertFrom32bits(shdr[i].sh_entsize); + qDebug() << elfS[i].sh_name << " " << elfS[i].sh_offset.toHex() << " " << elfS[i].sh_size.toHex(); + elfData->setSectionHeader(&elfS[i]); + + const char *shname = b + shdr[i].sh_name; + + // .symtab + if (shdr[i].sh_type == 0x02 && strcmp(shname, ".symtab") == 0) + indexSymbolTab = i; + + // .strtab for symbal name + if (shdr[i].sh_type == 0x03 && strcmp(shname, ".strtab") == 0) + indexStrTab = i; + + if (shdr[i].sh_type == 0x01 && strcmp(shname, ".text") == 0){ + indexTextTab = i; + } + + if (shdr[i].sh_type == 0x01 && strcmp(shname, ".fini") == 0) + indexFiniTab = i; + + // .data + if (shdr[i].sh_type == 0x01){ } + + // .hash + if (shdr[i].sh_type == 0x05 && strcmp(shname, ".hash") == 0) + indexHashTab = i; + + if (shdr[i].sh_type == 0xb && strcmp(shname, ".dynsym") == 0) + indexDynSymTab = i; + + if (shdr[i].sh_type == 0x3 && strcmp(shname, ".dynstr") == 0) + indexDynStrTab = i; + + // .bss + if (shdr[i].sh_type == 0x08){ + //qDebug() << b + shdr[i].sh_name; + //char *bss = (char *)malloc(shdr[i].sh_size); + //memcpy(bss, buffer + shdr[i].sh_offset, shdr[i].sh_size); + //free(bss); + } + + } + + // Get all symbols in the .text section + shdrtext = &shdr[indexTextTab]; + shdrfini = &shdr[indexFiniTab]; + + /* + * Not needed to use shdrfini->sh_offset for the range of the .text + * We can add shdrtext->sh_offset + shdrtext->sh_size + */ + /* + * We can try for each symbols, to get the section .text, .data, .rodata + * getTextSections, getRodataSections, getDataSections, etc. and push to a QList + */ + + // Get all symbols + res = getSymbols(buffer, elfData, shdr, indexStrTab, indexSymbolTab, shdrtext->sh_offset, shdrfini->sh_offset); + if (!res) + return false; + + // Get dynsym tab + getDynSym(buffer, elfData, shdr, indexDynSymTab, indexDynStrTab, indexHashTab); // Segmentation fault + + // Hash table + shdrhash = &shdr[indexHashTab]; + + return true; +} +bool getDynSym(const char *buffer, ElfData *elfData, Elf64_Shdr *shdr, int indexDynSymTab, int indexDynStrTab, int indexHashTab){ + Elf64_Shdr *shdrdynsym = &shdr[indexDynSymTab]; + Elf64_Shdr *shdrDynStr = &shdr[indexDynStrTab]; + //Elf64_Shdr *shdrhash = &shdr[indexHashTab]; + //size_t dynsymnum = shdrdynsym->sh_size / shdrdynsym->sh_entsize; + + const char *b2 = buffer + shdrDynStr->sh_offset; + Elf64_Sym *symtab = (Elf64_Sym*)(buffer + shdrdynsym->sh_offset); + size_t symtabnum = shdrdynsym->sh_size / shdrdynsym->sh_entsize; + + struct elfSymbol *elfS = (struct elfSymbol*)malloc(symtabnum * sizeof(elfSymbol)); + if (elfS == NULL){ + qDebug() << "Failed to allocate memory"; + return false; + } + + + for (size_t i = 0; i < symtabnum; i++){ + memset(&elfS[i], 0, sizeof(struct elfSymbol)); + + // Get the data block from the buffer with the st_value which indicate the offset and the st_size + elfS[i].st_name = QByteArray(b2 + symtab[i].st_name); + //qDebug() << elfS[i].st_name ; + elfS[i].st_info = symtab[i].st_info; + elfS[i].st_other = symtab[i].st_other; + elfS[i].st_size = convertFrom64bits(symtab[i].st_size); + elfS[i].st_shndx = convertFrom16bits(symtab[i].st_shndx); + elfS[i].st_value = convertFrom64bits(symtab[i].st_value); + + elfData->setDynSymbols(&elfS[i]); + } + return true; +} +bool getSymbols(const char *buffer, ElfData *elfData, Elf64_Shdr *shdr, int indexStrTab, int indexSymbolTab, int offsetText, int offsetFini){ + // Get section string table + Elf64_Shdr *strTabShdr = &shdr[indexStrTab]; + // Get section for symbol table + Elf64_Shdr *symtabShdr = &shdr[indexSymbolTab]; + + const char *b2 = buffer + strTabShdr->sh_offset; + Elf64_Sym *symtab = (Elf64_Sym*)(buffer + shdr[indexSymbolTab].sh_offset); + size_t symtabnum = symtabShdr->sh_size / sizeof(Elf64_Sym); + + struct elfSymbol *elfS = (struct elfSymbol*)malloc(symtabnum * sizeof(elfSymbol)); + if (elfS == NULL){ + qDebug() << "Failed to allocate memory"; + return false; + } + + for (size_t i = 0; i < symtabnum; i++){ + memset(&elfS[i], 0, sizeof(struct elfSymbol)); + + // Get the data block from the buffer with the st_value which indicate the offset and the st_size + elfS[i].st_name = QByteArray(b2 + symtab[i].st_name); + elfS[i].st_info = symtab[i].st_info; + elfS[i].st_other = symtab[i].st_other; + elfS[i].st_size = convertFrom64bits(symtab[i].st_size); + elfS[i].st_shndx = convertFrom16bits(symtab[i].st_shndx); + elfS[i].st_value = convertFrom64bits(symtab[i].st_value); + + //qDebug() << elfS[i].st_name << " " << elfS[i].st_info << " " << symtab[i].st_size; + if (symtab[i].st_size > 0){ + elfS[i].data = (char*)malloc(symtab[i].st_size); + memcpy(elfS[i].data, buffer + symtab[i].st_value, symtab[i].st_size); + elfS[i].size = symtab[i].st_size; + + // We are going to the get Assembly code + struct dataDisas *sData = disas_asm((unsigned char*)elfS[i].data, symtab[i].st_value, elfS[i].size); + + if (sData == NULL){ + qDebug() << "Failed to get the assembly code"; + // I must freeing elfS structures + /*for (size_t i = 0; i < symtabnum; i++) + free(&elfS[i]);*/ + free(elfS); + elfS = NULL; + return false; + } + + /* + * Nous allons decoder les fonctions des symbols + * Pourquoi pas les variables + */ + int type = (elfS[i].st_info) & 0xf; + //qDebug() << elfS[i].st_name << " " << elfS[i].st_info <<" " << type; + + /* + * We will create a new list which will contains a struct elfObjectDisas + * and that structure store all disassembled code + * After, the entry index in struct elfSymbol is in index to the struct elfObjectDisas + */ + elfS[i].countEntries = sData->countEntries; + //QList *list = new QList; + //qDebug() << elfS[i].st_name; + + // Create new entry + for (int j = 0; j < sData->countEntries; j++){ + // Where I freeing the following structure ???? + struct elfObjectDisas *sElfObjectDisas = new struct elfObjectDisas; + + sElfObjectDisas->cmd = QByteArray( + sData->s_object_asm[j].cmd, + strlen(sData->s_object_asm[j].cmd) + ); + + sElfObjectDisas->hexCmd = QByteArray( + sData->s_object_asm[j].hexCmd, + sData->s_object_asm[j].octets + ); + sElfObjectDisas->octets = sData->s_object_asm[j].octets; + sElfObjectDisas->addr = sData->s_object_asm[j].addr; + //qDebug() << sElfObjectDisas->hexCmd; + //list->append(sElfObjectDisas); + if (type == 2){ + elfData->addToHash(elfS[i].st_name, sElfObjectDisas); + } + } + // Store the symbol disassembled code and get the index + //elfS[i].entry = elfData->setSymbolDisassembled(list); + + /* + * We don't need it anymore, we could clean our sData which contains the disas code + */ + free(sData->s_object_asm->hexCmd); + free(sData->s_object_asm); + free(sData); + } + + elfData->setSymbol(&elfS[i]); + //qDebug() << b2 + symtab[i].st_name << " " << symtab[i].st_value << " " << offsetText << " " << offsetFini; + //qDebug() << b2 + symtab[i].st_name << " " << symtab[i].st_value << " " << symtab[i].st_size; + // We check if the symbol is in the .text section + if (symtab[i].st_value >= offsetText && symtab[i].st_value <= offsetFini){ + elfData->setTextSymbol(&elfS[i]); + } + + /* + * We can push the symbols to the correct list + * For instance, if symtab[i].st_name == .rodata, we push to the QList + * Also, we can check with the size of the symbols + * size_t l = symtab[i].st_value + symtab[i].st_size + */ + } + + return true; +} +void getSymbolsText(struct elfSection *sElfS, int indexTextTab, int inexFiniTab){ + +} +bool getHashes(const char *buffer, ElfData *elfData){ + return true; +} +void elf32SectionHdr(int fd, Elf32_Off phoff, off_t shnum){ + +} +QByteArray convertFrom16bits(quint16 value){ + QBuffer buf; + buf.open(QBuffer::ReadWrite); + QDataStream ds(&buf); + ds << value; + QByteArray ba = buf.buffer(); + buf.close(); + return cutByteArray(ba); + //return ba; +} +QByteArray convertFrom32bits(quint32 value){ + QBuffer buf; + buf.open(QBuffer::ReadWrite); + QDataStream ds(&buf); + ds << value; + QByteArray ba = buf.buffer(); + buf.close(); + return cutByteArray(ba); + //return ba; +} +QByteArray convertFrom64bits(quint64 value){ + QBuffer buf; + buf.open(QBuffer::ReadWrite); + QDataStream ds(&buf); + ds << value; + QByteArray ba = buf.buffer(); + buf.close(); + return cutByteArray(ba); + //return ba; +} diff --git a/RElicAnalysis/parsingelf.h b/RElicAnalysis/parsingelf.h new file mode 100644 index 0000000..982ad3f --- /dev/null +++ b/RElicAnalysis/parsingelf.h @@ -0,0 +1,52 @@ +#ifndef PARSINGELF_H +#define PARSINGELF_H + +#include +#include +#include +#include +#include "data.h" +#include "elfdata.h" +#include "disasasm.h" +#include "functions.h" + +/* + * These values are important, they match directly from the buffer ELFCLASSxx + * Cf: https://man7.org/linux/man-pages/man5/elf.5.html + */ +#define ELFCLASS_64 2 /* ELFCLASS64 */ +#define ELFCLASS_32 1 /* ELFCLASS32 */ + +#define READ_FILE_NO_ERROR 0 +#define READ_FILE_ERROR 1 +#define READ_FILE_32BIT 2 + +// https://refspecs.linuxfoundation.org/elf/gabi4+/ch4.eheader.html#elfid +// https://man7.org/linux/man-pages/man5/elf.5.html +// Sizeof 12 bytes +struct elf_ident{ + unsigned char arch; + unsigned char endianess; + unsigned char version; + unsigned char abi; + unsigned char vers_abi; + unsigned char padding[7]; +} __attribute__((packed)); + +int elfFile(const char *, ElfData *); +// /void getIdentification(struct elf_ident **, const char *); +void elf32(int, off_t, Elf32_Ehdr*); +bool elf64(const char *, ElfData *); +bool elf64ProgramHdr(const char *, ElfData *, size_t, int); +void elf32ProgramHdr(int, Elf32_Off, off_t); +bool elf64SectionHdr(const char *, ElfData *, size_t, quint16, int); +void elf32SectionHdr(int, Elf32_Off, off_t); +bool getSymbols(const char *, ElfData *, Elf64_Shdr *, int, int, int, int); +bool getDynSym(const char *, ElfData *, Elf64_Shdr *, int, int, int); +void getSymbolsText(struct elfSection *, int, int); +bool getHashes(const char *, ElfData *); +QByteArray convertFrom16bits(quint16); +QByteArray convertFrom32bits(quint32); +QByteArray convertFrom64bits(quint64); + +#endif // PARSINGELF_H diff --git a/RElicAnalysis/project.cpp b/RElicAnalysis/project.cpp new file mode 100644 index 0000000..ac06910 --- /dev/null +++ b/RElicAnalysis/project.cpp @@ -0,0 +1,21 @@ +#include "project.h" + +Project::Project() {} +void Project::setFileInfo(FileInfo *f){ + m_fileInfo = f; +} +void Project::setProjectName(QString name){ + m_projectName = name; +} +FileInfo *Project::getFileInfo(){ + return m_fileInfo; +} +QString Project::getProjectName(){ + return m_projectName; +} +void Project::setPath(QString p){ + m_projectPath = p; +} +QString Project::getPath(){ + return m_projectPath; +} diff --git a/RElicAnalysis/project.h b/RElicAnalysis/project.h new file mode 100644 index 0000000..49509fe --- /dev/null +++ b/RElicAnalysis/project.h @@ -0,0 +1,29 @@ +#ifndef PROJECT_H +#define PROJECT_H + +#include +#include "fileinfo.h" + +/* + * This class contains information regarding the project + * Such as the filename, the path, and so on + */ + +class Project +{ + public: + Project(); + void setFileInfo(FileInfo *); + void setProjectName(QString); + FileInfo *getFileInfo(); + QString getProjectName(); + void setPath(QString); + QString getPath(); + + private: + FileInfo *m_fileInfo; + QString m_projectName; + QString m_projectPath; +}; + +#endif // PROJECT_H diff --git a/RElicAnalysis/projectelf.cpp b/RElicAnalysis/projectelf.cpp new file mode 100644 index 0000000..8f8a1e4 --- /dev/null +++ b/RElicAnalysis/projectelf.cpp @@ -0,0 +1,10 @@ +#include "projectelf.h" + +ProjectElf::ProjectElf() {} + +void ProjectElf::setElfData(ElfData *elfData){ + m_elfData = elfData; +} +ElfData *ProjectElf::getElfData(){ + return m_elfData; +} diff --git a/RElicAnalysis/projectelf.h b/RElicAnalysis/projectelf.h new file mode 100644 index 0000000..e86dd94 --- /dev/null +++ b/RElicAnalysis/projectelf.h @@ -0,0 +1,20 @@ +#ifndef PROJECTELF_H +#define PROJECTELF_H + +#include "project.h" +#include "elfdata.h" + + +class ProjectElf : public Project { + public: + ProjectElf(); + void setElfData(ElfData *); + ElfData *getElfData(); + + private: + ElfData *m_elfData; +}; + + + +#endif // PROJECTELF_H diff --git a/RElicAnalysis/stylesheet.h b/RElicAnalysis/stylesheet.h new file mode 100644 index 0000000..bc3f75b --- /dev/null +++ b/RElicAnalysis/stylesheet.h @@ -0,0 +1,11 @@ +#ifndef STYLESHEET_H +#define STYLESHEET_H + +#include +#include + +QString styleBar("border-bottom:1px solid #000;background-color:#656565;color:#fff;padding:2px;margin:0px"); +QString labelBar("QLabel{margin:0px;}QLabel{color:#fff;}"); +QFont fontBar("Arial MT", 11); + +#endif // STYLESHEET_H