diff --git a/.gitignore b/.gitignore index 1358d83..fb50266 100644 --- a/.gitignore +++ b/.gitignore @@ -14,7 +14,6 @@ *.so.* *_pch.h.cpp *_resource.rc -*.qm .#* *.*# core diff --git a/Proxmark3GUI.pro b/Proxmark3GUI.pro index 85feb73..31f5966 100644 --- a/Proxmark3GUI.pro +++ b/Proxmark3GUI.pro @@ -16,6 +16,7 @@ DEFINES += QT_DEPRECATED_WARNINGS #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 SOURCES += \ + common/myeventfilter.cpp \ main.cpp \ common/pm3process.cpp \ common/util.cpp \ @@ -27,6 +28,7 @@ SOURCES += \ ui/mf_attack_hardnesteddialog.cpp \ HEADERS += \ + common/myeventfilter.h \ common/pm3process.h \ common/util.h \ module/mifare.h \ @@ -52,7 +54,7 @@ qnx: target.path = /tmp/$${TARGET}/bin else: unix:!android: target.path = /opt/$${TARGET}/bin !isEmpty(target.path): INSTALLS += target -VERSION = 0.1.3 +VERSION = 0.1.4 QMAKE_TARGET_PRODUCT = "Proxmark3GUI" QMAKE_TARGET_DESCRIPTION = "Proxmark3GUI" QMAKE_TARGET_COMPANY = "wh201906" diff --git a/README.md b/README.md index 228b2de..07e6c72 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # Proxmark3GUI -A GUI for [Proxmark3](https://github.com/Proxmark/proxmark3) client +![downloads](https://img.shields.io/github/downloads/wh201906/Proxmark3GUI/total) + +A cross-platform GUI for [Proxmark3](https://github.com/Proxmark/proxmark3) client [中文](README/doc/README_zh_CN.md) @@ -25,32 +27,53 @@ A GUI for [Proxmark3](https://github.com/Proxmark/proxmark3) client ## Preview ![preview](README/img/preview.png) -more previews [here](README/doc/previews.md) +[more previews](README/doc/previews.md) *** ## About Iceman fork/repo The [Iceman fork/repo](https://github.com/RfidResearchGroup/proxmark3) has more powerful functions like offline sniff. These guys even developed a new hardware called Proxmark3 RDV4 with smart card support. But the official repo and the Iceman repo is not fully compatible. -This GUI was designed for only official repo at first, but I'm trying to make it compatible with Iceman repo. - -Supported functions when using Iceman client: - -[supported functions](README/doc/supported_Iceman.md) +This GUI is compatible with Iceman/RRG repo(tested on v4.9237) *** ## About Compiled Windows clients A cool guy [Gator96100](https://github.com/Gator96100) creates [ProxSpace](https://github.com/Gator96100/ProxSpace) and makes it possible to compile both the firmware and client on Windows. -Also, he makes the [pre-compiled Windows client](http://www.proxmark.org/forum/viewtopic.php?id=3975) so you can download it and run your PM3 client on Windows instantly. +Also, he makes the [pre-compiled Windows client](https://www.proxmarkbuilds.org/) so you can download it and run your PM3 client on Windows instantly. I included his compiled client in my releases so you can use the GUI on the fly, and you can also use the GUI with your prefered client. Great thanks to him. *** +## Build on Linux + + cd ~ + git clone https://github.com/wh201906/Proxmark3GUI.git + cd Proxmark3GUI + mkdir build + cd build + qmake ../ + make + make clean + cp -r ../lang ./ + ./Proxmark3GUI + +*** + ## Update Log: +### V0.1.4 ++ Optimize performance ++ Optimize UI ++ Search available ports automatically ++ Add High-DPI support ++ Support configuring environment variables by script +(Useful when the client requires specific environment variables) ++ All functions are compatible with Iceman/RRG repo(tested on v4.9237) ++ Fix some bugs + ### V0.1.3 + Fix Trailer Decoder + Add feedback when writing selected blocks diff --git a/README/doc/README_zh_CN.md b/README/doc/README_zh_CN.md index 4f04611..7d4605d 100644 --- a/README/doc/README_zh_CN.md +++ b/README/doc/README_zh_CN.md @@ -1,5 +1,7 @@ # Proxmark3GUI -一个自制的[Proxmark3](https://github.com/Proxmark/proxmark3) GUI +![downloads](https://img.shields.io/github/downloads/wh201906/Proxmark3GUI/total) + +一个自制的[Proxmark3](https://github.com/Proxmark/proxmark3) GUI,可在Windows/Linux系统下运行 [English](../../README.md) @@ -30,23 +32,47 @@ *** ## 关于冰人版 - 这个GUI一开始是针对官方版本做的,现在正在尽力让它兼容冰人版的功能 - (没钱买RDV4也没钱买两台PM3,测一次冰人就要烧一次固件 qwq) + [冰人版](https://github.com/RfidResearchGroup/proxmark3)(Iceman/RRG)的客户端和固件更新更为激进,相比官方版具有更多的功能 + 此GUI所有功能均兼容冰人版(在v4.9237上测试通过) - [已支持功能](../doc/supported_Iceman.md) *** ## 关于预编译Windows客户端 -一个国外大佬 [Gator96100](https://github.com/Gator96100) 做了个 [ProxSpace](https://github.com/Gator96100/ProxSpace) 以便在Windows平台上编译PM3固件和客户端,他还把自己编译好的客户端放到了[论坛](http://www.proxmark.org/forum/viewtopic.php?id=3975)里面 -文件都是放到Google Drive上面的,国内网络无法访问,所以我在release版本里面放了个带预编译客户端版本的GUI。这个GUI也可以搭配你自己的客户端使用 +一位国外大佬 [Gator96100](https://github.com/Gator96100) 做了个 [ProxSpace](https://github.com/Gator96100/ProxSpace) 以便在Windows平台上编译PM3固件和客户端,他还把自己编译好的客户端放到了[网站](https://www.proxmarkbuilds.org/)上 +release页面中有含客户端的GUI。这个GUI也可以搭配你自己的客户端使用 (本来打算在CSDN下载里面放几个最新版客户端的,结果不能把下载币改为0) 感谢大佬 *** +## 在Linux系统下编译 + + cd ~ + git clone https://github.com/wh201906/Proxmark3GUI.git + cd Proxmark3GUI + mkdir build + cd build + qmake ../ + make + make clean + cp -r ../lang ./ + ./Proxmark3GUI + +*** + ## 更新日志: +### V0.1.4 ++ 优化性能 ++ 优化用户界面 ++ 自动搜索可用端口 ++ 支持高分屏 ++ 可通过外部脚本配置环境变量 +(在客户端需要配置环境变量时很有用) ++ 全功能兼容冰人版(在v4.9237上测试通过) ++ 修复部分bug + ### V0.1.3 + 修复访问控制位解码器 + 写多个块时显示写入结果 diff --git a/README/doc/supported_Iceman.md b/README/doc/supported_Iceman.md deleted file mode 100644 index 70dbef0..0000000 --- a/README/doc/supported_Iceman.md +++ /dev/null @@ -1,9 +0,0 @@ -## About Iceman fork/repo - -The [Iceman fork/repo](https://github.com/RfidResearchGroup/proxmark3) has more powerful functions like offline sniff. These guys even developed a new hardware called Proxmark3 RDV4 with smart card support. But the official repo and the Iceman repo is not fully compatible. This GUI was designed for only official repo at first, but I'm trying to make it compatible with Iceman repo. -Supported functions when using Iceman client: -+ Command Line -+ Mifare Card info -+ Mifare Check default keys -+ Mifare Nested Attack -+ Mifare Read/Write \ No newline at end of file diff --git a/README/img/preview.png b/README/img/preview.png index 0c26557..ad618be 100644 Binary files a/README/img/preview.png and b/README/img/preview.png differ diff --git a/README/img/preview_zh_CN.png b/README/img/preview_zh_CN.png index 119f702..cb23753 100644 Binary files a/README/img/preview_zh_CN.png and b/README/img/preview_zh_CN.png differ diff --git a/common/myeventfilter.cpp b/common/myeventfilter.cpp new file mode 100644 index 0000000..b717ca3 --- /dev/null +++ b/common/myeventfilter.cpp @@ -0,0 +1,13 @@ +#include "myeventfilter.h" + +MyEventFilter::MyEventFilter(QEvent::Type filter) +{ + targetEventType = filter; +} + +bool MyEventFilter::eventFilter(QObject *obj, QEvent *event) +{ + if(event->type() == targetEventType) + emit eventHappened(obj, *event); + return QObject::eventFilter(obj, event); +} diff --git a/common/myeventfilter.h b/common/myeventfilter.h new file mode 100644 index 0000000..3ab624a --- /dev/null +++ b/common/myeventfilter.h @@ -0,0 +1,22 @@ +#ifndef MYEVENTFILTER_H +#define MYEVENTFILTER_H + +#include +#include + +class MyEventFilter : public QObject +{ + Q_OBJECT + +public: + explicit MyEventFilter(QEvent::Type filter); +protected: + bool eventFilter(QObject *obj, QEvent *event) override; + +signals: + void eventHappened(QObject* obj_addr, QEvent& event); +private: + QEvent::Type targetEventType; +}; + +#endif // MYEVENTFILTER_H diff --git a/common/pm3process.cpp b/common/pm3process.cpp index 08aef5a..a80397c 100644 --- a/common/pm3process.cpp +++ b/common/pm3process.cpp @@ -14,17 +14,22 @@ PM3Process::PM3Process(QThread* thread, QObject* parent): QProcess(parent) connect(this, &PM3Process::readyRead, this, &PM3Process::onReadyRead); } -void PM3Process::connectPM3(const QString path, const QString port) +void PM3Process::connectPM3(const QString& path, const QString& port, const QStringList args) { QString result; - Util::ClientType clientType = Util::CLIENTTYPE_OFFICIAL; + Util::ClientType clientType; setRequiringOutput(true); + // stash for reconnect + currPath = path; + currPort = port; + currArgs = args; + // using "-f" option to make the client output flushed after every print. - start(path, QStringList() << port << "-f", QProcess::Unbuffered | QProcess::ReadWrite); + start(path, args, QProcess::Unbuffered | QProcess::ReadWrite); if(waitForStarted(10000)) { - waitForReadyRead(1000); + waitForReadyRead(10000); setRequiringOutput(false); result = *requiredOutput; if(result.indexOf("[=]") != -1) @@ -32,10 +37,17 @@ void PM3Process::connectPM3(const QString path, const QString port) clientType = Util::CLIENTTYPE_ICEMAN; setRequiringOutput(true); write("hw version\r\n"); - waitForReadyRead(1000); - result = *requiredOutput; + for(int i = 0; i < 10; i++) + { + waitForReadyRead(200); + result += *requiredOutput; + } setRequiringOutput(false); } + else + { + clientType = Util::CLIENTTYPE_OFFICIAL; + } if(result.indexOf("os: ") != -1) // make sure the PM3 is connected { emit changeClientType(clientType); @@ -50,6 +62,11 @@ void PM3Process::connectPM3(const QString path, const QString port) } } +void PM3Process::reconnectPM3() +{ + connectPM3(currPath, currPort, currArgs); +} + void PM3Process::setRequiringOutput(bool st) { isRequiringOutput = st; @@ -93,7 +110,6 @@ void PM3Process::testThread() qDebug() << "PM3:" << QThread::currentThread(); } - qint64 PM3Process::write(QString data) { return QProcess::write(data.toLatin1()); @@ -111,3 +127,10 @@ void PM3Process::onReadyRead() } } + +void PM3Process::setProcEnv(const QStringList* env) +{ +// qDebug() << "passed Env List" << *env; + this->setEnvironment(*env); +// qDebug() << "final Env List" << processEnvironment().toStringList(); +} diff --git a/common/pm3process.h b/common/pm3process.h index 093ebfd..b2a6631 100644 --- a/common/pm3process.h +++ b/common/pm3process.h @@ -8,6 +8,7 @@ #include #include #include +#include #include "util.h" @@ -21,9 +22,11 @@ public: void testThread(); public slots: - void connectPM3(const QString path, const QString port); - void setSerialListener(const QString &name, bool state); + void connectPM3(const QString& path, const QString& port, const QStringList args); + void setSerialListener(const QString& name, bool state); qint64 write(QString data); + void reconnectPM3(); + void setProcEnv(const QStringList* env); private slots: void onTimeout(); void onReadyRead(); @@ -33,9 +36,13 @@ private: void setRequiringOutput(bool st);// It only works in this class now QTimer* serialListener; QSerialPortInfo* portInfo; + QString currPath; + QString currPort; + QStringList currArgs; + signals: - void PM3StatedChanged(bool st, QString info = ""); - void newOutput(QString output); + void PM3StatedChanged(bool st, const QString& info = ""); + void newOutput(const QString& output); void changeClientType(Util::ClientType); }; diff --git a/common/util.cpp b/common/util.cpp index 3d73cc3..9e360b4 100644 --- a/common/util.cpp +++ b/common/util.cpp @@ -1,15 +1,16 @@ #include "util.h" +Util::ClientType Util::clientType = CLIENTTYPE_OFFICIAL; + Util::Util(QObject *parent) : QObject(parent) { isRequiringOutput = false; requiredOutput = new QString(); timeStamp = QTime::currentTime(); - this->clientType = CLIENTTYPE_OFFICIAL; qRegisterMetaType("Util::ClientType"); } -void Util::processOutput(QString output) +void Util::processOutput(const QString& output) { // qDebug() << "Util::processOutput:" << output; if(isRequiringOutput) @@ -20,30 +21,58 @@ void Util::processOutput(QString output) emit refreshOutput(output); } -void Util::execCMD(QString cmd) +void Util::execCMD(const QString& cmd) { - qDebug() << cmd; - emit write(cmd + "\r\n"); + qDebug() << "executing: " << cmd; + if(isRunning) + emit write(cmd + "\r\n"); } -QString Util::execCMDWithOutput(QString cmd, unsigned long waitTime) +QString Util::execCMDWithOutput(const QString& cmd, ReturnTrigger trigger) { + // if the trigger is empty, this function will wait trigger.waitTime then return all outputs during the wait time. + // otherwise, this function will return empty string if no trigger is detected, or return outputs if any trigger is detected. + // the waitTime will be refreshed if the client have new outputs + bool isResultFound = false; + QRegularExpression re; + re.setPatternOptions(QRegularExpression::DotMatchesEverythingOption); + + if(!isRunning) + return ""; QTime currTime = QTime::currentTime(); - QTime targetTime = QTime::currentTime().addMSecs(waitTime); + QTime targetTime = QTime::currentTime().addMSecs(trigger.waitTime); isRequiringOutput = true; requiredOutput->clear(); execCMD(cmd); while(QTime::currentTime() < targetTime) { + if(!isRunning) + break; QApplication::processEvents(); - if(timeStamp > currTime) +// qDebug() << "currOutput:" << *requiredOutput; + for(QString otpt : trigger.expectedOutputs) + { + re.setPattern(otpt); + isResultFound = re.match(*requiredOutput).hasMatch(); + if(isResultFound) + { + qDebug() << "output Matched: " << *requiredOutput; + break; + } + } + if(isResultFound) + { + delay(200); + break; + } + if(timeStamp > currTime) //has new output { currTime = timeStamp; - targetTime = timeStamp.addMSecs(waitTime); + targetTime = timeStamp.addMSecs(trigger.waitTime); } } isRequiringOutput = false; - return *requiredOutput; + return (isResultFound || trigger.expectedOutputs.isEmpty() ? *requiredOutput : ""); } void Util::delay(unsigned int msec) @@ -52,12 +81,42 @@ void Util::delay(unsigned int msec) while(QTime::currentTime() < timer) QApplication::processEvents(QEventLoop::AllEvents, 100); } + Util::ClientType Util::getClientType() { - return this->clientType; + return Util::clientType; } void Util::setClientType(Util::ClientType clientType) { - this->clientType = clientType; + Util::clientType = clientType; +} + +void Util::setRunningState(bool st) +{ + this->isRunning = st; +} + +bool Util::chooseLanguage(QSettings* guiSettings, QMainWindow* window) +{ + // make sure the GUISettings is not in any group + QSettings* langSettings = new QSettings("lang/languages.ini", QSettings::IniFormat); + QMap langMap; + langSettings->setIniCodec("UTF-8"); + langSettings->beginGroup("Languages"); + QStringList langList = langSettings->allKeys(); + for(int i = 0; i < langList.size(); i++) + langMap.insert(langSettings->value(langList[i]).toString(), langList[i]); + langSettings->endGroup(); + delete langSettings; + bool isOk = false; + QString selectedText = QInputDialog::getItem(window, "", "Choose a language:", langMap.keys(), 0, false, &isOk); + if(isOk) + { + guiSettings->beginGroup("lang"); + guiSettings->setValue("language", langMap[selectedText]); + guiSettings->endGroup(); + guiSettings->sync(); + } + return isOk; } diff --git a/common/util.h b/common/util.h index adc9291..fed751e 100644 --- a/common/util.h +++ b/common/util.h @@ -9,6 +9,10 @@ #include #include #include +#include +#include +#include +#include class Util : public QObject { @@ -20,26 +24,51 @@ public: CLIENTTYPE_ICEMAN, }; + struct ReturnTrigger + { + unsigned long waitTime; + QStringList expectedOutputs; + ReturnTrigger(unsigned long time) + { + waitTime = time; + expectedOutputs = QStringList(); + } + ReturnTrigger(const QStringList& outputs) + { + waitTime = 10000; + expectedOutputs = outputs; + } + ReturnTrigger(unsigned long time, const QStringList& outputs) + { + waitTime = time; + expectedOutputs = outputs; + } + }; + Q_ENUM(Util::ClientType) explicit Util(QObject *parent = nullptr); - void execCMD(QString cmd); - QString execCMDWithOutput(QString cmd, unsigned long waitTime = 2000); + void execCMD(const QString& cmd); + QString execCMDWithOutput(const QString& cmd, ReturnTrigger trigger = 10000); void delay(unsigned int msec); - ClientType getClientType(); + static ClientType getClientType(); + static const int rawTabIndex = 1; + static bool chooseLanguage(QSettings *guiSettings, QMainWindow *window); public slots: - void processOutput(QString output); - void setClientType(Util::ClientType clientType); + void processOutput(const QString& output); + static void setClientType(Util::ClientType clientType); + void setRunningState(bool st); private: bool isRequiringOutput; + bool isRunning; QString* requiredOutput; QTime timeStamp; - ClientType clientType; + static ClientType clientType; signals: void refreshOutput(const QString& output); - void write(QString data); + void write(QString data); // connected to PM3Process::write(QString data); }; #endif // UTIL_H diff --git a/lang/en_US.qm b/lang/en_US.qm new file mode 100644 index 0000000..9dad8df Binary files /dev/null and b/lang/en_US.qm differ diff --git a/lang/en_US.ts b/lang/en_US.ts index b881d0e..f73b0da 100644 --- a/lang/en_US.ts +++ b/lang/en_US.ts @@ -45,72 +45,102 @@ - + u - + UID 4 or 7 bytes. If not specified, the UID 4B from emulator memory will be used - + + --atqa + + + + + Provide explicit ATQA (2 bytes) + + + + + --sak + + + + n - + Automatically exit simulation after <numreads> blocks have been read by reader. 0 = infinite - + i - + Interactive, means that console will not be returned until simulation finishes or is aborted - + x - + Crack, performs the 'reader attack', nr/ar attack against a legitimate reader, fishes out the key(s) - + e - - set keys found from 'reader attack' to emulator memory (implies x and i) + + set keys found from 'reader attack' to emulator memory (implies x(--crack) and i) - + + -v + + + + + verbose output + + + + f - + + Provide explicit SAK (1 byte) + + + + get UIDs to use for 'reader attack' from file 'f <filename.txt>' (implies x and i) - + r - + Generate random nonces instead of sequential nonces. Standard reader attack won't work with this option, only moebius attack works @@ -285,559 +315,703 @@ It could make the whole sector blocked irreversibly! - - Path: - - - - - Refresh - - - - + Connect - + Disconnect - + Mifare - + Select Trailer - + Card Type - + MINI 320 - + 1K 1024 - + 2K 2048 - + 4K 4096 - + File - - + + Load - - + + Save - - + + Data - + Key - + Attack - + Card Info - + Check Default - + Nested - + Hardnested - - Read/Write + + Darkside - - Block: + + Read/Write - Key: + Block: - - Key Type: + + Key: - - Snoop + + Key Type: - + List Data - + Data: - + Normal(Require Password) - + Dump - + Restore - + Chinese Magic Card(Without Password) - + Lock UFUID Card - - + + About UID Card - + Set Parameter - + Wipe - - + + Simulate - - + + Clear - + + Client Path: + + + + + Port: + + + + + Refresh Ports + + + + Select All - + KeyBlocks->Key - + KeyBlocks<-Key - + Fill Keys - + Trailer Decoder - + Set Fonts - - + + Read One - - + + Write One - - - + + + Read Selected - - - + + + Write Selected - - + + Sniff - - RawCommand + + Sniff(14a) + + + + + LF/Data - + LF Config + + + + + Frequency + + + + + 125k + + + + + 134k + + + + + BitRate: + + + + + Decimation: + + + + + Averaging: + + + + + Threshold: + + + + + Skips: + + + + + Get + + + + + Set + + + + + T55xx + + + + + RawCommand + + + + + History: - + ClearHistory - + Send - + ClearOutput - - - - - - - - - - - + + Settings + + + + + Client + + + + + Preload script path: + + + + + Note: +If the client requires some enviroment variables, you can make a script file(*.bat on Windows or *.sh on Linux) to configure them, +then put the path of the script there + + + + + Start arguments + + + + + <port> -f + + + + + Note: +-f is necessary because the GUI need to handle the output in time +In some cases the arguments should be set to "-p /dev/<port> -f" +or "-p <port> -f" + + + + + Keep buttons enabled even the client is running or disconnected + + + + + GUI + + + + + Language: + + + + + Choose Language + + + + + (Restart this app to use new language) + + + + + + + + + + + + + + Info - + Plz choose a port first - + Connected - - - + + Not Connected - + Binary Data Files(*.bin *.dump);;Text Data Files(*.txt *.eml);;All Files(*.*) - - - + + + Failed to open - + Continue? - + Check Update - + Some of the data and key will be cleared. - + Plz select the font of data widget and key widget - + Data must consists of 32 Hex symbols(Whitespace is allowed) - - + + Key must consists of 12 Hex symbols(Whitespace is allowed) - + Plz select the data file: - + Plz select the key file: - + Binary Key Files(*.bin *.dump);;Binary Data Files(*.bin *.dump);;All Files(*.*) - + Plz select the location to save data file: - + Binary Data Files(*.bin *.dump);;Text Data Files(*.txt *.eml) - - - + + + Failed to save to - + Plz select the location to save key file: - + Binary Key Files(*.bin *.dump) - + Normally, the Block 0 of a typical Mifare card, which contains the UID, is locked during the manufacture. Users cannot write anything to Block 0 or set a new UID to a normal Mifare card. - + Chinese Magic Cards(aka UID Cards) are some special cards whose Block 0 are writeable. And you can change UID by writing to it. - + There are two versions of Chinese Magic Cards, the Gen1 and the Gen2. - + Gen1: - + also called UID card in China. It responses to some backdoor commands so you can access any blocks without password. The Proxmark3 has a bunch of related commands(csetblk, cgetblk, ...) to deal with this type of card, and my GUI also support these commands. - + Gen2: - + doesn't response to the backdoor commands, which means that a reader cannot detect whether it is a Chinese Magic Card or not by sending backdoor commands. - + There are some types of Chinese Magic Card Gen2. - + CUID Card: - + the Block 0 is writeable, you can write to this block repeatedly by normal wrbl command. - + (hf mf wrbl 0 A FFFFFFFFFFFF <the data you want to write>) - + FUID Card: - + you can only write to Block 0 once. After that, it seems like a typical Mifare card(Block 0 cannot be written to). - + (some readers might try changing the Block 0, which could detect the CUID Card. In that case, you should use FUID card.) - + UFUID Card: - + It behaves like a CUID card(or UID card? I'm not sure) before you send some special command to lock it. Once it is locked, you cannot change its Block 0(just like a typical Mifare card). - + Seemingly, these Chinese Magic Cards are more easily to be compromised by Nested Attack(it takes little time to get an unknown key). - + Plz select the trace file: - + Trace Files(*.trc);;All Files(*.*) - + Plz select the location to save trace file: - + Trace Files(*.trc) - - + + Idle - - + + Stop + + + + + Sec - + Blk - + KeyA - + KeyB - + HW Version: - + PM3: - + State: - + Running @@ -845,31 +1019,56 @@ It could make the whole sector blocked irreversibly! Mifare - + Success! - - - - + + + + + + + Info - + Plz provide at least one known key - - + + Failed! - + + The Access Bits is invalid! +It could make the whole sector blocked irreversibly! +Continue to write? + + + + + Successful! + + + + + Failed to write to these blocks: + + + + + Select them? + + + + Failed to read card. diff --git a/lang/languages.ini b/lang/languages.ini new file mode 100644 index 0000000..d9a11e4 --- /dev/null +++ b/lang/languages.ini @@ -0,0 +1,3 @@ +[Languages] +en_US=English +zh_CN=简体中文 \ No newline at end of file diff --git a/lang/zh_CN.qm b/lang/zh_CN.qm new file mode 100644 index 0000000..3edc62a Binary files /dev/null and b/lang/zh_CN.qm differ diff --git a/lang/zh_CN.ts b/lang/zh_CN.ts index 316cf77..3efde9e 100644 --- a/lang/zh_CN.ts +++ b/lang/zh_CN.ts @@ -45,72 +45,102 @@ 模拟 - + u - + UID 4 or 7 bytes. If not specified, the UID 4B from emulator memory will be used 4或7字节的UID,如果不指定,则使用模拟器内存中的4字节UID - + + --atqa + + + + + Provide explicit ATQA (2 bytes) + 指定ATQA(2个字节) + + + + --sak + + + + + Provide explicit SAK (1 byte) + 指定SAK(1个字节) + + + n - + Automatically exit simulation after <numreads> blocks have been read by reader. 0 = infinite 在读卡器读取<n>个块后自动退出模拟,n为0或不指定时永远不退出 - + i - + Interactive, means that console will not be returned until simulation finishes or is aborted 交互模式,勾选后PM3客户端将在模拟完成或者模拟中断后才可继续使用 - + x - + Crack, performs the 'reader attack', nr/ar attack against a legitimate reader, fishes out the key(s) 破解,对读卡器进行攻击,通过nr/ar攻击来钓出密码(无卡嗅探) - + e - - set keys found from 'reader attack' to emulator memory (implies x and i) - 在获得密码后自动将密码写入模拟器内存(自动勾选x和i) + + set keys found from 'reader attack' to emulator memory (implies x(--crack) and i) + 在获得密码后自动将密码写入模拟器内存(自动勾选x(--crack)和i) - + + -v + + + + + verbose output + 更多输出内容 + + + f - + get UIDs to use for 'reader attack' from file 'f <filename.txt>' (implies x and i) 从<filename.txt>当中获取用于破解读卡器的UID(批量模拟)(自动勾选x和i) - + r - + Generate random nonces instead of sequential nonces. Standard reader attack won't work with this option, only moebius attack works 生成随机nonce而不是顺序的nonce,这种情况下PM3将不对读卡器进行标准攻击,只进行moebius攻击 @@ -125,7 +155,7 @@ UID: - + 卡号: @@ -164,7 +194,7 @@ Trailer Data: (like "FF0780" or "FF 07 80") - 输入Access Bits + 输入控制位数据 (形如“FF0780”或“FF 07 80”) @@ -195,22 +225,22 @@ Data Block Permission: - 数据Block访问权限: + 数据块访问权限: Block0 - + 块0 Block1 - + 块1 Block2 - + 块2 @@ -242,17 +272,17 @@ KeyA - + 密钥A Access Bits - Access Bits + 控制位 KeyB - + 密钥B @@ -289,591 +319,766 @@ It could make the whole sector blocked irreversibly! - - Path: - 路径: - - - - Refresh - 刷新端口 - - - + Connect 连接 - + Disconnect 断开 - + Mifare Mifare(IC)卡 - + Select Trailer 选中密码块 - + Card Type - 卡类型 + 卡片类型 - + MINI 320 - + 1K 1024 - + 2K 2048 - + 4K 4096 - + File 文件 - - + + Load 加载 - - + + Save 保存 - - + + Data - + 数据 - + Key - + 密钥 - + Attack 破解 - + Card Info - 读卡片信息 + 读卡信息 - + Check Default 验证默认密码 - + Nested Nested攻击 - + Hardnested Hardested攻击 - + + Darkside + Darkside攻击 + + + Read/Write 读/写 - + Block: - + 块: - + Key: - + 密钥: - + Key Type: - Key类型: + 密钥类型: - - Snoop - 嗅探(Snoop) - - - + List Data 列出嗅探数据 - + Data: - + 数据: - + Normal(Require Password) 普通卡(需要密码) - + Dump Dump命令 - + Restore Restore命令 - + Chinese Magic Card(Without Password) UID卡(不需要密码) - + Lock UFUID Card 锁定UFUID卡 - - + + About UID Card 关于UID卡 - + Set Parameter 设置卡参数 - + Wipe 擦除 - - + + Simulate 模拟 - - + + Clear 清空 - + + Client Path: + 客户端路径: + + + + Port: + 端口: + + + + Refresh Ports + 刷新端口 + + + Select All 全选 - + KeyBlocks->Key 密码区->密码 - + KeyBlocks<-Key 密码区<-密码 - + Fill Keys 填充密码 - + Trailer Decoder Trailer解码 - + Set Fonts 设置字体 - - + + Read One - 读取单个区 + 读取单个块 - - + + Write One - 写入单个区 + 写入单个块 - - - + + + Read Selected 读取选中块 - - - + + + Write Selected 写入选中块 - - + + Sniff 嗅探 - + + Sniff(14a) + 嗅探(14a) + + + + LF/Data + + + + + LF Config + + + + + Frequency + + + + + 125k + + + + + 134k + + + + + BitRate: + + + + + Decimation: + + + + + Averaging: + + + + + Threshold: + + + + + Skips: + + + + + Get + + + + + Set + + + + + T55xx + + + + RawCommand 原始命令 - - + + History: 命令历史: - + ClearHistory 清空历史 - + Send 发送 - + ClearOutput 清空输出 - - - - - - - - - - - + + Settings + 设置 + + + + Client + 客户端 + + + + Preload script path: + 预加载脚本路径: + + + + Note: +If the client requires some enviroment variables, you can make a script file(*.bat on Windows or *.sh on Linux) to configure them, +then put the path of the script there + 注意: +如果客户端需要配置环境变量才能正常运行,可以将配置环境变量所需的脚本文件(Windows系统内为*.bat,linux系统内为*.sh)路径填入此处 + + + + Start arguments + 启动参数 + + + + <port> -f + + + + + Note: +-f is necessary because the GUI need to handle the output in time +In some cases the arguments should be set to "-p /dev/<port> -f" +or "-p <port> -f" + 注意: +-f选项用于使客户端实时返回命令回显,必须添加 +部分情况下启动参数需设置为"-p /dev/<port> -f" +或"-p <port> -f" + + + + Language: + 语言: + + + + Choose Language + 选择语言 + + + + (Restart this app to use new language) + (重启此程序以使用新语言) + + + + Keep buttons enabled even the client is running or disconnected + 保持所有按钮可点击,即使未连接客户端或有任务正在运行 + + + + GUI + 图形化界面 + + + + + + + + + + + + + Info 信息 - + Plz choose a port first 请先选择端口 - + Connected 已连接 - - - + + Not Connected 未连接 - + Binary Data Files(*.bin *.dump);;Text Data Files(*.txt *.eml);;All Files(*.*) - 二进制Data文件(*.bin *.dump);;文本Data文件(*.txt *.eml);;所有文件(*.*) + 二进制数据文件(*.bin *.dump);;文本数据文件(*.txt *.eml);;所有文件(*.*) - - - + + + Failed to open 无法打开 - + Continue? 确定? - + Check Update 检查更新 - + Some of the data and key will be cleared. 部分数据和密码将被清除 - + Plz select the font of data widget and key widget - 请选择Data窗口和Key窗口的字体 + 请选择数据窗口和密钥窗口的字体 - + Data must consists of 32 Hex symbols(Whitespace is allowed) - Data必须由32个十六进制字符组成(中间可含有空格) + 数据必须由32个十六进制字符组成(中间可含有空格) - - + + Key must consists of 12 Hex symbols(Whitespace is allowed) - Key必须由12个十六进制字符组成(中间可含有空格) + 密钥必须由12个十六进制字符组成(中间可含有空格) - + Plz select the data file: - 请选择data文件: + 请选择数据文件: - + Plz select the key file: - 请选择key文件: + 请选择密钥文件: - + Binary Key Files(*.bin *.dump);;Binary Data Files(*.bin *.dump);;All Files(*.*) - 二进制Key文件(*.bin *.dump)二进制Data文件(*.bin *.dump);;所有文件(*.*) + 二进制密钥文件(*.bin *.dump)二进制密钥文件(*.bin *.dump);所有文件(*.*) - + Plz select the location to save data file: - 请选择文件保存的位置: + 请选择数据文件保存的位置: - + Binary Data Files(*.bin *.dump);;Text Data Files(*.txt *.eml) - 二进制Data文件(*.bin *.dump);;文本Data文件(*.txt *.eml) + 二进制数据文件(*.bin *.dump);文本数据文件(*.txt *.eml) - - - + + + Failed to save to 无法保存至 - + Plz select the location to save key file: - 请选择key文件保存的位置: + 请选择密钥文件保存的位置: - + Binary Key Files(*.bin *.dump) - 二进制Key文件(*.bin *.dump) + 二进制密码文件(*.bin *.dump) - + Normally, the Block 0 of a typical Mifare card, which contains the UID, is locked during the manufacture. Users cannot write anything to Block 0 or set a new UID to a normal Mifare card. - 普通Mifare卡的Block0无法写入,UID也不能更改 + 普通Mifare卡的块0无法写入,卡号也不能更改 - + Chinese Magic Cards(aka UID Cards) are some special cards whose Block 0 are writeable. And you can change UID by writing to it. - UID卡(在国外叫Chinese Magic Card)的Block0可写,UID可变 + UID卡(在国外叫Chinese Magic Card)的块0可写,卡号可变。 - + There are two versions of Chinese Magic Cards, the Gen1 and the Gen2. 国外把UID卡分为Chinese Magic Card Gen1和Gen2 - + Gen1: - + also called UID card in China. It responses to some backdoor commands so you can access any blocks without password. The Proxmark3 has a bunch of related commands(csetblk, cgetblk, ...) to deal with this type of card, and my GUI also support these commands. 指通常所说的UID卡,可以通过后门指令直接读写块而无需密码,在PM3和此GUI中有特殊命令处理这类卡片 - + Gen2: - + doesn't response to the backdoor commands, which means that a reader cannot detect whether it is a Chinese Magic Card or not by sending backdoor commands. 这个叫法在国内比较罕见,在国外指CUID/FUID/UFUID这类对后门指令不响应的卡(防火墙卡) - + There are some types of Chinese Magic Card Gen2. 以下是Gen2卡的详细介绍 - + CUID Card: CUID卡: - + the Block 0 is writeable, you can write to this block repeatedly by normal wrbl command. - 可通过普通的写块命令来写Block0,可重复擦写 + 可通过普通的写块命令来写块0,可重复擦写 - + (hf mf wrbl 0 A FFFFFFFFFFFF <the data you want to write>) (hf mf wrbl 0 A FFFFFFFFFFFF <待写入数据>) - + FUID Card: FUID卡: - + you can only write to Block 0 once. After that, it seems like a typical Mifare card(Block 0 cannot be written to). - Block0只能写入一次 + 块0只能写入一次 - + (some readers might try changing the Block 0, which could detect the CUID Card. In that case, you should use FUID card.) (更高级的穿防火墙卡,可以过一些能识别出CUID卡的读卡器) - + UFUID Card: UFUID卡: - + It behaves like a CUID card(or UID card? I'm not sure) before you send some special command to lock it. Once it is locked, you cannot change its Block 0(just like a typical Mifare card). - 锁卡前和普通UID/CUID卡一样可以反复读写Block0,用特殊命令锁卡后就和FUID卡一样了 + 锁卡前和普通UID/CUID卡一样可以反复读写块0,用特殊命令锁卡后就和FUID卡一样了 - + Seemingly, these Chinese Magic Cards are more easily to be compromised by Nested Attack(it takes little time to get an unknown key). 所有UID卡都似乎更容易被Nested攻击破解 - + Plz select the trace file: 请选择trace文件: - + Trace Files(*.trc);;All Files(*.*) Trace文件(*.trc);;所有文件(*.*) - + Plz select the location to save trace file: 请选择trace文件保存的位置: - + Trace Files(*.trc) Trace文件(*.trc) - - + + Idle 空闲 - - + + Stop + 停止 + + + + Sec - + 扇区 - + Blk - + - + KeyA - + 密钥A - + KeyB - + 密钥B - + HW Version: 固件版本: - + PM3: 连接状态: - + State: 运行状态: - + Running - 运行中 + 正在运行 Mifare - + Success! 成功! - - - - + + + + + + + Info 信息 - + Plz provide at least one known key 请至少提供一个已知密码 - - + + Failed! 失败! - + + The Access Bits is invalid! +It could make the whole sector blocked irreversibly! +Continue to write? + 控制位无效! +使用该控制位可能导致目标扇区损坏且无法恢复! +确定要写入吗? + + + + Successful! + 成功! + + + + Failed to write to these blocks: + 写入以下块失败: + + + + Select them? + 选中这些块? + + + Failed to read card. 读卡失败。 diff --git a/main.cpp b/main.cpp index 999eb0b..4f87d81 100644 --- a/main.cpp +++ b/main.cpp @@ -4,56 +4,50 @@ #include #include #include -#include +#include int main(int argc, char *argv[]) { + QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8")); QApplication a(argc, argv); MainWindow w; + QSettings* settings = new QSettings("GUIsettings.ini", QSettings::IniFormat); + settings->setIniCodec("UTF-8"); settings->beginGroup("lang"); - QVariant lang = settings->value("language", "null"); - if(lang == "null") + QString currLang = settings->value("language", "").toString(); + settings->endGroup(); + if(currLang == "") { -#ifdef Q_OS_WIN - lang = "lang/en_US.qm"; -#else - lang = "lang/en_US.ts"; -#endif - QStringList langList; - langList.append("English"); - langList.append("简体中文"); - QString seletedText = QInputDialog::getItem(&w, "", "Choose a language:", langList, 0, false); - if(seletedText == "English") + if(Util::chooseLanguage(settings, &w)) { -#ifdef Q_OS_WIN - lang = "lang/en_US.qm"; -#else - lang = "lang/en_US.ts"; -#endif + settings->beginGroup("lang"); + currLang = settings->value("language", "").toString(); + settings->endGroup(); } - else if(seletedText == "简体中文") - { + else + currLang = "en_US"; + } + currLang = "lang/" + currLang; #ifdef Q_OS_WIN - lang = "lang/zh_CN.qm"; + currLang += ".qm"; #else - lang = "lang/zh_CN.ts"; + currLang += ".ts";; #endif - } - } QTranslator* translator = new QTranslator(&w); - if(translator->load(lang.toString())) + if(translator->load(currLang)) { a.installTranslator(translator); - settings->setValue("language", lang); } else { - QMessageBox::information(&w, "Error", "Can't load " + lang.toString() + " as translation file."); + QMessageBox::information(&w, "Error", "Can't load " + currLang + " as translation file."); } - settings->endGroup(); delete settings; w.initUI(); w.show(); return a.exec(); } + + diff --git a/module/mifare.cpp b/module/mifare.cpp index 5c0f63e..064a9ae 100644 --- a/module/mifare.cpp +++ b/module/mifare.cpp @@ -6,7 +6,8 @@ const Mifare::CardType Mifare::card_mini = 5, 20, {4, 4, 4, 4, 4}, - {0, 4, 8, 12, 16} + {0, 4, 8, 12, 16}, + "mini" }; const Mifare::CardType Mifare::card_1k = { @@ -14,7 +15,8 @@ const Mifare::CardType Mifare::card_1k = 16, 64, {4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4}, - {0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60} + {0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60}, + "1k" }; const Mifare::CardType Mifare::card_2k = { @@ -22,7 +24,8 @@ const Mifare::CardType Mifare::card_2k = 32, 128, {4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4}, - {0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92, 96, 100, 104, 108, 112, 116, 120, 124} + {0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92, 96, 100, 104, 108, 112, 116, 120, 124}, + "2k" }; const Mifare::CardType Mifare::card_4k = { @@ -30,7 +33,8 @@ const Mifare::CardType Mifare::card_4k = 40, 256, {4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 16, 16, 16, 16, 16, 16, 16, 16}, - {0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92, 96, 100, 104, 108, 112, 116, 120, 124, 128, 144, 160, 176, 192, 208, 224, 240} + {0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92, 96, 100, 104, 108, 112, 116, 120, 124, 128, 144, 160, 176, 192, 208, 224, 240}, + "4k" }; const Mifare::AccessType Mifare::dataCondition[8][4] = @@ -79,26 +83,24 @@ Mifare::Mifare(Ui::MainWindow *ui, Util *addr, QWidget *parent): QObject(parent) data_clearKey(); // fill with blank QString data_clearData(); // fill with blank QString dataPattern = new QRegularExpression("([0-9a-fA-F]{2} ){15}[0-9a-fA-F]{2}"); - keyPattern_res = new QRegularExpression("\\|\\d{3}\\|.+?\\|.+?\\|.+?\\|.+?\\|"); - keyPattern = new QRegularExpression("\\|\\d{3}\\|.+?\\|.+?\\|"); + keyPattern_res = new QRegularExpression("\\|\\s*\\d{3}\\s*\\|\\s*.+?\\s*\\|\\s*.+?\\s*\\|\\s*.+?\\s*\\|\\s*.+?\\s*\\|"); + keyPattern = new QRegularExpression("\\|\\s*\\d{3}\\s*\\|\\s*.+?\\s*\\|\\s*.+?\\s*\\|"); } QString Mifare::info(bool isRequiringOutput) { - if(util->getClientType() == Util::CLIENTTYPE_OFFICIAL || util->getClientType() == Util::CLIENTTYPE_ICEMAN) + if(Util::getClientType() == Util::CLIENTTYPE_OFFICIAL || Util::getClientType() == Util::CLIENTTYPE_ICEMAN) { if(isRequiringOutput) { QString result = util->execCMDWithOutput("hf 14a info", 500); - result.replace("UID :", "|||"); - result.replace("ATQA :", "|||"); - result.replace("SAK :", "|||"); - result.replace("TYPE :", "|||"); - QStringList lis = result.split("|||"); - if(lis.length() > 4) + int begin, end; + begin = result.indexOf("UID"); + if(begin != -1) { - qDebug() << lis[1] + lis[2] + lis[3]; - return lis[1] + lis[2] + lis[3]; + end = result.indexOf("SAK", begin); + end = result.indexOf("\n", end); + return result.mid(begin, end - begin + 1); } else return ""; @@ -106,7 +108,7 @@ QString Mifare::info(bool isRequiringOutput) else { util->execCMD("hf 14a info"); - ui->funcTab->setCurrentIndex(1); + ui->funcTab->setCurrentIndex(Util::rawTabIndex); return ""; } } @@ -115,17 +117,17 @@ QString Mifare::info(bool isRequiringOutput) void Mifare::chk() { QRegularExpressionMatch reMatch; - QString result = util->execCMDWithOutput( - "hf mf chk *" - + QString::number(cardType.type) - + " ?", - 1000 + cardType.type * 1000); - qDebug() << result; - + QString result; int offset = 0; QString data; - if(util->getClientType() == Util::CLIENTTYPE_OFFICIAL) + if(Util::getClientType() == Util::CLIENTTYPE_OFFICIAL) { + result = util->execCMDWithOutput( + "hf mf chk *" + + QString::number(cardType.type) + + " ?", + Util::ReturnTrigger(1000 + cardType.sector_size * 200, {"No valid", keyPattern->pattern()})); + qDebug() << result; for(int i = 0; i < cardType.sector_size; i++) { reMatch = keyPattern->match(result, offset); @@ -146,8 +148,13 @@ void Mifare::chk() } } } - else if(util->getClientType() == Util::CLIENTTYPE_ICEMAN) + else if(Util::getClientType() == Util::CLIENTTYPE_ICEMAN) { + result = util->execCMDWithOutput( + "hf mf chk --" + + cardType.typeText, + Util::ReturnTrigger(1000 + cardType.sector_size * 200, {"No valid", keyPattern_res->pattern()})); + qDebug() << "mf_chk_iceman_result" << result; for(int i = 0; i < cardType.sector_size; i++) { reMatch = keyPattern_res->match(result, offset); @@ -178,21 +185,22 @@ void Mifare::nested() QString result; int offset = 0; QString data; - if(util->getClientType() == Util::CLIENTTYPE_OFFICIAL) + if(Util::getClientType() == Util::CLIENTTYPE_OFFICIAL) { result = util->execCMDWithOutput( "hf mf nested " + QString::number(cardType.type) - + " *", 10000); + + " *", + Util::ReturnTrigger(15000, {"Can't found", "\\|000\\|"})); } - else if(util->getClientType() == Util::CLIENTTYPE_ICEMAN) + else if(Util::getClientType() == Util::CLIENTTYPE_ICEMAN) { QString knownKeyInfo = ""; for(int i = 0; i < cardType.sector_size; i++) { if(data_isKeyValid(keyAList->at(i))) { - knownKeyInfo = " " + QString::number(i * 4) + " A " + keyAList->at(i); + knownKeyInfo = " --blk " + QString::number(i * 4) + " -a -k " + keyAList->at(i); break; } } @@ -202,7 +210,7 @@ void Mifare::nested() { if(data_isKeyValid(keyBList->at(i))) { - knownKeyInfo = " " + QString::number(i * 4) + " B " + keyBList->at(i); + knownKeyInfo = " --blk " + QString::number(i * 4) + " -b -k " + keyBList->at(i); break; } } @@ -210,9 +218,10 @@ void Mifare::nested() if(knownKeyInfo != "") { result = util->execCMDWithOutput( - "hf mf nested " - + QString::number(cardType.type) - + knownKeyInfo, 10000); + "hf mf nested --" + + cardType.typeText + + knownKeyInfo, + Util::ReturnTrigger(15000, {"Can't authenticate", keyPattern_res->pattern()})); } else { @@ -248,34 +257,57 @@ void Mifare::hardnested() MF_Attack_hardnestedDialog dialog(cardType.block_size); connect(&dialog, &MF_Attack_hardnestedDialog::sendCMD, util, &Util::execCMD); if(dialog.exec() == QDialog::Accepted) - ui->funcTab->setCurrentIndex(1); + ui->funcTab->setCurrentIndex(Util::rawTabIndex); +} + +void Mifare::darkside() +{ + if(Util::getClientType() == Util::CLIENTTYPE_OFFICIAL) + util->execCMD("hf mf mifare"); + else if(Util::getClientType() == Util::CLIENTTYPE_ICEMAN) + util->execCMD("hf mf darkside"); + + ui->funcTab->setCurrentIndex(Util::rawTabIndex); } void Mifare::sniff() { - util->execCMD("hf mf sniff"); - ui->funcTab->setCurrentIndex(1); + if(Util::getClientType() == Util::CLIENTTYPE_OFFICIAL) + util->execCMD("hf mf sniff"); + else if(Util::getClientType() == Util::CLIENTTYPE_ICEMAN) + util->execCMD("hf sniff"); + + ui->funcTab->setCurrentIndex(Util::rawTabIndex); } -void Mifare::snoop() +void Mifare::sniff14a() { - util->execCMD("hf 14a snoop"); - ui->funcTab->setCurrentIndex(1); + if(Util::getClientType() == Util::CLIENTTYPE_OFFICIAL) + util->execCMD("hf 14a snoop"); + else if(Util::getClientType() == Util::CLIENTTYPE_ICEMAN) + util->execCMD("hf 14a sniff"); + + ui->funcTab->setCurrentIndex(Util::rawTabIndex); } void Mifare::list() { - util->execCMD("hf list mf"); - ui->funcTab->setCurrentIndex(1); + if(Util::getClientType() == Util::CLIENTTYPE_OFFICIAL) + util->execCMD("hf list mf"); + else if(Util::getClientType() == Util::CLIENTTYPE_ICEMAN) + util->execCMD("trace list -t mf"); + + ui->funcTab->setCurrentIndex(Util::rawTabIndex); } QString Mifare::_readblk(int blockId, KeyType keyType, const QString& key, TargetType targetType, int waitTime) { QString data; QString result; - bool isKeyBlock = (blockId < 128 && ((blockId + 1) % 4 == 0)) || ((blockId + 1) % 16 == 0); + QRegularExpressionMatch currMatch; + bool isTrailerBlock = (blockId < 128 && ((blockId + 1) % 4 == 0)) || ((blockId + 1) % 16 == 0); - if(util->getClientType() == Util::CLIENTTYPE_OFFICIAL || util->getClientType() == Util::CLIENTTYPE_ICEMAN) + if(Util::getClientType() == Util::CLIENTTYPE_OFFICIAL || Util::getClientType() == Util::CLIENTTYPE_ICEMAN) { if(targetType == TARGET_MIFARE) { @@ -292,13 +324,14 @@ QString Mifare::_readblk(int blockId, KeyType keyType, const QString& key, Targe + " " + key, waitTime); - if(result.indexOf("isOk:01") != -1) + currMatch = dataPattern->match(result); + if(currMatch.hasMatch()) { - data = dataPattern->match(result).captured().toUpper(); + data = currMatch.captured().toUpper(); data.remove(" "); // when the target block is a key block and the given key type is KeyA, try to check whether the KeyB is valid(by Access Bits) // if the given key type is KeyB, it will never get the KeyA from the key block - if(isKeyBlock && keyType == KEY_A) // in this case, the Access Bits is always accessible + if(isTrailerBlock && keyType == KEY_A) // in this case, the Access Bits is always accessible { data.replace(0, 12, key); QList ACBits = data_getACBits(data.mid(12, 8)); @@ -307,7 +340,7 @@ QString Mifare::_readblk(int blockId, KeyType keyType, const QString& key, Targe data.replace(20, 12, "????????????"); } } - else if(isKeyBlock && keyType == KEY_B) + else if(isTrailerBlock && keyType == KEY_B) { data.replace(20, 12, key);; data.replace(0, 12, "????????????"); // fill the keyA part with ? @@ -322,15 +355,19 @@ QString Mifare::_readblk(int blockId, KeyType keyType, const QString& key, Targe "hf mf cgetblk " + QString::number(blockId), waitTime); - if(result.indexOf("Chinese magic") != -1) + currMatch = dataPattern->match(result); + if(currMatch.hasMatch()) { - data = dataPattern->match(result).captured().toUpper(); + data = currMatch.captured().toUpper(); data.remove(" "); } else data = ""; } - else if(targetType == TARGET_EMULATOR) + } + if(Util::getClientType() == Util::CLIENTTYPE_OFFICIAL) + { + if(targetType == TARGET_EMULATOR) { result = util->execCMDWithOutput( "hf mf eget " @@ -340,6 +377,18 @@ QString Mifare::_readblk(int blockId, KeyType keyType, const QString& key, Targe data.remove(" "); } } + else if(Util::getClientType() == Util::CLIENTTYPE_ICEMAN) + { + if(targetType == TARGET_EMULATOR) + { + result = util->execCMDWithOutput( + "hf mf egetblk " + + QString::number(blockId), + 150); + data = dataPattern->match(result).captured().toUpper(); + data.remove(" "); + } + } return data; } @@ -348,14 +397,14 @@ QStringList Mifare::_readsec(int sectorId, KeyType keyType, const QString& key, QStringList data; QString result, tmp; QRegularExpressionMatch reMatch; - int offset = -1; + int offset = -1; // for targetType == TARGET_EMULATOR for(int i = 0; i < cardType.blk[sectorId]; i++) { data.append(""); } - if(util->getClientType() == Util::CLIENTTYPE_OFFICIAL || util->getClientType() == Util::CLIENTTYPE_ICEMAN) + if(Util::getClientType() == Util::CLIENTTYPE_OFFICIAL || Util::getClientType() == Util::CLIENTTYPE_ICEMAN) { // try to read all blocks together if(targetType == TARGET_MIFARE) @@ -372,7 +421,7 @@ QStringList Mifare::_readsec(int sectorId, KeyType keyType, const QString& key, + " " + key, waitTime); - offset = result.indexOf("isOk:01"); + offset = result.indexOf("isOk:01"); // find successful flag } else if(targetType == TARGET_UID) { @@ -380,7 +429,7 @@ QStringList Mifare::_readsec(int sectorId, KeyType keyType, const QString& key, "hf mf cgetsc " + QString::number(sectorId), waitTime); - offset = result.indexOf("Chinese magic"); + offset = result.indexOf("error") == -1 ? 0 : -1; // find failed flag } if(offset != -1) { @@ -399,7 +448,7 @@ QStringList Mifare::_readsec(int sectorId, KeyType keyType, const QString& key, } // if failed, try to read them seperately. // (when one of the block cannot be read, the rdsc will return nothing, so you need to read the rest of blocks manually) - else if(targetType != TARGET_UID) // if the targetType is Chinese Magic Card, then the result implies the backdoor command is invalid. + else if(targetType == TARGET_UID || targetType == TARGET_EMULATOR) // if the targetType is Chinese Magic Card, then the result implies the backdoor command is invalid. { for(int i = 0; i < cardType.blk[sectorId]; i++) data[i] = _readblk(cardType.blks[sectorId] + i, keyType, key, targetType, waitTime); @@ -536,12 +585,12 @@ bool Mifare::_writeblk(int blockId, KeyType keyType, const QString& key, const Q { QString result; QString input = data.toUpper(); - input.remove(" "); + input.remove(" "); if(data_isDataValid(input) != DATA_NOSPACE) return false; - if(util->getClientType() == Util::CLIENTTYPE_OFFICIAL || util->getClientType() == Util::CLIENTTYPE_ICEMAN) + if(Util::getClientType() == Util::CLIENTTYPE_OFFICIAL || Util::getClientType() == Util::CLIENTTYPE_ICEMAN) { if(targetType == TARGET_MIFARE) { @@ -567,7 +616,7 @@ bool Mifare::_writeblk(int blockId, KeyType keyType, const QString& key, const Q + " " + input, waitTime); - return (result.indexOf("Chinese magic") != -1); + return (result.indexOf("error") == -1); // failed flag } else if(targetType == TARGET_EMULATOR) { @@ -601,6 +650,8 @@ void Mifare::writeSelected(TargetType targetType) { QList failedBlocks; QList selectedBlocks; + bool yes2All = false, no2All = false; + for(int i = 0; i < cardType.block_size; i++) { if(ui->MF_dataWidget->item(i, 1)->checkState() == Qt::Checked) @@ -609,6 +660,29 @@ void Mifare::writeSelected(TargetType targetType) for(int item : selectedBlocks) { bool result = false; + bool isTrailerBlock = (item < 128 && ((item + 1) % 4 == 0)) || ((item + 1) % 16 == 0); + + if(isTrailerBlock && !data_isACBitsValid(dataList->at(item).mid(12, 8))) // trailer block is invalid + { + if(!yes2All && !no2All) + { + QMessageBox::StandardButton choice = QMessageBox::information(parent, tr("Info"), + tr("The Access Bits is invalid!\nIt could make the whole sector blocked irreversibly!\nContinue to write?"), + QMessageBox::Yes | QMessageBox::YesToAll | QMessageBox::No | QMessageBox::NoToAll); + if(choice == QMessageBox::No) + continue; + else if(choice == QMessageBox::YesToAll) + yes2All = true; + else if(choice == QMessageBox::NoToAll) + { + no2All = true; + continue; + } + } + else if(no2All) + continue; + } + if(targetType == TARGET_MIFARE) { result = _writeblk(item, KEY_A, keyAList->at(data_b2s(item)), dataList->at(item), TARGET_MIFARE); @@ -667,23 +741,32 @@ void Mifare::writeSelected(TargetType targetType) void Mifare::dump() { - util->execCMD("hf mf dump"); - ui->funcTab->setCurrentIndex(1); + if(Util::getClientType() == Util::CLIENTTYPE_OFFICIAL || Util::getClientType() == Util::CLIENTTYPE_ICEMAN) + util->execCMD("hf mf dump"); + ui->funcTab->setCurrentIndex(Util::rawTabIndex); } void Mifare::restore() { - util->execCMD("hf mf restore"); - ui->funcTab->setCurrentIndex(1); + if(Util::getClientType() == Util::CLIENTTYPE_OFFICIAL || Util::getClientType() == Util::CLIENTTYPE_ICEMAN) + util->execCMD("hf mf restore"); + ui->funcTab->setCurrentIndex(Util::rawTabIndex); } void Mifare::wipeC() { - util->execCMD( - "hf mf cwipe " - + QString::number(cardType.type) - + " f"); - ui->funcTab->setCurrentIndex(1); + if(Util::getClientType() == Util::CLIENTTYPE_OFFICIAL) + { + util->execCMD( + "hf mf cwipe " + + QString::number(cardType.type) + + " f"); + } + else if(Util::getClientType() == Util::CLIENTTYPE_ICEMAN) + { + util->execCMD("hf mf cwipe"); + } + ui->funcTab->setCurrentIndex(Util::rawTabIndex); } void Mifare::setParameterC() @@ -693,50 +776,78 @@ void Mifare::setParameterC() QMessageBox::information(parent, tr("Info"), tr("Failed to read card.")); else { - QStringList lis = result.split("\r\n"); - lis[0].replace(" ", ""); - lis[1].replace(" ", ""); - lis[2].replace(" ", ""); - MF_UID_parameterDialog dialog(lis[0].toUpper(), lis[1].toUpper(), lis[2].mid(0, 2).toUpper()); + result.replace("\r\n", ""); + result.replace(QRegularExpression("\\[.\\]"), ""); + result.replace("UID", ""); + result.replace("ATQA", ""); + result.replace("SAK", ""); + result.replace(" ", ""); + QStringList lis = result.split(':'); + qDebug() << lis; + MF_UID_parameterDialog dialog(lis[1].toUpper(), lis[2].toUpper(), lis[3].toUpper()); connect(&dialog, &MF_UID_parameterDialog::sendCMD, util, &Util::execCMD); if(dialog.exec() == QDialog::Accepted) - ui->funcTab->setCurrentIndex(1); + ui->funcTab->setCurrentIndex(Util::rawTabIndex); } } void Mifare::lockC() { - util->execCMD("hf 14a raw -pa -b7 40"); - util->execCMD("hf 14a raw -pa 43"); - util->execCMD("hf 14a raw -pa E0 00 39 F7"); - util->execCMD("hf 14a raw -pa E1 00 E1 EE"); - util->execCMD("hf 14a raw -pa 85 00 00 00 00 00 00 00 00 00 00 00 00 00 00 08 18 47"); - util->execCMD("hf 14a raw 52"); + if(Util::getClientType() == Util::CLIENTTYPE_OFFICIAL) + { + util->execCMD("hf 14a raw -pa -b7 40"); + util->execCMD("hf 14a raw -pa 43"); + util->execCMD("hf 14a raw -pa E0 00 39 F7"); + util->execCMD("hf 14a raw -pa E1 00 E1 EE"); + util->execCMD("hf 14a raw -pa 85 00 00 00 00 00 00 00 00 00 00 00 00 00 00 08 18 47"); + util->execCMD("hf 14a raw -a 52"); + } + else if(Util::getClientType() == Util::CLIENTTYPE_ICEMAN) + { + util->execCMD("hf 14a raw -ak -b 7 40"); + util->execCMD("hf 14a raw -ak 43"); + util->execCMD("hf 14a raw -ak E0 00 39 F7"); + util->execCMD("hf 14a raw -ak E1 00 E1 EE"); + util->execCMD("hf 14a raw -ak 85 00 00 00 00 00 00 00 00 00 00 00 00 00 00 08 18 47"); + util->execCMD("hf 14a raw -a 52"); + } } void Mifare::wipeE() { - util->execCMD("hf mf eclr"); + if(Util::getClientType() == Util::CLIENTTYPE_OFFICIAL || Util::getClientType() == Util::CLIENTTYPE_ICEMAN) + util->execCMD("hf mf eclr"); } void Mifare::simulate() { - MF_Sim_simDialog dialog(cardType.type); + MF_Sim_simDialog dialog(cardType.type, cardType.typeText); connect(&dialog, &MF_Sim_simDialog::sendCMD, util, &Util::execCMD); if(dialog.exec() == QDialog::Accepted) - ui->funcTab->setCurrentIndex(1); + ui->funcTab->setCurrentIndex(Util::rawTabIndex); } void Mifare::loadSniff(const QString& file) { - util->execCMD("hf list mf -l " + file); - ui->funcTab->setCurrentIndex(1); + if(Util::getClientType() == Util::CLIENTTYPE_OFFICIAL) + util->execCMD("hf list mf -l " + file); + else if(Util::getClientType() == Util::CLIENTTYPE_ICEMAN) + { + if(util->execCMDWithOutput("trace load -f " + file, Util::ReturnTrigger({"loaded"})) != "") + util->execCMD("trace list -t mf"); + } + + ui->funcTab->setCurrentIndex(Util::rawTabIndex); } void Mifare::saveSniff(const QString& file) { - util->execCMD("hf list mf -s " + file); - ui->funcTab->setCurrentIndex(1); + if(Util::getClientType() == Util::CLIENTTYPE_OFFICIAL) + util->execCMD("hf list mf -s " + file); + else if(Util::getClientType() == Util::CLIENTTYPE_ICEMAN) + util->execCMD("trace save -f " + file); + + ui->funcTab->setCurrentIndex(Util::rawTabIndex); } void Mifare::data_syncWithDataWidget(bool syncAll, int block) @@ -846,7 +957,7 @@ void Mifare::data_clearKey(bool clearAll) } } -bool Mifare::data_isKeyValid(const QString &key) +bool Mifare::data_isKeyValid(const QString& key) { if(key.length() != 12) return false; @@ -912,7 +1023,7 @@ void Mifare::setCardType(int type) } } -bool Mifare::data_loadDataFile(const QString &filename) +bool Mifare::data_loadDataFile(const QString& filename) { QFile file(filename, this); if(file.open(QIODevice::ReadOnly)) @@ -959,7 +1070,7 @@ bool Mifare::data_loadDataFile(const QString &filename) } } -bool Mifare::data_loadKeyFile(const QString &filename) +bool Mifare::data_loadKeyFile(const QString& filename) { QFile file(filename, this); if(file.open(QIODevice::ReadOnly)) @@ -996,7 +1107,7 @@ bool Mifare::data_loadKeyFile(const QString &filename) } } -QString Mifare::bin2text(const QByteArray &buff, int i, int length) +QString Mifare::bin2text(const QByteArray& buff, int i, int length) { QString ret = ""; char LByte, RByte; @@ -1014,7 +1125,7 @@ QString Mifare::bin2text(const QByteArray &buff, int i, int length) return ret; } -bool Mifare::data_saveDataFile(const QString &filename, bool isBin) +bool Mifare::data_saveDataFile(const QString& filename, bool isBin) { QFile file(filename, this); if(file.open(QIODevice::WriteOnly)) @@ -1058,7 +1169,7 @@ bool Mifare::data_saveDataFile(const QString &filename, bool isBin) } } -bool Mifare::data_saveKeyFile(const QString &filename, bool isBin) +bool Mifare::data_saveKeyFile(const QString& filename, bool isBin) { QFile file(filename, this); if(file.open(QIODevice::WriteOnly)) @@ -1153,12 +1264,12 @@ void Mifare::data_data2Key() } } -void Mifare::data_setData(int block, const QString &data) +void Mifare::data_setData(int block, const QString& data) { dataList->replace(block, data); } -void Mifare::data_setKey(int sector, KeyType keyType, const QString &key) +void Mifare::data_setKey(int sector, KeyType keyType, const QString& key) { if(keyType == KEY_A) keyAList->replace(sector, key); @@ -1192,24 +1303,36 @@ int Mifare::data_b2s(int block) return -1; } -QList Mifare::data_getACBits(const QString& text) //return empty QList if the text is invalid +bool Mifare::data_isACBitsValid(const QString& text, QList* returnHalfBytes) { QString input = text; - QList result; input.remove(" "); if(input.length() < 6) { - return result; + return false; } input = input.left(6); quint32 val = input.toUInt(nullptr, 16); - quint8 halfBytes[6]; + QList halfBytes; for(int i = 0; i < 6; i++) { - halfBytes[i] = (val >> ((5 - i) * 4)) & 0xf; + halfBytes.append((val >> ((5 - i) * 4)) & 0xf); } qDebug() << val; if((~halfBytes[0] & 0xf) == halfBytes[5] && (~halfBytes[1] & 0xf) == halfBytes[2] && (~halfBytes[3] & 0xf) == halfBytes[4]) + { + if(returnHalfBytes != nullptr) + *returnHalfBytes = halfBytes; + return true; + } + else + return false; +} + +QList Mifare::data_getACBits(const QString& text) //return empty QList if the text is invalid +{ + QList halfBytes, result; + if(data_isACBitsValid(text, &halfBytes)) { for(int i = 0; i < 4; i++) { diff --git a/module/mifare.h b/module/mifare.h index 7865755..b50ff33 100644 --- a/module/mifare.h +++ b/module/mifare.h @@ -37,6 +37,7 @@ public: quint16 block_size; quint8 blk[40]; quint8 blks[40]; + QString typeText; }; enum AccessType @@ -66,9 +67,10 @@ public: QString info(bool isRequiringOutput = false); void chk(); void nested(); + void darkside(); void hardnested(); void sniff(); - void snoop(); + void sniff14a(); void list(); void readOne(TargetType targetType = TARGET_MIFARE); void readSelected(TargetType targetType = TARGET_MIFARE); @@ -106,8 +108,9 @@ public: void saveSniff(const QString& file); void data_fillKeys(); - static QList data_getACBits(const QString &text); + static QList data_getACBits(const QString& text); static int data_b2s(int block); + static bool data_isACBitsValid(const QString& text, QList *returnHalfBytes = nullptr); public slots: signals: @@ -124,9 +127,9 @@ private: QRegularExpression* keyPattern; QString bin2text(const QByteArray& buff, int start, int length); - QString _readblk(int blockId, KeyType keyType, const QString &key, TargetType targetType = TARGET_MIFARE, int waitTime = 300); - QStringList _readsec(int sectorId, KeyType keyType, const QString &key, TargetType targetType = TARGET_MIFARE, int waitTime = 300); - bool _writeblk(int blockId, KeyType keyType, const QString &key, const QString &data, TargetType targetType = TARGET_MIFARE, int waitTime = 300); + QString _readblk(int blockId, KeyType keyType, const QString& key, TargetType targetType = TARGET_MIFARE, int waitTime = 300); + QStringList _readsec(int sectorId, KeyType keyType, const QString& key, TargetType targetType = TARGET_MIFARE, int waitTime = 300); + bool _writeblk(int blockId, KeyType keyType, const QString& key, const QString& data, TargetType targetType = TARGET_MIFARE, int waitTime = 300); }; #endif // MIFARE_H diff --git a/ui/mainwindow.cpp b/ui/mainwindow.cpp index 2b30c0f..673acdc 100644 --- a/ui/mainwindow.cpp +++ b/ui/mainwindow.cpp @@ -7,6 +7,7 @@ MainWindow::MainWindow(QWidget *parent): { ui->setupUi(this); myInfo = new QAction("wh201906", this); + currVersion = new QAction("Ver: " + QApplication::applicationVersion().section('.', 0, -2), this); // ignore the 4th version number checkUpdate = new QAction(tr("Check Update"), this); connect(myInfo, &QAction::triggered, [ = ]() { @@ -17,9 +18,11 @@ MainWindow::MainWindow(QWidget *parent): QDesktopServices::openUrl(QUrl("https://github.com/wh201906/Proxmark3GUI/releases")); }); this->addAction(myInfo); + this->addAction(currVersion); this->addAction(checkUpdate); settings = new QSettings("GUIsettings.ini", QSettings::IniFormat); + settings->setIniCodec("UTF-8"); pm3Thread = new QThread(this); pm3 = new PM3Process(pm3Thread); @@ -29,6 +32,18 @@ MainWindow::MainWindow(QWidget *parent): util = new Util(this); mifare = new Mifare(ui, util, this); + keyEventFilter = new MyEventFilter(QEvent::KeyRelease); + resizeEventFilter = new MyEventFilter(QEvent::Resize); + + // hide unused tabs + ui->funcTab->removeTab(1); + ui->funcTab->removeTab(1); + + portSearchTimer = new QTimer(this); + portSearchTimer->setInterval(2000); + connect(portSearchTimer, &QTimer::timeout, this, &MainWindow::on_portSearchTimer_timeout); + portSearchTimer->start(); + } MainWindow::~MainWindow() @@ -51,52 +66,70 @@ void MainWindow::initUI() // will be called by main.app // ******************** basic functions ******************** -void MainWindow::on_PM3_refreshPortButton_clicked() +void MainWindow::on_portSearchTimer_timeout() { - ui->PM3_portBox->clear(); - QSerialPort serial; - QStringList serialList; + QStringList newPortList; foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts()) { - qDebug() << info.isBusy() << info.isNull() << info.portName(); - serial.setPort(info); - - if(serial.open(QIODevice::ReadWrite)) - { - serialList << info.portName(); - serial.close(); - } +// qDebug() << info.isBusy() << info.isNull() << info.portName() << info.description(); + if(!info.isNull()) + newPortList << info.portName(); } - foreach(QString port, serialList) + if(newPortList != portList) // update PM3_portBox when available ports changed { - ui->PM3_portBox->addItem(port); + portList = newPortList; + ui->PM3_portBox->clear(); + ui->PM3_portBox->addItems(portList); } } void MainWindow::on_PM3_connectButton_clicked() { qDebug() << "Main:" << QThread::currentThread(); + QString port = ui->PM3_portBox->currentText(); if(port == "") QMessageBox::information(NULL, tr("Info"), tr("Plz choose a port first"), QMessageBox::Ok); else { + QStringList args = ui->Set_Client_startArgsEdit->text().replace("", port).split(' '); saveClientPath(ui->PM3_pathEdit->text()); - emit connectPM3(ui->PM3_pathEdit->text(), port); + QProcess envSetProcess; + QFileInfo envScriptPath(ui->Set_Client_envScriptEdit->text()); + if(envScriptPath.exists()) + { + qDebug() << envScriptPath.absoluteFilePath(); +#ifdef Q_OS_WIN + // cmd /c "">>nul && set + envSetProcess.start("cmd /c \"" + envScriptPath.absoluteFilePath() + "\">>nul && set"); +#else + // sh -c '. "">>/dev/null && env' + envSetProcess.start("sh -c \' . \"" + envScriptPath.absoluteFilePath() + "\">>/dev/null && env"); +#endif + envSetProcess.waitForReadyRead(10000); + clientEnv = QString(envSetProcess.readAll()).split(QRegExp("[\r\n]"), QString::SkipEmptyParts); +// qDebug() << "Get Env List" << clientEnv; + } + else + clientEnv.clear(); + emit setProcEnv(&clientEnv); + emit connectPM3(ui->PM3_pathEdit->text(), port, args); } } -void MainWindow::onPM3StateChanged(bool st, QString info) +void MainWindow::onPM3StateChanged(bool st, const QString& info) { pm3state = st; setState(st); if(st == true) { + portSearchTimer->stop(); setStatusBar(PM3VersionBar, info); setStatusBar(connectStatusBar, tr("Connected")); } else { + portSearchTimer->start(); setStatusBar(PM3VersionBar, ""); setStatusBar(connectStatusBar, tr("Not Connected")); } @@ -104,11 +137,8 @@ void MainWindow::onPM3StateChanged(bool st, QString info) void MainWindow::on_PM3_disconnectButton_clicked() { - pm3state = false; - setState(false); emit killPM3(); emit setSerialListener("", false); - setStatusBar(connectStatusBar, tr("Not Connected")); } void MainWindow::refreshOutput(const QString& output) @@ -118,17 +148,31 @@ void MainWindow::refreshOutput(const QString& output) ui->Raw_outputEdit->moveCursor(QTextCursor::End); } -void MainWindow::refreshCMD(const QString& cmd) +void MainWindow::on_stopButton_clicked() { - ui->Raw_CMDEdit->setText(cmd); - if(cmd != "" && (ui->Raw_CMDHistoryWidget->count() == 0 || ui->Raw_CMDHistoryWidget->item(ui->Raw_CMDHistoryWidget->count() - 1)->text() != cmd)) - ui->Raw_CMDHistoryWidget->addItem(cmd); + if(!pm3state) + on_PM3_disconnectButton_clicked(); + else + { + on_PM3_disconnectButton_clicked(); + for(int i = 0; i < 10; i++) + { + util->delay(200); + if(!pm3state) + break; + } + emit reconnectPM3(); + } } - // ********************************************************* // ******************** raw command ******************** +void MainWindow::on_Raw_CMDEdit_textChanged(const QString &arg1) +{ + stashedCMDEditText = arg1; +} + void MainWindow::on_Raw_sendCMDButton_clicked() { util->execCMD(ui->Raw_CMDEdit->text()); @@ -173,17 +217,73 @@ void MainWindow::sendMSG() // send command when pressing Enter on_Raw_sendCMDButton_clicked(); } + +void MainWindow::refreshCMD(const QString& cmd) +{ + ui->Raw_CMDEdit->blockSignals(true); + ui->Raw_CMDEdit->setText(cmd); + if(cmd != "" && (ui->Raw_CMDHistoryWidget->count() == 0 || ui->Raw_CMDHistoryWidget->item(ui->Raw_CMDHistoryWidget->count() - 1)->text() != cmd)) + ui->Raw_CMDHistoryWidget->addItem(cmd); + stashedCMDEditText = cmd; + stashedIndex = -1; + ui->Raw_CMDEdit->blockSignals(false); +} + +void MainWindow::on_Raw_CMDEdit_keyPressed(QObject* obj_addr, QEvent& event) +{ + if(obj_addr == ui->Raw_CMDEdit && event.type() == QEvent::KeyRelease) + { + QKeyEvent& keyEvent = static_cast(event); + if(keyEvent.key() == Qt::Key_Up) + { + if(stashedIndex > 0) + stashedIndex--; + else if(stashedIndex == -1) + stashedIndex = ui->Raw_CMDHistoryWidget->count() - 1; + } + else if(keyEvent.key() == Qt::Key_Down) + { + if(stashedIndex < ui->Raw_CMDHistoryWidget->count() - 1 && stashedIndex != -1) + stashedIndex++; + else if(stashedIndex == ui->Raw_CMDHistoryWidget->count() - 1) + stashedIndex = -1; + } + if(keyEvent.key() == Qt::Key_Up || keyEvent.key() == Qt::Key_Down) + { + ui->Raw_CMDEdit->blockSignals(true); + if(stashedIndex == -1) + ui->Raw_CMDEdit->setText(stashedCMDEditText); + else + ui->Raw_CMDEdit->setText(ui->Raw_CMDHistoryWidget->item(stashedIndex)->text()); + ui->Raw_CMDEdit->blockSignals(false); + } + } +} // ***************************************************** // ******************** mifare ******************** -void MainWindow::MF_onTypeChanged(int id, bool st) +void MainWindow::on_MF_keyWidget_resized(QObject* obj_addr, QEvent& event) { - typeBtnGroup->blockSignals(true); - qDebug() << id << typeBtnGroup->checkedId(); + if(obj_addr == ui->MF_keyWidget && event.type() == QEvent::Resize) + { + QTableWidget* widget = (QTableWidget*)obj_addr; + int keyItemWidth = widget->width(); + keyItemWidth -= widget->verticalScrollBar()->width(); + keyItemWidth -= 2 * widget->frameWidth(); + keyItemWidth -= widget->horizontalHeader()->sectionSize(0); + widget->horizontalHeader()->resizeSection(1, keyItemWidth / 2); + widget->horizontalHeader()->resizeSection(2, keyItemWidth / 2); + } +} + +void MainWindow::MF_onMFCardTypeChanged(int id, bool st) +{ + MFCardTypeBtnGroup->blockSignals(true); + qDebug() << id << MFCardTypeBtnGroup->checkedId(); if(!st) { int result; - if(id > typeBtnGroup->checkedId()) // id is specified in uiInit() with a proper order, so I can compare the size by id. + if(id > MFCardTypeBtnGroup->checkedId()) // id is specified in uiInit() with a proper order, so I can compare the size by id. { result = QMessageBox::question(this, tr("Info"), tr("Some of the data and key will be cleared.") + "\n" + tr("Continue?"), QMessageBox::Yes | QMessageBox::No); } @@ -194,7 +294,7 @@ void MainWindow::MF_onTypeChanged(int id, bool st) if(result == QMessageBox::Yes) { qDebug() << "Yes"; - mifare->setCardType(typeBtnGroup->checkedId()); + mifare->setCardType(MFCardTypeBtnGroup->checkedId()); MF_widgetReset(); mifare->data_syncWithDataWidget(); mifare->data_syncWithKeyWidget(); @@ -202,10 +302,10 @@ void MainWindow::MF_onTypeChanged(int id, bool st) else { qDebug() << "No"; - typeBtnGroup->button(id)->setChecked(true); + MFCardTypeBtnGroup->button(id)->setChecked(true); } } - typeBtnGroup->blockSignals(false); + MFCardTypeBtnGroup->blockSignals(false); } void MainWindow::on_MF_selectAllBox_stateChanged(int arg1) @@ -736,10 +836,10 @@ void MainWindow::on_MF_Sniff_sniffButton_clicked() setState(true); } -void MainWindow::on_MF_Sniff_snoopButton_clicked() +void MainWindow::on_MF_14aSniff_snoopButton_clicked() { setState(false); - mifare->snoop(); + mifare->sniff14a(); setState(true); } @@ -752,6 +852,7 @@ void MainWindow::MF_widgetReset() { int secs = mifare->cardType.sector_size; int blks = mifare->cardType.block_size; + QBrush trailerItemForeColor = QBrush(QColor(0, 160, 255)); ui->MF_RW_blockBox->clear(); ui->MF_keyWidget->setRowCount(secs); ui->MF_dataWidget->setRowCount(blks); @@ -776,8 +877,10 @@ void MainWindow::MF_widgetReset() setTableItem(ui->MF_keyWidget, i, 1, ""); setTableItem(ui->MF_keyWidget, i, 2, ""); setTableItem(ui->MF_dataWidget, mifare->cardType.blks[i], 0, QString::number(i)); + ui->MF_dataWidget->item(mifare->cardType.blks[i] + mifare->cardType.blk[i] - 1, 2)->setForeground(trailerItemForeColor); ui->MF_dataWidget->item(mifare->cardType.blks[i], 0)->setCheckState(Qt::Checked); } + ui->MF_dataWidget->item(0, 2)->setForeground(QBrush(QColor(255, 160, 0))); ui->MF_selectAllBox->setCheckState(Qt::Checked); ui->MF_selectTrailerBox->setCheckState(Qt::Checked); @@ -794,42 +897,44 @@ void MainWindow::MF_widgetReset() void MainWindow::uiInit() { connect(ui->Raw_CMDEdit, &QLineEdit::editingFinished, this, &MainWindow::sendMSG); + ui->Raw_CMDEdit->installEventFilter(keyEventFilter); + connect(keyEventFilter, &MyEventFilter::eventHappened, this, &MainWindow::on_Raw_CMDEdit_keyPressed); + ui->MF_keyWidget->installEventFilter(resizeEventFilter); + connect(resizeEventFilter, &MyEventFilter::eventHappened, this, &MainWindow::on_MF_keyWidget_resized); connectStatusBar = new QLabel(this); programStatusBar = new QLabel(this); PM3VersionBar = new QLabel(this); + stopButton = new QPushButton(this); setStatusBar(connectStatusBar, tr("Not Connected")); setStatusBar(programStatusBar, tr("Idle")); setStatusBar(PM3VersionBar, ""); + stopButton->setText(tr("Stop")); ui->statusbar->addPermanentWidget(PM3VersionBar, 1); ui->statusbar->addPermanentWidget(connectStatusBar, 1); ui->statusbar->addPermanentWidget(programStatusBar, 1); + ui->statusbar->addPermanentWidget(stopButton); ui->MF_dataWidget->setColumnCount(3); ui->MF_dataWidget->setHorizontalHeaderItem(0, new QTableWidgetItem(tr("Sec"))); ui->MF_dataWidget->setHorizontalHeaderItem(1, new QTableWidgetItem(tr("Blk"))); ui->MF_dataWidget->setHorizontalHeaderItem(2, new QTableWidgetItem(tr("Data"))); - ui->MF_dataWidget->verticalHeader()->setVisible(false); ui->MF_dataWidget->setColumnWidth(0, 55); ui->MF_dataWidget->setColumnWidth(1, 55); - ui->MF_dataWidget->setColumnWidth(2, 430); ui->MF_keyWidget->setColumnCount(3); ui->MF_keyWidget->setHorizontalHeaderItem(0, new QTableWidgetItem(tr("Sec"))); ui->MF_keyWidget->setHorizontalHeaderItem(1, new QTableWidgetItem(tr("KeyA"))); ui->MF_keyWidget->setHorizontalHeaderItem(2, new QTableWidgetItem(tr("KeyB"))); - ui->MF_keyWidget->verticalHeader()->setVisible(false); - ui->MF_keyWidget->setColumnWidth(0, 35); - ui->MF_keyWidget->setColumnWidth(1, 115); - ui->MF_keyWidget->setColumnWidth(2, 115); + ui->MF_keyWidget->setColumnWidth(0, 45); MF_widgetReset(); - typeBtnGroup = new QButtonGroup(this); - typeBtnGroup->addButton(ui->MF_Type_miniButton, 0); - typeBtnGroup->addButton(ui->MF_Type_1kButton, 1); - typeBtnGroup->addButton(ui->MF_Type_2kButton, 2); - typeBtnGroup->addButton(ui->MF_Type_4kButton, 4); - connect(typeBtnGroup, QOverload::of(&QButtonGroup::buttonToggled), this, &MainWindow::MF_onTypeChanged); + MFCardTypeBtnGroup = new QButtonGroup(this); + MFCardTypeBtnGroup->addButton(ui->MF_Type_miniButton, 0); + MFCardTypeBtnGroup->addButton(ui->MF_Type_1kButton, 1); + MFCardTypeBtnGroup->addButton(ui->MF_Type_2kButton, 2); + MFCardTypeBtnGroup->addButton(ui->MF_Type_4kButton, 4); + connect(MFCardTypeBtnGroup, QOverload::of(&QButtonGroup::buttonToggled), this, &MainWindow::MF_onMFCardTypeChanged); ui->MF_keyWidget->installEventFilter(this); ui->MF_dataWidget->installEventFilter(this); @@ -837,7 +942,7 @@ void MainWindow::uiInit() settings->beginGroup("UI_grpbox_preference"); QStringList boxNames = settings->allKeys(); - QGroupBox* boxptr; + QGroupBox * boxptr; foreach(QString name, boxNames) { boxptr = this->findChild(name); @@ -860,11 +965,23 @@ void MainWindow::uiInit() ui->PM3_pathEdit->setText(settings->value("path", "proxmark3").toString()); settings->endGroup(); + settings->beginGroup("Client_Args"); + ui->Set_Client_startArgsEdit->setText(settings->value("args", " -f").toString()); + settings->endGroup(); + + settings->beginGroup("Client_forceButtonsEnabled"); + keepButtonsEnabled = settings->value("state", false).toBool(); + ui->Set_Client_forceEnabledBox->setChecked(keepButtonsEnabled); + settings->endGroup(); + + settings->beginGroup("Client_Env"); + ui->Set_Client_envScriptEdit->setText(settings->value("scriptPath").toString()); + settings->endGroup(); + ui->MF_RW_keyTypeBox->addItem("A", Mifare::KEY_A); ui->MF_RW_keyTypeBox->addItem("B", Mifare::KEY_B); on_Raw_CMDHistoryBox_stateChanged(Qt::Unchecked); - on_PM3_refreshPortButton_clicked(); } void MainWindow::signalInit() @@ -874,8 +991,11 @@ void MainWindow::signalInit() connect(util, &Util::refreshOutput, this, &MainWindow::refreshOutput); connect(this, &MainWindow::connectPM3, pm3, &PM3Process::connectPM3); + connect(this, &MainWindow::reconnectPM3, pm3, &PM3Process::reconnectPM3); connect(pm3, &PM3Process::PM3StatedChanged, this, &MainWindow::onPM3StateChanged); + connect(pm3, &PM3Process::PM3StatedChanged, util, &Util::setRunningState); connect(this, &MainWindow::killPM3, pm3, &PM3Process::kill); + connect(this, &MainWindow::setProcEnv, pm3, &PM3Process::setProcEnv); connect(util, &Util::write, pm3, &PM3Process::write); @@ -886,9 +1006,11 @@ void MainWindow::signalInit() connect(ui->MF_UIDGroupBox, &QGroupBox::clicked, this, &MainWindow::on_GroupBox_clicked); connect(ui->MF_simGroupBox, &QGroupBox::clicked, this, &MainWindow::on_GroupBox_clicked); connect(ui->MF_sniffGroupBox, &QGroupBox::clicked, this, &MainWindow::on_GroupBox_clicked); + + connect(stopButton, &QPushButton::clicked, this, &MainWindow::on_stopButton_clicked); } -void MainWindow::setStatusBar(QLabel * target, const QString & text) +void MainWindow::setStatusBar(QLabel * target, const QString& text) { if(target == PM3VersionBar) target->setText(tr("HW Version:") + text); @@ -898,7 +1020,7 @@ void MainWindow::setStatusBar(QLabel * target, const QString & text) target->setText(tr("State:") + text); } -void MainWindow::setTableItem(QTableWidget * widget, int row, int column, const QString & text) +void MainWindow::setTableItem(QTableWidget * widget, int row, int column, const QString& text) { if(widget->item(row, column) == nullptr) widget->setItem(row, column, new QTableWidgetItem()); @@ -956,6 +1078,11 @@ void MainWindow::setState(bool st) { setStatusBar(programStatusBar, tr("Idle")); } + setButtonsEnabled(st || keepButtonsEnabled); +} + +void MainWindow::setButtonsEnabled(bool st) +{ ui->MF_attackGroupBox->setEnabled(st); ui->MF_normalGroupBox->setEnabled(st); ui->MF_UIDGroupBox->setEnabled(st); @@ -983,10 +1110,53 @@ void MainWindow::on_GroupBox_clicked(bool checked) settings->endGroup(); } -void MainWindow::saveClientPath(const QString & path) +void MainWindow::saveClientPath(const QString& path) { settings->beginGroup("Client_Path"); settings->setValue("path", path); settings->endGroup(); } // *********************************************** + + + +void MainWindow::on_MF_Attack_darksideButton_clicked() +{ + setState(false); + mifare->darkside(); + setState(true); +} + +void MainWindow::on_Set_Client_startArgsEdit_editingFinished() +{ + settings->beginGroup("Client_Args"); + settings->setValue("args", ui->Set_Client_startArgsEdit->text()); + settings->endGroup(); +} + +void MainWindow::on_Set_Client_forceEnabledBox_stateChanged(int arg1) +{ + settings->beginGroup("Client_forceButtonsEnabled"); + keepButtonsEnabled = (arg1 == Qt::Checked); + settings->setValue("state", keepButtonsEnabled); + settings->endGroup(); + if(keepButtonsEnabled) + setButtonsEnabled(true); +} + +void MainWindow::on_Set_GUI_setLanguageButton_clicked() +{ + Util::chooseLanguage(settings, this); +} + +void MainWindow::on_PM3_refreshPortButton_clicked() +{ + on_portSearchTimer_timeout(); +} + +void MainWindow::on_Set_Client_envScriptEdit_editingFinished() +{ + settings->beginGroup("Client_Env"); + settings->setValue("scriptPath", ui->Set_Client_envScriptEdit->text()); + settings->endGroup(); +} diff --git a/ui/mainwindow.h b/ui/mainwindow.h index 873f6d4..b2747c9 100644 --- a/ui/mainwindow.h +++ b/ui/mainwindow.h @@ -19,7 +19,12 @@ #include #include #include +#include +#include +#include +#include +#include "common/myeventfilter.h" #include "common/pm3process.h" #include "module/mifare.h" #include "common/util.h" @@ -43,11 +48,13 @@ public: void initUI(); bool eventFilter(QObject *watched, QEvent *event); public slots: - void refreshOutput(const QString &output); - void refreshCMD(const QString &cmd); - void setStatusBar(QLabel* target, const QString & text); - void onPM3StateChanged(bool st, QString info); - void MF_onTypeChanged(int id, bool st); + void refreshOutput(const QString& output); + void refreshCMD(const QString& cmd); + void setStatusBar(QLabel* target, const QString& text); + void onPM3StateChanged(bool st, const QString& info); + void MF_onMFCardTypeChanged(int id, bool st); + void on_Raw_CMDEdit_keyPressed(QObject *obj_addr, QEvent &event); + void on_MF_keyWidget_resized(QObject *obj_addr, QEvent &event); private slots: void on_PM3_connectButton_clicked(); @@ -59,7 +66,8 @@ private slots: void on_Raw_clearOutputButton_clicked(); void sendMSG(); - void on_PM3_refreshPortButton_clicked(); + + void on_portSearchTimer_timeout(); void on_Raw_CMDHistoryBox_stateChanged(int arg1); @@ -87,7 +95,6 @@ private slots: void on_MF_RW_writeSelectedButton_clicked(); - void on_MF_RW_dumpButton_clicked(); void on_MF_RW_restoreButton_clicked(); @@ -142,27 +149,56 @@ private slots: void on_MF_fillKeysButton_clicked(); - void on_MF_Sniff_snoopButton_clicked(); + void on_MF_14aSniff_snoopButton_clicked(); void on_MF_trailerDecoderButton_clicked(); void on_MF_selectTrailerBox_stateChanged(int arg1); + void on_stopButton_clicked(); + + void on_Raw_CMDEdit_textChanged(const QString &arg1); + + void on_MF_Attack_darksideButton_clicked(); + + void on_Set_Client_startArgsEdit_editingFinished(); + + void on_Set_Client_forceEnabledBox_stateChanged(int arg1); + + void on_Set_GUI_setLanguageButton_clicked(); + + void setButtonsEnabled(bool st); + + void on_PM3_refreshPortButton_clicked(); + + void on_Set_Client_envScriptEdit_editingFinished(); + private: Ui::MainWindow* ui; - QButtonGroup* typeBtnGroup; + QButtonGroup* MFCardTypeBtnGroup; QLabel* connectStatusBar; QLabel* programStatusBar; QLabel* PM3VersionBar; + QPushButton* stopButton; QAction* myInfo; + QAction* currVersion; QAction* checkUpdate; QSettings* settings; + MyEventFilter* keyEventFilter; + MyEventFilter* resizeEventFilter; + + QString stashedCMDEditText; + int stashedIndex = -1; void uiInit(); PM3Process* pm3; bool pm3state; + bool keepButtonsEnabled; QThread* pm3Thread; + QTimer* portSearchTimer; + QStringList portList; + QStringList clientEnv; Mifare* mifare; Util* util; @@ -172,12 +208,14 @@ private: void signalInit(); void MF_widgetReset(); - void setTableItem(QTableWidget *widget, int row, int column, const QString &text); + void setTableItem(QTableWidget *widget, int row, int column, const QString& text); void setState(bool st); - void saveClientPath(const QString &path); + void saveClientPath(const QString& path); signals: - void connectPM3(const QString path, const QString port); + void connectPM3(const QString& path, const QString& port, const QStringList args); + void reconnectPM3(); void killPM3(); - void setSerialListener(const QString &name, bool state); + void setSerialListener(const QString& name, bool state); + void setProcEnv(const QStringList *env); }; #endif // MAINWINDOW_H diff --git a/ui/mainwindow.ui b/ui/mainwindow.ui index edd93f4..5143dba 100644 --- a/ui/mainwindow.ui +++ b/ui/mainwindow.ui @@ -6,14 +6,14 @@ 0 0 - 970 - 770 + 1050 + 700 - 970 - 770 + 800 + 600 @@ -53,13 +53,20 @@ - Path: + Client Path: + + + + Port: + + + @@ -68,6 +75,9 @@ 0 + + true + @@ -79,7 +89,7 @@ - Refresh + Refresh Ports @@ -170,6 +180,12 @@ QAbstractItemView::SingleSelection + + true + + + false + 20 @@ -329,6 +345,9 @@ QAbstractItemView::SingleSelection + + false + 20 @@ -568,6 +587,19 @@ + + + + + 40 + 0 + + + + Darkside + + + @@ -1097,7 +1129,7 @@ - + 40 @@ -1105,7 +1137,7 @@ - Snoop + Sniff(14a) @@ -1168,6 +1200,222 @@ + + + true + + + LF/Data + + + + + 10 + 10 + 121 + 211 + + + + LF Config + + + + 2 + + + 2 + + + 5 + + + 2 + + + 2 + + + + + Frequency + + + + 5 + + + 2 + + + 5 + + + 2 + + + 2 + + + + + + 0 + 0 + + + + 125k + + + + + + + + 0 + 0 + + + + 134k + + + + + + + + + + + + BitRate: + + + + + + + Decimation: + + + + + + + + + + Averaging: + + + + + + + + + + + + + + Threshold: + + + + + + + + + + Skips: + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 20 + 0 + + + + Get + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 20 + 0 + + + + Set + + + + + + + + + + + T55xx + + + + + 20 + 10 + 256 + 192 + + + + RawCommand @@ -1198,6 +1446,9 @@ 0 + + Qt::NoFocus + Qt::ScrollBarAlwaysOn @@ -1283,6 +1534,212 @@ + + + Settings + + + + + + + + Client + + + + + + Preload script path: + + + + + + + + + + + + + + Note: +If the client requires some enviroment variables, you can make a script file(*.bat on Windows or *.sh on Linux) to configure them, +then put the path of the script there + + + true + + + + + + + Qt::Horizontal + + + + + + + Start arguments + + + + + + + <port> -f + + + + + + + Note: +-f is necessary because the GUI need to handle the output in time +In some cases the arguments should be set to "-p /dev/<port> -f" +or "-p <port> -f" + + + true + + + + + + + Qt::Horizontal + + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + + + + + + + + 0 + 0 + + + + Keep buttons enabled even the client is running or disconnected + + + true + + + + + + + + + Qt::Horizontal + + + + + + + + + + GUI + + + + + + + + Language: + + + + + + + Choose Language + + + + + + + (Restart this app to use new language) + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + diff --git a/ui/mf_attack_hardnesteddialog.cpp b/ui/mf_attack_hardnesteddialog.cpp index 6896764..79b01c6 100644 --- a/ui/mf_attack_hardnesteddialog.cpp +++ b/ui/mf_attack_hardnesteddialog.cpp @@ -21,14 +21,26 @@ MF_Attack_hardnestedDialog::~MF_Attack_hardnestedDialog() void MF_Attack_hardnestedDialog::on_buttonBox_accepted() { - emit sendCMD("hf mf hardnested " - + ui->knownKeySectorBox->currentText() - + " " - + ui->knownKeyTypeBox->currentText() - + " " - + ui->knownKeyBox->text() - + " " - + ui->targetKeySectorBox->currentText() - + " " - + ui->targetKeyTypeBox->currentText()); + if(Util::getClientType() == Util::CLIENTTYPE_OFFICIAL) + emit sendCMD("hf mf hardnested " + + ui->knownKeySectorBox->currentText() + + " " + + ui->knownKeyTypeBox->currentText() + + " " + + ui->knownKeyBox->text() + + " " + + ui->targetKeySectorBox->currentText() + + " " + + ui->targetKeyTypeBox->currentText()); + else if(Util::getClientType() == Util::CLIENTTYPE_ICEMAN) // same format in v4.9237 + emit sendCMD("hf mf hardnested " + + ui->knownKeySectorBox->currentText() + + " " + + ui->knownKeyTypeBox->currentText() + + " " + + ui->knownKeyBox->text() + + " " + + ui->targetKeySectorBox->currentText() + + " " + + ui->targetKeyTypeBox->currentText()); } diff --git a/ui/mf_attack_hardnesteddialog.h b/ui/mf_attack_hardnesteddialog.h index 0f13bf7..785cb75 100644 --- a/ui/mf_attack_hardnesteddialog.h +++ b/ui/mf_attack_hardnesteddialog.h @@ -2,8 +2,10 @@ #define MF_ATTACK_HARDNESTEDDIALOG_H #include +#include "common/util.h" -namespace Ui { +namespace Ui +{ class MF_Attack_hardnestedDialog; } @@ -19,7 +21,7 @@ public: private: Ui::MF_Attack_hardnestedDialog *ui; signals: - void sendCMD(QString cmd); + void sendCMD(const QString& cmd); private slots: void on_buttonBox_accepted(); }; diff --git a/ui/mf_sim_simdialog.cpp b/ui/mf_sim_simdialog.cpp index c0ba180..2db9c19 100644 --- a/ui/mf_sim_simdialog.cpp +++ b/ui/mf_sim_simdialog.cpp @@ -1,12 +1,34 @@ #include "mf_sim_simdialog.h" #include "ui_mf_sim_simdialog.h" -MF_Sim_simDialog::MF_Sim_simDialog(int cardType, QWidget *parent) : +MF_Sim_simDialog::MF_Sim_simDialog(int cardType, QString cardTypeText, QWidget *parent) : QDialog(parent), ui(new Ui::MF_Sim_simDialog) { ui->setupUi(this); + if(Util::getClientType() == Util::CLIENTTYPE_OFFICIAL) + { + ui->atqaGroupBox->setVisible(false); + ui->atqaLine->setVisible(false); + ui->sakGroupBox->setVisible(false); + ui->sakLine->setVisible(false); + ui->vGroupBox->setVisible(false); + ui->vLine->setVisible(false); + } + else if(Util::getClientType() == Util::CLIENTTYPE_ICEMAN) + { + ui->fGroupBox->setVisible(false); + ui->fLine->setVisible(false); + ui->rGroupBox->setVisible(false); + ui->rLine->setVisible(false); + ui->uBox->setText("-u"); + ui->nBox->setText("-n"); + ui->iBox->setText("-i"); + ui->xBox->setText("--crack"); + ui->eBox->setText("-e"); + } this->cardType = cardType; + this->cardTypeText = cardTypeText; } MF_Sim_simDialog::~MF_Sim_simDialog() @@ -55,12 +77,27 @@ void MF_Sim_simDialog::on_fBox_clicked(bool checked) void MF_Sim_simDialog::on_buttonBox_accepted() { QString paras; - paras += (ui->uBox->isChecked() ? "u " + ui->uEdit->text() + " " : ""); - paras += (ui->nBox->isChecked() ? "n " + ui->nEdit->text() + " " : ""); - paras += (ui->iBox->isChecked() ? "i " : ""); - paras += (ui->xBox->isChecked() ? "x " : ""); - paras += (ui->eBox->isChecked() ? "e " : ""); - paras += (ui->fBox->isChecked() ? "f " + ui->fEdit->text() + " " : ""); - paras += (ui->rBox->isChecked() ? "r " : ""); - emit sendCMD(QString("hf mf sim ") + "*" + QString::number(cardType) + " " + paras.trimmed()); + if(Util::getClientType() == Util::CLIENTTYPE_OFFICIAL) + { + paras += (ui->uBox->isChecked() ? "u " + ui->uEdit->text() + " " : ""); + paras += (ui->nBox->isChecked() ? "n " + ui->nEdit->text() + " " : ""); + paras += (ui->iBox->isChecked() ? "i " : ""); + paras += (ui->xBox->isChecked() ? "x " : ""); + paras += (ui->eBox->isChecked() ? "e " : ""); + paras += (ui->fBox->isChecked() ? "f " + ui->fEdit->text() + " " : ""); + paras += (ui->rBox->isChecked() ? "r " : ""); + emit sendCMD(QString("hf mf sim ") + "*" + QString::number(cardType) + " " + paras.trimmed()); + } + else if(Util::getClientType() == Util::CLIENTTYPE_ICEMAN) + { + paras += (ui->uBox->isChecked() ? "-u " + ui->uEdit->text() + " " : ""); + paras += (ui->atqaBox->isChecked() ? "--atqa " + ui->atqaEdit->text() + " " : ""); + paras += (ui->sakBox->isChecked() ? "--sak " + ui->sakEdit->text() + " " : ""); + paras += (ui->nBox->isChecked() ? "-n " + ui->nEdit->text() + " " : ""); + paras += (ui->iBox->isChecked() ? "-i " : ""); + paras += (ui->xBox->isChecked() ? "--crack " : ""); + paras += (ui->eBox->isChecked() ? "-e " : ""); + paras += (ui->vBox->isChecked() ? "-v " : ""); + emit sendCMD(QString("hf mf sim --") + cardTypeText + " " + paras.trimmed()); + } } diff --git a/ui/mf_sim_simdialog.h b/ui/mf_sim_simdialog.h index 7fa53a1..34a455b 100644 --- a/ui/mf_sim_simdialog.h +++ b/ui/mf_sim_simdialog.h @@ -3,6 +3,7 @@ #include #include +#include "common/util.h" namespace Ui { @@ -14,7 +15,7 @@ class MF_Sim_simDialog : public QDialog Q_OBJECT public: - explicit MF_Sim_simDialog(int cardType, QWidget *parent = nullptr); + explicit MF_Sim_simDialog(int cardType, QString cardTypeText, QWidget *parent = nullptr); ~MF_Sim_simDialog(); private slots: @@ -25,8 +26,9 @@ private slots: private: Ui::MF_Sim_simDialog *ui; int cardType; + QString cardTypeText; signals: - void sendCMD(QString cmd); + void sendCMD(const QString& cmd); private slots: void on_buttonBox_accepted(); }; diff --git a/ui/mf_sim_simdialog.ui b/ui/mf_sim_simdialog.ui index 1a9c15a..2fcbf31 100644 --- a/ui/mf_sim_simdialog.ui +++ b/ui/mf_sim_simdialog.ui @@ -7,309 +7,598 @@ 0 0 461 - 456 + 524 Simulate - - 5 - - - 5 - - - 5 - - - 5 - - - 5 - - - - - - u - - - - - - - - 0 - 0 - - - - - 100 - 16777215 - - - - - - - - - 0 - 0 - - - - UID 4 or 7 bytes. If not specified, the UID 4B from emulator memory will be used - - - true - - - - + + + + 5 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + u + + + + + + + + 0 + 0 + + + + + 100 + 16777215 + + + + + + + + + 0 + 0 + + + + UID 4 or 7 bytes. If not specified, the UID 4B from emulator memory will be used + + + true + + + + + + + + + + Qt::Horizontal + + + + + + + + 5 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + --atqa + + + + + + + + 0 + 0 + + + + + 100 + 16777215 + + + + + + + + + 0 + 0 + + + + Provide explicit ATQA (2 bytes) + + + true + + + + + + + + + + Qt::Horizontal + + + + + + + + 5 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + --sak + + + + + + + + 0 + 0 + + + + + 100 + 16777215 + + + + + + + + + 0 + 0 + + + + Provide explicit SAK (1 byte) + + + true + + + + + - + Qt::Horizontal - - - - - n - - - - - - - - 0 - 0 - - - - - 100 - 16777215 - - - - - - - - - 0 - 0 - - - - Automatically exit simulation after <numreads> blocks have been read by reader. 0 = infinite - - - true - - - - + + + + 5 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + n + + + + + + + + 0 + 0 + + + + + 100 + 16777215 + + + + + + + + + 0 + 0 + + + + Automatically exit simulation after <numreads> blocks have been read by reader. 0 = infinite + + + true + + + + + - + Qt::Horizontal - - - - - i - - - - - - - - 0 - 0 - - - - Interactive, means that console will not be returned until simulation finishes or is aborted - - - true - - - - + + + + 5 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + i + + + + + + + + 0 + 0 + + + + Interactive, means that console will not be returned until simulation finishes or is aborted + + + true + + + + + - + Qt::Horizontal - - - - - x - - - - - - - - 0 - 0 - - - - Crack, performs the 'reader attack', nr/ar attack against a legitimate reader, fishes out the key(s) - - - true - - - - + + + + 5 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + x + + + + + + + + 0 + 0 + + + + Crack, performs the 'reader attack', nr/ar attack against a legitimate reader, fishes out the key(s) + + + true + + + + + - + Qt::Horizontal - - - - - e - - - - - - - - 0 - 0 - - - - set keys found from 'reader attack' to emulator memory (implies x and i) - - - true - - - - + + + + 5 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + e + + + + + + + + 0 + 0 + + + + set keys found from 'reader attack' to emulator memory (implies x(--crack) and i) + + + true + + + + + - + Qt::Horizontal - - - - - f - - - - - - - - 0 - 0 - - - - - 100 - 16777215 - - - - - - - - - 0 - 0 - - - - get UIDs to use for 'reader attack' from file 'f <filename.txt>' (implies x and i) - - - true - - - - + + + + 5 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + f + + + + + + + + 0 + 0 + + + + + 100 + 16777215 + + + + + + + + + 0 + 0 + + + + get UIDs to use for 'reader attack' from file 'f <filename.txt>' (implies x and i) + + + true + + + + + - + Qt::Horizontal - - - - - r - - - - - - - - 0 - 0 - - - - Generate random nonces instead of sequential nonces. Standard reader attack won't work with this option, only moebius attack works - - - true - - - - + + + + 5 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + r + + + + + + + + 0 + 0 + + + + Generate random nonces instead of sequential nonces. Standard reader attack won't work with this option, only moebius attack works + + + true + + + + + + + + + + Qt::Horizontal + + + + + + + + 5 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + -v + + + + + + + + 0 + 0 + + + + verbose output + + + true + + + + + - + Qt::Horizontal diff --git a/ui/mf_trailerdecoderdialog.cpp b/ui/mf_trailerdecoderdialog.cpp index 3b09a16..fca353d 100644 --- a/ui/mf_trailerdecoderdialog.cpp +++ b/ui/mf_trailerdecoderdialog.cpp @@ -15,10 +15,10 @@ MF_trailerDecoderDialog::MF_trailerDecoderDialog(QWidget *parent) : sizeGroup->addButton(ui->size4Button, 4); sizeGroup->addButton(ui->size16Button, 16); connect(sizeGroup, QOverload::of(&QButtonGroup::buttonToggled), this, &MF_trailerDecoderDialog::on_blockSizeChanged); - connect(ui->C0Box, &QSpinBox::textChanged, this, &MF_trailerDecoderDialog::on_boxChanged); - connect(ui->C1Box, &QSpinBox::textChanged, this, &MF_trailerDecoderDialog::on_boxChanged); - connect(ui->C2Box, &QSpinBox::textChanged, this, &MF_trailerDecoderDialog::on_boxChanged); - connect(ui->C3Box, &QSpinBox::textChanged, this, &MF_trailerDecoderDialog::on_boxChanged); + connect(ui->C0Box, QOverload::of(&QSpinBox::valueChanged), this, &MF_trailerDecoderDialog::on_boxChanged); + connect(ui->C1Box, QOverload::of(&QSpinBox::valueChanged), this, &MF_trailerDecoderDialog::on_boxChanged); + connect(ui->C2Box, QOverload::of(&QSpinBox::valueChanged), this, &MF_trailerDecoderDialog::on_boxChanged); + connect(ui->C3Box, QOverload::of(&QSpinBox::valueChanged), this, &MF_trailerDecoderDialog::on_boxChanged); ui->dataBlockWidget->setRowCount(3); ui->dataBlockWidget->setColumnCount(4); @@ -123,7 +123,7 @@ void MF_trailerDecoderDialog::setTableItem(QTableWidget* widget, int row, int co widget->item(row, column)->setText(text); } -void MF_trailerDecoderDialog::on_boxChanged(const QString &arg1) +void MF_trailerDecoderDialog::on_boxChanged(int arg1) { quint8 ACBits[4]; ACBits[0] = ui->C0Box->value(); diff --git a/ui/mf_trailerdecoderdialog.h b/ui/mf_trailerdecoderdialog.h index 93a6abd..775608a 100644 --- a/ui/mf_trailerdecoderdialog.h +++ b/ui/mf_trailerdecoderdialog.h @@ -23,11 +23,11 @@ public: private slots: - void on_accessBitsEdit_textChanged(const QString &arg1); + void on_accessBitsEdit_textChanged(const QString& arg1); void on_blockSizeChanged(int id, bool st); - void on_boxChanged(const QString &arg1); + void on_boxChanged(int arg1); private: Ui::MF_trailerDecoderDialog *ui; QRegularExpressionValidator* validator; diff --git a/ui/mf_uid_parameterdialog.cpp b/ui/mf_uid_parameterdialog.cpp index e30705d..030d3b1 100644 --- a/ui/mf_uid_parameterdialog.cpp +++ b/ui/mf_uid_parameterdialog.cpp @@ -18,10 +18,18 @@ MF_UID_parameterDialog::~MF_UID_parameterDialog() void MF_UID_parameterDialog::on_buttonBox_accepted() { - emit sendCMD("hf mf csetuid " - + ui->UIDLineEdit->text() - + " " - + ui->ATQALineEdit->text() - + " " - + ui->SAKLineEdit->text()); + if(Util::getClientType() == Util::CLIENTTYPE_OFFICIAL) + emit sendCMD("hf mf csetuid " + + ui->UIDLineEdit->text() + + " " + + ui->ATQALineEdit->text() + + " " + + ui->SAKLineEdit->text()); + else if(Util::getClientType() == Util::CLIENTTYPE_ICEMAN) // same format in v4.9237 + emit sendCMD("hf mf csetuid " + + ui->UIDLineEdit->text() + + " " + + ui->ATQALineEdit->text() + + " " + + ui->SAKLineEdit->text()); } diff --git a/ui/mf_uid_parameterdialog.h b/ui/mf_uid_parameterdialog.h index ec580cb..8812168 100644 --- a/ui/mf_uid_parameterdialog.h +++ b/ui/mf_uid_parameterdialog.h @@ -2,8 +2,10 @@ #define MF_UID_PARAMETERDIALOG_H #include +#include "common/util.h" -namespace Ui { +namespace Ui +{ class MF_UID_parameterDialog; } @@ -18,7 +20,7 @@ public: private: Ui::MF_UID_parameterDialog *ui; signals: - void sendCMD(QString cmd); + void sendCMD(const QString& cmd); private slots: void on_buttonBox_accepted(); };