From 20fe52f8eb2cebe65ebe75683d4ad34ce20f3c4c Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Mon, 5 Jul 2021 13:42:33 +0200 Subject: [PATCH] added a picture viewer in QT to be used with `hf emrtd` --- CHANGELOG.md | 1 + client/CMakeLists.txt | 1 + client/Makefile | 10 ++-- client/src/cmdhfemrtd.c | 69 ++++++++++++++++++++++-- client/src/cmdhfemrtd.h | 8 +++ client/src/guidummy.cpp | 12 +++++ client/src/proxgui.cpp | 31 +++++++++++ client/src/proxgui.h | 6 +++ client/src/proxguiqt.cpp | 114 +++++++++++++++++++++++++++++++++++++++ client/src/proxguiqt.h | 28 +++++++++- client/src/ui/image.ui | 33 ++++++++++++ 11 files changed, 306 insertions(+), 7 deletions(-) create mode 100644 client/src/ui/image.ui diff --git a/CHANGELOG.md b/CHANGELOG.md index 41880a244..86b8a4a9e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file. This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log... ## [unreleased][unreleased] + - Added a picture viewer in QT. To be used with `hf emrtd info` (@iceman1001) - Fix - move des functions to libcrypto (@merlokk) - Added `CLIGetOptionList` to cliparser that makes it easier to implement text options in the cli (@merlokk) - Added experimental support for macOS users utilizing MacPorts instead of Homebrew (@linuxgemini) diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index a81682337..f7920b4e0 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -229,6 +229,7 @@ set (TARGET_SOURCES ${PM3_ROOT}/client/src/uart/uart_posix.c ${PM3_ROOT}/client/src/uart/uart_win32.c ${PM3_ROOT}/client/src/ui/overlays.ui + ${PM3_ROOT}/client/src/ui/image.ui ${PM3_ROOT}/client/src/aiddesfire.c ${PM3_ROOT}/client/src/aidsearch.c ${PM3_ROOT}/client/src/cmdanalyse.c diff --git a/client/Makefile b/client/Makefile index 507a3a7a8..526bf21b0 100644 --- a/client/Makefile +++ b/client/Makefile @@ -657,9 +657,9 @@ OBJS += $(OBJCSRCS:%.m=$(OBJDIR)/%.o) BINS = proxmark3 -CLEAN = $(BINS) src/version_pm3.c src/*.moc.cpp src/ui/ui_overlays.h lualibs/pm3_cmd.lua lualibs/mfc_default_keys.lua +CLEAN = $(BINS) src/version_pm3.c src/*.moc.cpp src/ui/ui_overlays.h src/ui/ui_image.h lualibs/pm3_cmd.lua lualibs/mfc_default_keys.lua # transition: cleaning also old path stuff -CLEAN += flasher *.moc.cpp ui/ui_overlays.h +CLEAN += flasher *.moc.cpp ui/ui_overlays.h ui/ui_image.h ########### # targets # @@ -675,7 +675,7 @@ proxmark3: $(OBJS) $(STATICLIBS) lualibs/pm3_cmd.lua lualibs/mfc_default_keys.lu $(info [=] LD $@) $(Q)$(LD) $(PM3LDFLAGS) $(OBJS) $(STATICLIBS) $(LDLIBS) -o $@ -src/proxgui.cpp: src/ui/ui_overlays.h +src/proxgui.cpp: src/ui/ui_overlays.h src/ui/ui_image.h src/proxguiqt.moc.cpp: src/proxguiqt.h $(info [-] MOC $@) @@ -685,6 +685,10 @@ src/ui/ui_overlays.h: src/ui/overlays.ui $(info [-] UIC $@) $(Q)$(UIC) $^ > $@ +src/ui/ui_image.h: src/ui/image.ui + $(info [-] UIC $@) + $(Q)$(UIC) $^ > $@ + lualibs/pm3_cmd.lua: ../include/pm3_cmd.h $(info [=] GEN $@) $(Q)awk -f pm3_cmd_h2lua.awk $^ > $@ diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index 312099268..2cda5e1da 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -24,6 +24,8 @@ #include "crapto1/crapto1.h" // prng_successor #include "commonutil.h" // num_to_bytes #include "util_posix.h" // msclock +#include "ui.h" // searchhomedirectory +#include "proxgui.h" // Picture Window // Max file size in bytes. Used in several places. // Average EF_DG2 seems to be around 20-25kB or so, but ICAO doesn't set an upper limit @@ -53,6 +55,7 @@ static int emrtd_dump_ef_dg7(uint8_t *file_contents, size_t file_length, const c static int emrtd_dump_ef_sod(uint8_t *file_contents, size_t file_length, const char *path); static int emrtd_print_ef_com_info(uint8_t *data, size_t datalen); static int emrtd_print_ef_dg1_info(uint8_t *data, size_t datalen); +static int emrtd_print_ef_dg2_info(uint8_t *data, size_t datalen); static int emrtd_print_ef_dg11_info(uint8_t *data, size_t datalen); static int emrtd_print_ef_dg12_info(uint8_t *data, size_t datalen); static int emrtd_print_ef_cardaccess_info(uint8_t *data, size_t datalen); @@ -84,7 +87,7 @@ static emrtd_dg_t dg_table[] = { // tag dg# fileid filename desc pace eac req fast parser dumper {0x60, 0, 0x011E, "EF_COM", "Header and Data Group Presence Information", false, false, true, true, emrtd_print_ef_com_info, NULL}, {0x61, 1, 0x0101, "EF_DG1", "Details recorded in MRZ", false, false, true, true, emrtd_print_ef_dg1_info, NULL}, - {0x75, 2, 0x0102, "EF_DG2", "Encoded Face", false, false, true, false, NULL, emrtd_dump_ef_dg2}, + {0x75, 2, 0x0102, "EF_DG2", "Encoded Face", false, false, true, false, emrtd_print_ef_dg2_info, emrtd_dump_ef_dg2}, {0x63, 3, 0x0103, "EF_DG3", "Encoded Finger(s)", false, true, false, false, NULL, NULL}, {0x76, 4, 0x0104, "EF_DG4", "Encoded Eye(s)", false, true, false, false, NULL, NULL}, {0x65, 5, 0x0105, "EF_DG5", "Displayed Portrait", false, false, false, false, NULL, emrtd_dump_ef_dg5}, @@ -1398,6 +1401,65 @@ static int emrtd_print_ef_dg1_info(uint8_t *data, size_t datalen) { return PM3_SUCCESS; } +static int emrtd_print_ef_dg2_info(uint8_t *data, size_t datalen) { + + int offset = 0; + + // This is a hacky impl that just looks for the image header. I'll improve it eventually. + // based on mrpkey.py + // Note: Doing datalen - 6 to account for the longest data we're checking. + // Checks first byte before the rest to reduce overhead + for (offset = 0; offset < datalen - 6; offset++) { + if ((data[offset] == 0xFF && memcmp(jpeg_header, data + offset, 4) == 0) || + (data[offset] == 0x00 && memcmp(jpeg2k_header, data + offset, 6) == 0)) { + datalen = datalen - offset; + break; + } + } + + // If we didn't get any data, return false. + if (datalen == 0) { + return PM3_ESOFT; + } + + bool is_jpg = (data[offset] == 0xFF); + + char *fn = calloc( strlen(dg_table[EF_DG2].filename) + 4 + 1, sizeof(uint8_t)); + if (fn == NULL) + return PM3_EMALLOC; + + sprintf(fn, "%s.%s", dg_table[EF_DG2].filename, (is_jpg) ? "jpg" : "jp2"); + + PrintAndLogEx(DEBUG, "image filename `" _YELLOW_("%s") "`", fn); + + char *path; + if (searchHomeFilePath(&path, NULL, fn, false) != PM3_SUCCESS) { + free(fn); + return PM3_EFILE; + } + free(fn); + + // remove old file + if (fileExists(path)) { + PrintAndLogEx(DEBUG, "Delete old temp file `" _YELLOW_("%s") "`", path); + remove(path); + } + + // temp file. + PrintAndLogEx(DEBUG, "Save temp file `" _YELLOW_("%s") "`", path); + saveFile(path, "", data + offset, datalen); + + PrintAndLogEx(DEBUG, "view temp file `" _YELLOW_("%s") "`", path); + ShowPictureWindow(path); + msleep(500); + + // delete temp file + PrintAndLogEx(DEBUG, "Deleting temp file `" _YELLOW_("%s") "`", path); + remove(path); + //free(path); + return PM3_SUCCESS; +} + static int emrtd_print_ef_dg11_info(uint8_t *data, size_t datalen) { uint8_t taglist[100] = { 0x00 }; size_t taglistlen = 0; @@ -1930,6 +1992,7 @@ int infoHF_EMRTD_offline(const char *path) { if (loadFile_safeEx(filepath, ".BIN", (void **)&data, (size_t *)&datalen, false) == PM3_SUCCESS) { emrtd_print_ef_cardaccess_info(data, datalen); + free(data); } else { PrintAndLogEx(HINT, "The error above this is normal. It just means that your eMRTD lacks PACE."); } @@ -2184,12 +2247,12 @@ static int CmdHFeMRTDInfo(const char *Cmd) { } } uint8_t path[FILENAME_MAX] = { 0x00 }; - bool offline = CLIParamStrToBuf(arg_get_str(ctx, 5), path, sizeof(path), &slen) == 0 && slen > 0; + bool is_offline = CLIParamStrToBuf(arg_get_str(ctx, 5), path, sizeof(path), &slen) == 0 && slen > 0; CLIParserFree(ctx); if (error) { return PM3_ESOFT; } - if (offline) { + if (is_offline) { return infoHF_EMRTD_offline((const char *)path); } else { bool restore_apdu_logging = GetAPDULogging(); diff --git a/client/src/cmdhfemrtd.h b/client/src/cmdhfemrtd.h index 81f59b88c..45d0d28df 100644 --- a/client/src/cmdhfemrtd.h +++ b/client/src/cmdhfemrtd.h @@ -13,6 +13,10 @@ #include "common.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef struct emrtd_dg_s { uint8_t tag; uint8_t dgnum; @@ -53,4 +57,8 @@ int CmdHFeMRTD(const char *Cmd); int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_available, const char *path); int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_available); int infoHF_EMRTD_offline(const char *path); + +#ifdef __cplusplus +} +#endif #endif diff --git a/client/src/guidummy.cpp b/client/src/guidummy.cpp index 8557b0015..886bc1516 100644 --- a/client/src/guidummy.cpp +++ b/client/src/guidummy.cpp @@ -21,6 +21,18 @@ extern "C" void ShowGraphWindow(void) { extern "C" void HideGraphWindow(void) {} extern "C" void RepaintGraphWindow(void) {} + +extern "C" void ShowPictureWindow(char *fn) { + static int warned = 0; + + if (!warned) { + printf("No GUI in this build!\n"); + warned = 1; + } +} +extern "C" void HidePictureWindow(void) {} +extern "C" void RepaintPictureWindow(void) {} + extern "C" void MainGraphics() {} extern "C" void InitGraphics(int argc, char **argv) {} extern "C" void ExitGraphics(void) {} diff --git a/client/src/proxgui.cpp b/client/src/proxgui.cpp index f8dd10fa4..0fe941133 100644 --- a/client/src/proxgui.cpp +++ b/client/src/proxgui.cpp @@ -40,6 +40,7 @@ extern "C" void ShowGraphWindow(void) { } gui->ShowGraphWindow(); + } extern "C" void HideGraphWindow(void) { @@ -56,6 +57,36 @@ extern "C" void RepaintGraphWindow(void) { gui->RepaintGraphWindow(); } + +// hook up picture viewer +extern "C" void ShowPictureWindow(char *fn) { + if (!gui) { + // Show a notice if X11/XQuartz isn't available +#if defined(__MACH__) && defined(__APPLE__) + PrintAndLogEx(WARNING, "You appear to be on a MacOS device without XQuartz.\nYou may need to install XQuartz (https://www.xquartz.org/) to make the plot work."); +#else + PrintAndLogEx(WARNING, "You appear to be on an environment without an X11 server or without DISPLAY environment variable set.\nPlot may not work until you resolve these issues."); +#endif + return; + } + + gui->ShowPictureWindow(fn); +} + +extern "C" void HidePictureWindow(void) { + if (!gui) + return; + + gui->HidePictureWindow(); +} + +extern "C" void RepaintPictureWindow(void) { + if (!gui) + return; + + gui->RepaintPictureWindow(); +} + extern "C" void MainGraphics(void) { if (!gui) return; diff --git a/client/src/proxgui.h b/client/src/proxgui.h index 887a0e36d..d62c08d39 100644 --- a/client/src/proxgui.h +++ b/client/src/proxgui.h @@ -22,6 +22,12 @@ extern "C" { void ShowGraphWindow(void); void HideGraphWindow(void); void RepaintGraphWindow(void); + +// hook up picture viewer +void ShowPictureWindow(char *fn); +void HidePictureWindow(void); +void RepaintPictureWindow(void); + void MainGraphics(void); void InitGraphics(int argc, char **argv, char *script_cmds_file, char *script_cmd, bool stayInCommandLoop); void ExitGraphics(void); diff --git a/client/src/proxguiqt.cpp b/client/src/proxguiqt.cpp index 6ba6b7ab7..2194b8bdc 100644 --- a/client/src/proxguiqt.cpp +++ b/client/src/proxguiqt.cpp @@ -12,6 +12,7 @@ #include #include #include +//#include #include #include #include @@ -32,6 +33,7 @@ #include "graph.h" #include "cmddata.h" #include "util_darwin.h" +//#include "fileutils.h" extern "C" int preferences_save(void); @@ -54,6 +56,19 @@ void ProxGuiQT::HideGraphWindow(void) { emit HideGraphWindowSignal(); } +// emit picture viewer signals +void ProxGuiQT::ShowPictureWindow(char *fn) { + emit ShowPictureWindowSignal(fn); +} + +void ProxGuiQT::RepaintPictureWindow(void) { + emit RepaintPictureWindowSignal(); +} + +void ProxGuiQT::HidePictureWindow(void) { + emit HidePictureWindowSignal(); +} + void ProxGuiQT::Exit(void) { emit ExitSignal(); } @@ -71,6 +86,7 @@ void ProxGuiQT::_ShowGraphWindow(void) { plotwidget = new ProxWidget(); } plotwidget->show(); + } void ProxGuiQT::_RepaintGraphWindow(void) { @@ -87,10 +103,80 @@ void ProxGuiQT::_HideGraphWindow(void) { plotwidget->hide(); } +// picture viewer +void ProxGuiQT::_ShowPictureWindow(char *fn) { + if (fn == NULL) + return; + size_t slen = strlen(fn); + if (slen == 0) + return; + + char *myfn = (char*)calloc(slen + 1, sizeof(uint8_t)); + if (myfn == NULL) + return; + + memcpy(myfn, fn, slen); + + if (!plotapp) + return; + + if (!pictureWidget) { + +#if defined(__MACH__) && defined(__APPLE__) + makeFocusable(); +#endif + + pictureWidget = new QWidget(); + } + + QPixmap pm; + if(pm.load(myfn) == false){ + qWarning("Failed to load %s", myfn); + } + free(myfn); + free(fn); + + //QPixmap newPixmap = pm.scaled(QSize(50,50), Qt::KeepAspectRatio); + //pm = pm.scaled(pictureController->lbl_pm->size(), Qt::KeepAspectRatio); + + pictureController->lbl_pm->setPixmap(pm); + pictureController->lbl_pm->setScaledContents(false); + pictureController->lbl_pm->setAlignment(Qt::AlignCenter); + + QString s = QString("w: %1 h: %2") + .arg(pm.size().width()) + .arg(pm.size().height() + ); + pictureController->lbl_sz->setText(s); + pictureWidget->show(); +} + +void ProxGuiQT::_RepaintPictureWindow(void) { + if (!plotapp || !pictureWidget) + return; + + pictureWidget->update(); +} + +void ProxGuiQT::_HidePictureWindow(void) { + if (!plotapp || !pictureWidget) + return; + + pictureWidget->hide(); +} + void ProxGuiQT::_Exit(void) { delete this; } +void ProxGuiQT::closeEvent(QCloseEvent *event) { + event->ignore(); + pictureWidget->hide(); +} +void ProxGuiQT::hideEvent(QHideEvent *event) { + pictureWidget->hide(); +} + void ProxGuiQT::_StartProxmarkThread(void) { if (!proxmarkThread) return; @@ -105,11 +191,23 @@ void ProxGuiQT::_StartProxmarkThread(void) { void ProxGuiQT::MainLoop() { plotapp = new QApplication(argc, argv); + // Setup the picture widget + pictureWidget = new QWidget(); + pictureController = new Ui::PictureForm(); + pictureController->setupUi(pictureWidget); + connect(this, SIGNAL(ShowGraphWindowSignal()), this, SLOT(_ShowGraphWindow())); connect(this, SIGNAL(RepaintGraphWindowSignal()), this, SLOT(_RepaintGraphWindow())); connect(this, SIGNAL(HideGraphWindowSignal()), this, SLOT(_HideGraphWindow())); + connect(this, SIGNAL(ExitSignal()), this, SLOT(_Exit())); + // hook up picture viewer signals + connect(this, SIGNAL(ShowPictureWindowSignal(char*)), this, SLOT(_ShowPictureWindow(char*))); + connect(this, SIGNAL(RepaintPictureWindowSignal()), this, SLOT(_RepaintPictureWindow())); + connect(this, SIGNAL(HidePictureWindowSignal()), this, SLOT(_HidePictureWindow())); + + //start proxmark thread after starting event loop QTimer::singleShot(200, this, SLOT(_StartProxmarkThread())); @@ -127,6 +225,18 @@ ProxGuiQT::ProxGuiQT(int argc, char **argv, WorkerThread *wthread) : plotapp(NUL } ProxGuiQT::~ProxGuiQT(void) { + + if (pictureController) { + delete pictureController; + pictureController = NULL; + } + + if (pictureWidget) { + pictureWidget->close(); + delete pictureWidget; + pictureWidget = NULL; + } + if (plotapp) { plotapp->quit(); plotapp = NULL; @@ -197,6 +307,8 @@ void ProxWidget::vchange_dthr_down(int v) { g_useOverlays = true; RepaintGraphWindow(); } + + ProxWidget::ProxWidget(QWidget *parent, ProxGuiQT *master) : QWidget(parent) { this->master = master; // Set the initail postion and size from settings @@ -207,6 +319,7 @@ ProxWidget::ProxWidget(QWidget *parent, ProxGuiQT *master) : QWidget(parent) { // Setup the controller widget controlWidget = new SliderWidget(); //new QWidget(); + opsController = new Ui::Form(); opsController->setupUi(controlWidget); //Due to quirks in QT Designer, we need to fiddle a bit @@ -295,6 +408,7 @@ void ProxWidget::showEvent(QShowEvent *event) { controlWidget->show(); else controlWidget->hide(); + plot->show(); } void ProxWidget::moveEvent(QMoveEvent *event) { diff --git a/client/src/proxguiqt.h b/client/src/proxguiqt.h index 168b1b9eb..513300d89 100644 --- a/client/src/proxguiqt.h +++ b/client/src/proxguiqt.h @@ -22,6 +22,7 @@ #include #include "ui/ui_overlays.h" +#include "ui/ui_image.h" class ProxWidget; @@ -81,8 +82,8 @@ class ProxWidget : public QWidget { ProxGuiQT *master; Plot *plot; Ui::Form *opsController; -// QWidget *controlWidget; SliderWidget *controlWidget; + public: ProxWidget(QWidget *parent = 0, ProxGuiQT *master = NULL); ~ProxWidget(void); @@ -125,6 +126,10 @@ class ProxGuiQT : public QObject { private: QApplication *plotapp; ProxWidget *plotwidget; + + Ui::PictureForm *pictureController; + QWidget *pictureWidget; + int argc; char **argv; //void (*main_func)(void); @@ -136,13 +141,29 @@ class ProxGuiQT : public QObject { void ShowGraphWindow(void); void RepaintGraphWindow(void); void HideGraphWindow(void); + + // hook up picture viewer + void ShowPictureWindow(char *fn); + void HidePictureWindow(void); + void RepaintPictureWindow(void); + void MainLoop(void); void Exit(void); + protected: + void closeEvent(QCloseEvent *event); + void hideEvent(QHideEvent *event); + private slots: void _ShowGraphWindow(void); void _RepaintGraphWindow(void); void _HideGraphWindow(void); + + // hook up picture viewer + void _ShowPictureWindow(char *fn); + void _HidePictureWindow(void); + void _RepaintPictureWindow(void); + void _Exit(void); void _StartProxmarkThread(void); @@ -151,6 +172,11 @@ class ProxGuiQT : public QObject { void RepaintGraphWindowSignal(void); void HideGraphWindowSignal(void); void ExitSignal(void); + + // hook up picture viewer signals + void ShowPictureWindowSignal(char *fn); + void HidePictureWindowSignal(void); + void RepaintPictureWindowSignal(void); }; #endif // PROXGUI_QT diff --git a/client/src/ui/image.ui b/client/src/ui/image.ui new file mode 100644 index 000000000..cb28d7700 --- /dev/null +++ b/client/src/ui/image.ui @@ -0,0 +1,33 @@ + + + PictureForm + + + + 0 + 0 + 400 + 400 + + + + Picture Viewer + + + + + + + + + + Image size + + + + + + + + +