From ea9123c1376a913d7a035a6a0fd8244b08846f9b Mon Sep 17 00:00:00 2001 From: gbucchino Date: Wed, 29 Apr 2026 13:56:33 +0200 Subject: [PATCH] First commit --- .gitignore | 1 + README.md | 2 + RElicAnalysis-0.1.0 | Bin 0 -> 291880 bytes RElicAnalysis/CMakeLists.txt | 129 ++++++ RElicAnalysis/CMakeLists.txt.user | 417 ++++++++++++++++++ RElicAnalysis/centralwidget.cpp | 49 +++ RElicAnalysis/centralwidget.h | 29 ++ RElicAnalysis/data.h | 110 +++++ RElicAnalysis/dataDisas.h | 22 + RElicAnalysis/dialogabout.cpp | 25 ++ RElicAnalysis/dialogabout.h | 19 + RElicAnalysis/disasasm.cpp | 164 +++++++ RElicAnalysis/disasasm.h | 28 ++ RElicAnalysis/dockanalizing.cpp | 23 + RElicAnalysis/dockanalizing.h | 14 + RElicAnalysis/dockexplorer.cpp | 34 ++ RElicAnalysis/dockexplorer.h | 19 + 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 | 516 +++++++++++++++++++++++ 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 | 36 ++ RElicAnalysis/mainwindow.h | 33 ++ RElicAnalysis/mainwindow.ui | 31 ++ RElicAnalysis/menubar.cpp | 92 ++++ RElicAnalysis/menubar.h | 31 ++ RElicAnalysis/modelwidgetelf.cpp | 49 +++ RElicAnalysis/modelwidgetelf.h | 28 ++ RElicAnalysis/parsingelf.cpp | 422 ++++++++++++++++++ RElicAnalysis/parsingelf.h | 52 +++ RElicAnalysis/project.cpp | 21 + RElicAnalysis/project.h | 29 ++ RElicAnalysis/projectelf.cpp | 10 + RElicAnalysis/projectelf.h | 20 + RElicAnalysis/stylesheet.h | 11 + tests/bfd_file | Bin 0 -> 17872 bytes tests/bfd_file.c | 121 ++++++ tests/test_bfd | Bin 0 -> 16616 bytes tests/test_bfd.c | 100 +++++ tests/test_bfd.c_bck | 119 ++++++ tests/test_buffer.c | 36 ++ tests/test_realloc | Bin 0 -> 16104 bytes tests/test_realloc.c | 22 + 68 files changed, 4733 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/dockanalizing.cpp create mode 100644 RElicAnalysis/dockanalizing.h create mode 100644 RElicAnalysis/dockexplorer.cpp create mode 100644 RElicAnalysis/dockexplorer.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 create mode 100755 tests/bfd_file create mode 100644 tests/bfd_file.c create mode 100755 tests/test_bfd create mode 100644 tests/test_bfd.c create mode 100644 tests/test_bfd.c_bck create mode 100644 tests/test_buffer.c create mode 100755 tests/test_realloc create mode 100644 tests/test_realloc.c 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..689cd9dd765562edcebc8b99f234c1a99065d311 GIT binary patch literal 291880 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

4U_TQ^(SO_Om*#zSDZ_ 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

#!+V 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 LKF{}n?^gXk*&+#Q literal 0 HcmV?d00001 diff --git a/RElicAnalysis/CMakeLists.txt b/RElicAnalysis/CMakeLists.txt new file mode 100644 index 0000000..fb88578 --- /dev/null +++ b/RElicAnalysis/CMakeLists.txt @@ -0,0 +1,129 @@ +cmake_minimum_required(VERSION 3.5) + +project(RElicAnalysis VERSION 0.1 LANGUAGES CXX) + +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets LinguistTools) +find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets LinguistTools) + +#link_directories(/usr/lib/x86_64-linux-gnu/) +find_package (opcodes) +#find_library(libopcodes ONLY_CMAKE_FIND_ROOT_PATH) +#add_library (libopcodes) +#add_library( libopcodes STATIC IMPORTED ) +#add_executable( libopcodes ) +#set_target_properties(libopcodes PROPERTIES +# IMPORTED_LOCATION "/usr/lib/x86_64-linux-gnu/libopcodes.so" +#) +# target_link_libraries( ${BFD_LIBRARY} ) + +set(TS_FILES RElicAnalysis_en_GB.ts) + +set(PROJECT_SOURCES + main.cpp + menubar.h + menubar.cpp + mainwindow.cpp + mainwindow.h +) + +if(${QT_VERSION_MAJOR} GREATER_EQUAL 6) + qt_add_executable(RElicAnalysis + MANUAL_FINALIZATION + ${PROJECT_SOURCES} + + centralwidget.h centralwidget.cpp + parsingelf.h parsingelf.cpp + dockexplorer.h dockexplorer.cpp + dockanalizing.h dockanalizing.cpp + data.h + stylesheet.h + elfdata.h elfdata.cpp + project.h project.cpp + projectelf.h projectelf.cpp + handleproject.h handleproject.cpp + + fileinfo.h fileinfo.cpp + modelwidgetelf.h modelwidgetelf.cpp + + elf/dialogelfident.h elf/dialogelfident.cpp + elf/dialogelfheader.h elf/dialogelfheader.cpp + functions.h + functions.cpp + elf/dialogelfprogram.h elf/dialogelfprogram.cpp + elf/dialogelfsection.h elf/dialogelfsection.cpp + elf/dialogelfsymbol.h elf/dialogelfsymbol.cpp + + disasasm.h disasasm.cpp + dataDisas.h + + elf/mdielfhandler.h elf/mdielfhandler.cpp + elf/mdielf.h elf/mdielf.cpp + + + + elf/mdielfdisassembled.h elf/mdielfdisassembled.cpp + elf/mdielfview.cpp + elf/mdielfview.h + dialogabout.h dialogabout.cpp + + + + + + + ) +# Define target properties for Android with Qt 6 as: +# set_property(TARGET RElicAnalysis APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR +# ${CMAKE_CURRENT_SOURCE_DIR}/android) +# For more information, see https://doc.qt.io/qt-6/qt-add-executable.html#target-creation + + qt_create_translation(QM_FILES ${CMAKE_SOURCE_DIR} ${TS_FILES}) +else() + if(ANDROID) + add_library(RElicAnalysis SHARED + ${PROJECT_SOURCES} + ) +# Define properties for Android with Qt 5 after find_package() calls as: +# set(ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android") + else() + add_executable(RElicAnalysis + ${PROJECT_SOURCES} + ) + endif() + + qt5_create_translation(QM_FILES ${CMAKE_SOURCE_DIR} ${TS_FILES}) +endif() + +target_link_libraries(RElicAnalysis PRIVATE Qt${QT_VERSION_MAJOR}::Widgets opcodes) + +# Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1. +# If you are developing for iOS or macOS you should consider setting an +# explicit, fixed bundle identifier manually though. +if(${QT_VERSION} VERSION_LESS 6.1.0) + set(BUNDLE_ID_OPTION MACOSX_BUNDLE_GUI_IDENTIFIER com.example.RElicAnalysis) +endif() +set_target_properties(RElicAnalysis PROPERTIES + ${BUNDLE_ID_OPTION} + MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION} + MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR} + MACOSX_BUNDLE TRUE + WIN32_EXECUTABLE TRUE +) + +include(GNUInstallDirs) +install(TARGETS RElicAnalysis + BUNDLE DESTINATION . + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} +) + +if(QT_VERSION_MAJOR EQUAL 6) + qt_finalize_executable(RElicAnalysis) +endif() diff --git a/RElicAnalysis/CMakeLists.txt.user b/RElicAnalysis/CMakeLists.txt.user new file mode 100644 index 0000000..2458b35 --- /dev/null +++ b/RElicAnalysis/CMakeLists.txt.user @@ -0,0 +1,417 @@ + + + + + + EnvironmentId + {c1ad1696-344c-4cbf-8c9b-7903590215f2} + + + 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 + 4 + true + + + + true + + + + + ProjectExplorer.Project.Target.0 + + Desktop + Desktop Qt 6.6.2 GCC 64bit + Desktop Qt 6.6.2 GCC 64bit + qt.qt6.662.gcc_64_kit + 0 + 0 + 0 + + Debug + 2 + false + + -DCMAKE_GENERATOR:STRING=Ninja +-DCMAKE_BUILD_TYPE:STRING=Debug +-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake +-DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable} +-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX} +-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C} +-DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx} +-DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG} + 0 + /home/gbucchino/Documents/GIT/RElicAnalysis/build-RElicAnalysis-Desktop_Qt_6_6_2_GCC_64bit-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=Ninja +-DCMAKE_BUILD_TYPE:STRING=Release +-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake +-DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable} +-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX} +-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C} +-DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx} +-DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG} + /home/gbucchino/Documents/GIT/RElicAnalysis/build-RElicAnalysis-Desktop_Qt_6_6_2_GCC_64bit-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=Ninja +-DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo +-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake +-DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable} +-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX} +-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C} +-DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx} +-DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG} + /home/gbucchino/Documents/GIT/RElicAnalysis/build-RElicAnalysis-Desktop_Qt_6_6_2_GCC_64bit-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=Ninja +-DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo +-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake +-DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable} +-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX} +-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C} +-DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx} +-DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG} + 0 + /home/gbucchino/Documents/GIT/RElicAnalysis/build-RElicAnalysis-Desktop_Qt_6_6_2_GCC_64bit-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=Ninja +-DCMAKE_BUILD_TYPE:STRING=MinSizeRel +-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake +-DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable} +-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX} +-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C} +-DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx} +-DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG} + /home/gbucchino/Documents/GIT/RElicAnalysis/build-RElicAnalysis-Desktop_Qt_6_6_2_GCC_64bit-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..c521511 --- /dev/null +++ b/RElicAnalysis/centralwidget.cpp @@ -0,0 +1,49 @@ +#include "centralwidget.h" + + +CentralWidget::CentralWidget(MainWindow *mainWindow): QMdiArea() { + m_mainWindow = mainWindow; + //m_mdiElfHandler = NULL; + 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::addMdiWindowElf(ProjectElf *projectElf){ + // Create MdiELF project + m_mdiElfHandler = new MdiElfHandler(this, projectElf); + + m_mdiElfHandler->showMdiSubWindow(); +}*/ +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(){ + //delete m_mdiElfHandler; +} diff --git a/RElicAnalysis/centralwidget.h b/RElicAnalysis/centralwidget.h new file mode 100644 index 0000000..fd1f8eb --- /dev/null +++ b/RElicAnalysis/centralwidget.h @@ -0,0 +1,29 @@ +#ifndef CENTRALWIDGET_H +#define CENTRALWIDGET_H + +#include +#include +#include +#include +#include "projectelf.h" +#include "elf/mdielfhandler.h" + + +class MainWindow; +class CentralWidget: public QMdiArea { + Q_OBJECT + public: + CentralWidget(MainWindow *); + ~CentralWidget(); + //void addMdiWindowElf(ProjectElf *); + void closeCurrentProject(); + + + private: + MainWindow *m_mainWindow; + //MdiElfHandler *m_mdiElfHandler; + + 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..4698061 --- /dev/null +++ b/RElicAnalysis/dialogabout.cpp @@ -0,0 +1,25 @@ +#include "dialogabout.h" + +DialogAbout::DialogAbout(): QDialog() { + setWindowTitle("About"); + + + + + QHBoxLayout *layoutButtons = new QHBoxLayout; + QPushButton *buttonQuit = new QPushButton("Close"); + connect(buttonQuit, SIGNAL(clicked()), this, SLOT(close())); + layoutButtons->addWidget(buttonQuit); + + setLayout(layoutButtons); + /* QHBoxLayout layoutButtons; + QPushButton buttonQuit; + buttonQuit.setText("Close"); + connect(&buttonQuit, SIGNAL(clicked()), this, SLOT(close())); + layoutButtons.addWidget(&buttonQuit); + setLayout(&layoutButtons);*/ +} +DialogAbout::~DialogAbout(){ + + +} diff --git a/RElicAnalysis/dialogabout.h b/RElicAnalysis/dialogabout.h new file mode 100644 index 0000000..3f2f1b7 --- /dev/null +++ b/RElicAnalysis/dialogabout.h @@ -0,0 +1,19 @@ +#ifndef DIALOGABOUT_H +#define DIALOGABOUT_H + +#include +#include +#include +#include +#include + +class DialogAbout : public QDialog +{ +public: + DialogAbout(); + ~DialogAbout(); + + +}; + +#endif // DIALOGABOUT_H diff --git a/RElicAnalysis/disasasm.cpp b/RElicAnalysis/disasasm.cpp new file mode 100644 index 0000000..28e574b --- /dev/null +++ b/RElicAnalysis/disasasm.cpp @@ -0,0 +1,164 @@ +#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; + + // glpat-hv6rEghasOqdE0hYYGoyLmM6MQpvOjEKdTptZ3g4dQ8.01.170dz3dp9 + /* + * 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(); + /* + * $ ld --version +GNU ld (GNU Binutils for Debian) 2.35.2 +Copyright (C) 2020 Free Software Foundation, Inc. +This program is free software; you may redistribute it under the terms of +the GNU General Public License version 3 or (at your option) a later version. +This program has absolutely no warranty. + */ + /* + * 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, dump_asm_styled); + 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() << "Can't 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/dockanalizing.cpp b/RElicAnalysis/dockanalizing.cpp new file mode 100644 index 0000000..17e5a83 --- /dev/null +++ b/RElicAnalysis/dockanalizing.cpp @@ -0,0 +1,23 @@ +#include "dockanalizing.h" + +DockAnalizing::DockAnalizing(): QDockWidget() { + QWidget *widget = new QWidget; + QVBoxLayout *layout = new QVBoxLayout; + setFeatures(QDockWidget::NoDockWidgetFeatures); + + // Title bar + QWidget *bar = new QWidget; + bar->setMinimumHeight(20); + bar->setStyleSheet("border-bottom:1px solid #000;background-color:#656565;"); + + layout->addWidget(bar); + layout->setContentsMargins(QMargins(0,0,0,0)); // Remove all margins + + setTitleBarWidget(new QWidget); // hide the titlebar + + widget->setLayout(layout); + setWidget(widget); +} +DockAnalizing::~DockAnalizing(){ + +} diff --git a/RElicAnalysis/dockanalizing.h b/RElicAnalysis/dockanalizing.h new file mode 100644 index 0000000..3b1677b --- /dev/null +++ b/RElicAnalysis/dockanalizing.h @@ -0,0 +1,14 @@ +#ifndef DOCKANALIZING_H +#define DOCKANALIZING_H + +#include +#include + + +class DockAnalizing: public QDockWidget { + public: + DockAnalizing(); + ~DockAnalizing(); +}; + +#endif // DOCKANALIZING_H diff --git a/RElicAnalysis/dockexplorer.cpp b/RElicAnalysis/dockexplorer.cpp new file mode 100644 index 0000000..b3d8052 --- /dev/null +++ b/RElicAnalysis/dockexplorer.cpp @@ -0,0 +1,34 @@ +#include "dockexplorer.h" + +DockExplorer::DockExplorer(): QDockWidget() { + QWidget *widget = new QWidget; + QVBoxLayout *layout = new QVBoxLayout; + setFeatures(QDockWidget::NoDockWidgetFeatures); + + // Title bar + QWidget *bar = new QWidget; + bar->setMinimumHeight(20); + bar->setStyleSheet("border-bottom:1px solid #000;background-color:#656565;"); + + // List + m_model = new QStandardItemModel; + + m_view = new QListView; + //connect(m_view, SIGNAL(doubleClicked(const QModelIndex&)), this, SLOT(itemClicked(const QModelIndex&))); + //connect(m_view, SIGNAL(clicked(const QModelIndex&)), this, SLOT(selectItem(const QModelIndex&))); + m_view->setModel(m_model); + m_view->setStyleSheet("border:none;"); + + layout->addWidget(bar); + layout->addWidget(m_view); + layout->setContentsMargins(QMargins(0,0,0,0)); // Remove all margins + + setTitleBarWidget(new QWidget); // hide the titlebar + + widget->setLayout(layout); + setWidget(widget); +} + +DockExplorer::~DockExplorer(){ + delete m_view; +} diff --git a/RElicAnalysis/dockexplorer.h b/RElicAnalysis/dockexplorer.h new file mode 100644 index 0000000..d25201f --- /dev/null +++ b/RElicAnalysis/dockexplorer.h @@ -0,0 +1,19 @@ +#ifndef DOCKEXPLORER_H +#define DOCKEXPLORER_H + +#include +#include +#include +#include + +class DockExplorer: public QDockWidget { + public: + DockExplorer(); + ~DockExplorer(); + + private: + QStandardItemModel *m_model; + QListView *m_view; +}; + +#endif // DOCKEXPLORER_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..3566171 --- /dev/null +++ b/RElicAnalysis/elf/mdielf.cpp @@ -0,0 +1,516 @@ +#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); + + + /**************************/ + /***** Analyzing part *****/ + /**************************/ + m_textAnalyzing = new QTextEdit; + m_textAnalyzing->setReadOnly(true); + m_textAnalyzing->setStyleSheet("border:none;border-top:1px solid #000;"); + m_textAnalyzing->setMaximumHeight(250); + + layoutCentral->addLayout(layoutTextEdit); + layoutCentral->addWidget(m_textAnalyzing); + + 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..a028101 --- /dev/null +++ b/RElicAnalysis/mainwindow.cpp @@ -0,0 +1,36 @@ +#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); + //m_dockExplorer = new DockExplorer; + //m_dockAnalyzing = new DockAnalizing; + + setWindowTitle("RElicAnalysis"); + setWindowState(Qt::WindowMaximized); + setMinimumSize(1500, 700); + + setCentralWidget(m_centralWidget); + setMenuBar(m_menuBar); + //addDockWidget(Qt::LeftDockWidgetArea, m_dockExplorer, Qt::Horizontal); + //addDockWidget(Qt::BottomDockWidgetArea, m_dockAnalyzing); +} +/*void MainWindow::newProject(ProjectElf *projectElf){ + m_centralWidget->addMdiWindowElf(projectElf); +}*/ +void MainWindow::closeCurrentProject(){ + +} +CentralWidget *MainWindow::getCentralWidget(){ + return m_centralWidget; +} +MainWindow::~MainWindow() { + delete m_centralWidget; + delete m_menuBar; + //delete m_dockExplorer; + //delete m_dockAnalyzing; + if (m_project != NULL) + delete m_project; +} diff --git a/RElicAnalysis/mainwindow.h b/RElicAnalysis/mainwindow.h new file mode 100644 index 0000000..f4022e9 --- /dev/null +++ b/RElicAnalysis/mainwindow.h @@ -0,0 +1,33 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include "menubar.h" +#include "centralwidget.h" +#include "dockexplorer.h" +#include "dockanalizing.h" +#include "projectelf.h" +#include "project.h" +#include "handleproject.h" + + +class MainWindow : public QMainWindow { + Q_OBJECT + + public: + MainWindow(); + ~MainWindow(); + //void newProject(ProjectElf *); + void closeCurrentProject(); + CentralWidget *getCentralWidget(); + + private: + CentralWidget *m_centralWidget; + MenuBar *m_menuBar; + DockExplorer *m_dockExplorer; + DockAnalizing *m_dockAnalyzing; + 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..0fa0ce2 --- /dev/null +++ b/RElicAnalysis/menubar.cpp @@ -0,0 +1,92 @@ +#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 *menuAnalyze = new QMenu("Analyze"); + QMenu *menuAbout = new QMenu("About"); + + // Menu file + QAction *actionOpenFile = new QAction("Open file", this); + actionOpenFile->setShortcut(QKeySequence("ctrl+O")); + connect(actionOpenFile, SIGNAL(triggered()), this, SLOT(_openFile())); + + QAction *actionCloseFile = new QAction("Close the file", 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 analyze + /*QAction *actionShowBinary = new QAction("Show binary", this); + actionShowBinary->setShortcut(QKeySequence("ctrl+b")); + QAction *actionShowStrings = new QAction("Show strings", this); + + menuAnalyze->addAction(actionShowBinary); + menuAnalyze->addAction(actionShowStrings);*/ + + // 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(menuAnalyze); + 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 ELF 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 ELF 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(){ + qDebug() << "Close file"; + // 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..c350630 --- /dev/null +++ b/RElicAnalysis/parsingelf.cpp @@ -0,0 +1,422 @@ +#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); + + 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; + //qDebug() << "Section: " << elfS[i].sh_addr.toHex() << " " << elfS[i].sh_size.toHex(); + } + + 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]; + + // 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); // F***CK segmentation fault + + // Hash table + // https://flapenguin.me/elf-dt-hash + 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"; + 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]); + + // 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]); + } + } + + 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 diff --git a/tests/bfd_file b/tests/bfd_file new file mode 100755 index 0000000000000000000000000000000000000000..2bd5b56ae7bccc65f72524d753a5293a7bffedc2 GIT binary patch literal 17872 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 +#include +#include +#include +#include +#include +#include +#include +#include + +// apt install binutils binutils-dev +// https://sourceware.org/binutils/docs/bfd/BFD-Index.html +// gcc bfd_file.c -o bfd_file -lopcodes -lbfd && ./bfd_file +// https://ftp.gnu.org/old-gnu/Manuals/bfd-2.9.1/html_chapter/bfd_1.html + +static bfd_boolean disassemble; +struct data { + char *data; +}; + +static int dump_asm(void *stream, const char *fmt, ...){ + struct data *sData = (struct data*)stream; + va_list args; + va_start(args, fmt); + char str[64]; + vsprintf(str, fmt, args); + //vsnprintf(str, sizeof(str), fmt, args); + //asprintf(str, "%s\n", args); + + va_end(args); + char tmp[64]; + memset(tmp, 0, 64); + //printf("%d\n", sData->offset); + if (strlen(sData->data) > 0){ + memcpy(tmp, sData->data, 64); + //printf("TMP: %s %d\n", tmp, strlen(tmp)); + strcat(tmp, str); + } + else + memcpy(tmp, str, strlen(str)); + memcpy(sData->data, tmp, 64); +} +static int disass(){ + struct disassemble_info disasm_info; + struct data *sData = (struct data*)malloc(sizeof(struct data)); + sData->data = (char*)malloc(64); + //char filename[256] = "/home/geoffrey/Documents/GIT/cybersecurity/forensic/disassembly/call_function"; + char filename[256]; + bfd *abfd = NULL; + struct stat s_stat; + int fd_r; + + pid_t pid = getpid(); + sprintf(filename, "/proc/%d/exe", pid); + printf("%s\n", filename); + if ((fd_r = open(filename, O_RDONLY)) < 0){ + printf("Failed to read file\n"); + return -1; + } + + fstat(fd_r, &s_stat); + printf("Len: %d\n", s_stat.st_size); + + close(fd_r); + + bfd_init(); + abfd = bfd_openr(filename, NULL); + //set_default_bfd_target(); + + if (abfd == NULL){ + printf("Cannot read bfd file\n"); + free(sData->data); + free(sData); + return -1; + } + + // Defined in https://github.com/redox-os/binutils-gdb/blob/master/include/dis-asm.h + //init_disassemble_info (&disasm_info, stdout, (fprintf_ftype) fprintf); + init_disassemble_info (&disasm_info, sData, dump_asm); + disasm_info.arch = bfd_get_arch(abfd); + disasm_info.mach = bfd_get_mach(abfd); + /*disasm_info.arch = bfd_arch_i386; + disasm_info.mach = bfd_mach_x86_64; + disasm_info.read_memory_func = buffer_read_memory; + disasm_info.buffer = code; + disasm_info.buffer_vma = 0; + disasm_info.buffer_length = length;*/ + disassemble_init_for_target(&disasm_info); + printf("%d\n", bfd_get_arch(abfd)); + printf("%d\n", bfd_get_mach(abfd)); + printf("%s\n", bfd_printable_arch_mach( + bfd_get_arch(abfd), + bfd_get_mach(abfd))); + + //disassembler_ftype disas = disassembler(abfd); + disassembler_ftype disas = disassembler(bfd_arch_i386, false, bfd_mach_x86_64, NULL); + + if (!disas){ + printf("Can't disassemble\n"); + perror("disassembler()"); + return -1; + } + + size_t i = 0; + size_t length = s_stat.st_size; + while (i < length){ + size_t octets = disas(i, &disasm_info); + printf("%s\n", sData->data); + i += octets; + memset(sData->data, 0, 64); + }; + + free(sData->data); + free(sData); + bfd_close(abfd); + return 0; +} +int main(void){ + disass(); + return 0; +} diff --git a/tests/test_bfd b/tests/test_bfd new file mode 100755 index 0000000000000000000000000000000000000000..62beadb5845c22e64096adbe8cf8d13eb49f9af8 GIT binary patch literal 16616 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 +#include +#include +#include +#include +#include +#include + +#include +#include "bfd.h" + +// apt install binutils binutils-dev +// gcc test_bfd.c -o test_bfd -lopcodes && ./test_bfd +// https://ftp.gnu.org/old-gnu/Manuals/bfd-2.9.1/html_chapter/bfd_1.html + +static bfd_boolean disassemble; +struct data { + char *data; +}; + +static int dump_asm(void *stream, const char *fmt, ...){ + struct data *sData = (struct data*)stream; + va_list args; + va_start(args, fmt); + char str[64]; + vsprintf(str, fmt, args); + //vsnprintf(str, sizeof(str), fmt, args); + //asprintf(str, "%s\n", args); + + va_end(args); + char tmp[64]; + memset(tmp, 0, 64); + //printf("%d\n", sData->offset); + if (strlen(sData->data) > 0){ + memcpy(tmp, sData->data, 64); + //printf("TMP: %s %d\n", tmp, strlen(tmp)); + strcat(tmp, str); + } + else + memcpy(tmp, str, strlen(str)); + memcpy(sData->data, tmp, 64); +} +static int disas(unsigned char *code, size_t length){ + struct disassemble_info disasm_info; + struct data *sData = (struct data*)malloc(sizeof(struct data)); + sData->data = (char*)malloc(64); + + disassemble = TRUE; + bfd_init(); + //set_default_bfd_target(); + + // Defined in https://github.com/redox-os/binutils-gdb/blob/master/include/dis-asm.h + //init_disassemble_info (&disasm_info, stdout, (fprintf_ftype) fprintf); + 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 = code; + 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){ + printf("Can't disassemble\n"); + return -1; + } + + size_t i = 0; + while (i < length){ + size_t octets = disas(i, &disasm_info); + printf("%s\n", sData->data); + i += octets; + memset(sData->data, 0, 64); + }; + + free(sData->data); + free(sData); + return 0; +} +int main(void){ + unsigned char code[] = { + 0x55, 0x48, 0x89, 0xE5, 0x48, 0x83, 0xEC, 0x20, 0x89, 0x7D, +0xEC, 0x48, 0x8D, 0x4D, 0xF4, 0x8B, 0x45, 0xEC, 0xBA, 0x04, +0x00, 0x00, 0x00, 0x48, 0x89, 0xCE, 0x89, 0xC7, 0xE8, 0x42, +0xFC, 0xFF, 0xFF, 0x48, 0x89, 0x45, 0xF8, 0x48, 0x83, 0x7D, +0xF8, 0xFF, 0x75, 0x07, 0xB8, 0xFF, 0xFF, 0xFF, 0xFF, 0xEB, +0x2C, 0x0F, 0xB6, 0x45, 0xF4, 0x3C, 0x7F, 0x75, 0x1F, 0x0F, +0xB6, 0x45, 0xF5, 0x3C, 0x45, 0x75, 0x17, 0x0F, 0xB6, 0x45, +0xF6, 0x3C, 0x4C, 0x75, 0x0F, 0x0F, 0xB6, 0x45, 0xF7, 0x3C, +0x46, 0x75, 0x07, 0xB8, 0x01, 0x00, 0x00, 0x00, 0xEB, 0x05, +0xB8, 0x00, 0x00, 0x00, 0x00, 0xC9, 0xC3 + }; + size_t length = sizeof(code); + + disas(code, length); + return 0; +} diff --git a/tests/test_bfd.c_bck b/tests/test_bfd.c_bck new file mode 100644 index 0000000..b07be19 --- /dev/null +++ b/tests/test_bfd.c_bck @@ -0,0 +1,119 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include "bfd.h" + +// apt install binutils binutils-dev +// gcc test_bfd.c -o test_bfd -lopcodes && ./test_bfd +// https://ftp.gnu.org/old-gnu/Manuals/bfd-2.9.1/html_chapter/bfd_1.html + +static bfd_boolean disassemble; +struct data { + char **data; + size_t offset; + size_t index; +}; + +static int dump_asm(void *stream, const char *fmt, ...){ + struct data *sData = (struct data*)stream; + va_list args; + va_start(args, fmt); + char str[64]; + vsprintf(str, fmt, args); + //vsnprintf(str, sizeof(str), fmt, args); + //asprintf(str, "%s\n", args); + + va_end(args); + //printf("%s\n", str); + sData->offset = strlen(str); + int index = sData->index; + char tmp[64]; + memset(tmp, 0, 64); + //printf("%d\n", sData->offset); + if (strlen(sData->data[index]) > 0){ + memcpy(tmp, sData->data[index], 64); + //printf("TMP: %s %d\n", tmp, strlen(tmp)); + strcat(tmp, str); + } + else + memcpy(tmp, str, strlen(str)); + memcpy(sData->data[index], tmp, 64); +} +static int disas(unsigned char *code, size_t length){ + struct disassemble_info disasm_info; + struct data *sData = (struct data*)malloc(sizeof(struct data)); + sData->data = (char**)malloc(sizeof(char *) * 64); + for (int i = 0; i < 64; i++){ + sData->data[i] = (char*)malloc(64); + } + for (int i = 0; i < 64; i++) + memset(sData->data[i], 0, 64); + sData->offset = 0; + sData->index = 0; + + disassemble = TRUE; + bfd_init(); + //set_default_bfd_target(); + + // Defined in https://github.com/redox-os/binutils-gdb/blob/master/include/dis-asm.h + //init_disassemble_info (&disasm_info, stdout, (fprintf_ftype) fprintf); + 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 = code; + 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){ + printf("Can't disassemble\n"); + return -1; + } + + size_t i = 0; + while (i < length){ + size_t octets = disas(i, &disasm_info); + printf("%s\n", sData->data[sData->index]); + //disasm_info.stream = stdout; + //disasm_info.stream = (void*)sData; + //struct data *sData = (struct data*)disasm_info.stream; + //disasm_info.fprintf_func = (fprintf_ftype)fprintf; + //printf("%ld\n", sData->index); + i += octets; + sData->index += 1; + }; + + for (int i = 0; i < 64; i++) + free(sData->data[i]); + free(sData->data); + free(sData); + return 0; +} +int main(void){ + unsigned char code[] = { + 0x55, 0x48, 0x89, 0xE5, 0x48, 0x83, 0xEC, 0x20, 0x89, 0x7D, +0xEC, 0x48, 0x8D, 0x4D, 0xF4, 0x8B, 0x45, 0xEC, 0xBA, 0x04, +0x00, 0x00, 0x00, 0x48, 0x89, 0xCE, 0x89, 0xC7, 0xE8, 0x42, +0xFC, 0xFF, 0xFF, 0x48, 0x89, 0x45, 0xF8, 0x48, 0x83, 0x7D, +0xF8, 0xFF, 0x75, 0x07, 0xB8, 0xFF, 0xFF, 0xFF, 0xFF, 0xEB, +0x2C, 0x0F, 0xB6, 0x45, 0xF4, 0x3C, 0x7F, 0x75, 0x1F, 0x0F, +0xB6, 0x45, 0xF5, 0x3C, 0x45, 0x75, 0x17, 0x0F, 0xB6, 0x45, +0xF6, 0x3C, 0x4C, 0x75, 0x0F, 0x0F, 0xB6, 0x45, 0xF7, 0x3C, +0x46, 0x75, 0x07, 0xB8, 0x01, 0x00, 0x00, 0x00, 0xEB, 0x05, +0xB8, 0x00, 0x00, 0x00, 0x00, 0xC9, 0xC3 + }; + size_t length = sizeof(code); + + disas(code, length); + return 0; +} diff --git a/tests/test_buffer.c b/tests/test_buffer.c new file mode 100644 index 0000000..7b563c7 --- /dev/null +++ b/tests/test_buffer.c @@ -0,0 +1,36 @@ +#include +#include +#include +#include +#include +#include + +struct test{ + char b1; + char b2; + char b3; +}; +int main(void){ + char buf[9]; + int fd_r; + //struct test *s_test = malloc(sizeof(struct test)); + struct test *s_test = NULL; + memset(buf, 0, 9); + + if ((fd_r = open("test.txt", O_RDONLY)) == -1){ + exit(-1); + } + + read(fd_r, buf, 9); + + for (int i = 0; i < 9; i++){ + printf("%c", buf[i]); + } + printf("\n"); + s_test = (struct test*)buf + 2; + + printf("%c %c %c\n", s_test->b1, s_test->b2, s_test->b3); + + close(fd_r); + //free(s_test); +} diff --git a/tests/test_realloc b/tests/test_realloc new file mode 100755 index 0000000000000000000000000000000000000000..b3fb3391eebdcf0ff233ec447776fe893a0d3568 GIT binary patch literal 16104 zcmeHOZ)_CD6`wmBFa+>Hpx99GsSPbO@?wK=Nm`c!`(TauDAV{r1l_JurDv3Zk3(_2hMsBP4`rgcY z$9lM%sY0L;5N}^*vDzFi+D9*8Aj;XqSS(s+xs%`=+u(O zqk&KJVQR0^dFY}1Gg5lRmPEX_wPi~p)|iN=a>I?oJ6aoCTa0Yl*eV;weud$KV`}fg zqXNQAp^nlhg2$E2@zA!4?C5{-?fPfFIA^{3>do1Eul#7u2QwqB1@yswBL{u3U_2fo zC}Vvwe9%Ybc+vACricN;4Q%mNBlWptnIEF5~}H1^;6e{PDQTwY#~3{zQd1 zc-)_Y&qKws`i5<*Kc2D^@#h?ov=fPRpRg=Ak9}6wwKJ}jwBsp}ag=2!=k^WS88MWJ zr`&#lZg_TE#oqm$yW6d;#w(K#z zZ<&*ASD@A6>U)L|_xp|7hVOs~2RPYQ4_tF{!AgXuX5H0r=x>7)9{S?qjoOCe==UZe z_)nv8a{GG-o9E2&f_eV$JI(X6Rc7F#d2P;J1p$6c0cwgj`fD5DxR{UmIo^h5k$du} zIo9?9R?YES?oxB2?IjTM({sgQJ_gCTxZoJbfv*F19)J2-Fk+yiz=M+&K8H_|`FDY5 zjz?!NMe`tv+v_KydGq_`XE91X0ga4B?*RQy_2`vzOHgNG!w~*=o3NenOZkCPPYU=v9*X|aJQ-E0TNfb-qxx*qb915&kx~S$P{2zdNe0Sdi zoX?;`$0GS^>j|i0JEuQTvBdJwp5=A>Y9D*H=D66o_UX@UdqTe6L;ly`a~1sfofwER zt_ye>^bu50!M5<7;XBY?J^mjNT^@QV*uHFGRVV;mA}=5O4EP!FGvH^y&w!r+KLdUS z{0#UR@H6oLn*m{m-AQoQR zhwD_a3{%6bKT93seoslS2kT>bK&M@qs3@yY|crs zd35Vmqp4*xsUIB?^HnYG7IiPRcDY)dpoE3V;qeOoc)o(LDz=~<7xGXaruD5L%CZf< zQP0;4sYNGL0%ENQMU;lmC$6Urh))O}|H>-Fx1fS?`1)i0%hG>!Y5gthUx!M$*zVk4e4K3x~^FNAymq(!(IXjDX0G|=)+9* zpd_`byYF4VK&!9I$k#~%WH*yG35=Rv~U0ZA<(B^yS+91?WRCPU<2YL1A3_hf4cm`Nix#>*SvU9_tFP$7pIIa^jn#z0N8vM9r zXEOGP<)qxqi0IGQNym!ilF1REcq9uv-FdFAlXWdxq7VJDtUZT!9f(@dgB=#EtMB;g z!CePB+d&Q6Bdn-NR(HJ(N21+U_paUhqn4mW{t;f&FBkIbTLMZ} zSn>bJRsK%Qc5NY#kG2hzs46hp)h+<&;`}WERxF#g2JKV~w+nP00eoOi(`3%@C<==zh)GqcB^UQpn_r*bnQx4|y{FRaCtKcFF#_*PJf`D^D=JWiI zk>{!0fA(V>1wPI#S?2jAV-q!|_n-SP2^GvK^Lf6@I7N=!zJC1QBmQpkxe&ukCz`N9%Gl+8R}#%;p-ZIh#VN%pTn@sct+zN zCj(=IiG<8!{H?~HA_HTPPmIRru;(;B&x08GxsCnx>*x2xFEtY;PeMLlq;1U}y5;cU=Olg}<$amDKg*1Ffm`e0^L+hu zBPgM;pOPNPtp5lMICi;xo=5XO+X5aO6ukbhFptmw&p?MR%;)FTQ(Mqmh#vX<;EVZ; z3xVwAiwOBPDdcuI0`pj23S`uA`#et!5nuPmw!CEpwNLIP6Z4-eX;g*zUgWXkb&XKK w)6}u`V8nSf_lNJli$TYI_ww`jzy_s33(oB-E6(A|^B3U{8 +#include + +#define TEST 5 + +int main(void){ + int *foo = (int*)malloc(sizeof(int) * TEST); + for(int i = 0; i < TEST; i++) + foo[i] = i; + + for(int i = 0; i < TEST; i++) + printf("%d\n", foo[i]); + printf("\n"); + + foo = (int *)realloc(foo, sizeof(int) * (TEST + TEST)); + for(int i = TEST; i < TEST + TEST; i++) + foo[i] = i + 10; + + for(int i = 0; i < TEST + TEST; i++) + printf("%d\n", foo[i]); + return 0; +}