Merge branch 'dev'

master
wh201906 2 years ago
commit 9aff432b1b
No known key found for this signature in database

@ -2,6 +2,17 @@
[中文](doc/CHANGELOG/CHANGELOG_zh_CN.md)
### V0.2.6
+ Add support for Iceman/RRG repo v4.15864 [#37](https://github.com/wh201906/Proxmark3GUI/issues/37)
+ Optimize mifare classic block writing logic
+ Fix the default lf config
+ Add feedback for the GUI failing to start the client
+ Add feedback for the client failing to connect to PM3 hardware
+ Detect PM3 hardware when searching serial ports
+ Remove extra empty lines in raw command output
+ Use embedded config files
+ Remove the wait time between performing nested attack then switching to staticnested attack
### V0.2.5
+ Fix bug [#28](https://github.com/wh201906/Proxmark3GUI/issues/28)

@ -60,7 +60,6 @@ Great thanks to him.
mkdir build && cd build
qmake ../src
make -j4 && make clean
cp -r ../config ./
./Proxmark3GUI
## Build on macOS

@ -0,0 +1,7 @@
<RCC>
<qresource prefix="/config">
<file>config_official.json</file>
<file>config_rrgv4.13441.json</file>
<file>config_rrgv4.15864.json</file>
</qresource>
</RCC>

@ -10,6 +10,13 @@
"2k": "2",
"4k": "4"
},
"//": "|---|----------------|---|----------------|---| ",
"//": "|sec|key A |res|key B |res| ",
"//": "|---|----------------|---|----------------|---| ",
"//": "|000| ffffffffffff | 1 | ffffffffffff | 1 | ",
"//": "......",
"//": "|---|----------------|---|----------------|---| ",
"//": "",
"key pattern": "\\|\\s*\\d{3}\\s*\\|\\s*.+?\\s*\\|\\s*.+?\\s*\\|\\s*.+?\\s*\\|\\s*.+?\\s*\\|",
"key A index": 2,
"key B index": 4
@ -22,6 +29,15 @@
"2k": "2",
"4k": "4"
},
"//": "|---|----------------|----------------| ",
"//": "|sec|key A |key B | ",
"//": "|---|----------------|----------------| ",
"//": "|000| ffffffffffff | ffffffffffff | ",
"//": "......",
"//": "|004| ? | ? | ",
"//": "......",
"//": "|---|----------------|----------------| ",
"//": " ",
"key pattern": "\\|\\s*\\d{3}\\s*\\|\\s*.+?\\s*\\|\\s*.+?\\s*\\|",
"key A index": 2,
"key B index": 3
@ -39,10 +55,22 @@
"cmd": "hf list mf"
},
"dump": {
"cmd": "hf mf dump"
"cmd": "hf mf dump <card type>",
"card type": {
"mini": "0",
"1k": "1",
"2k": "2",
"4k": "4"
}
},
"restore": {
"cmd": "hf mf restore"
"cmd": "hf mf restore <card type>",
"card type": {
"mini": "0",
"1k": "1",
"2k": "2",
"4k": "4"
}
},
"emulator wipe": {
"cmd": "hf mf eclr"

@ -15,6 +15,13 @@
"A": "a",
"B": "b"
},
"//": "[+] |-----|----------------|---|----------------|---|",
"//": "[+] | Sec | key A |res| key B |res|",
"//": "[+] |-----|----------------|---|----------------|---|",
"//": "[+] | 000 | ffffffffffff | 1 | ffffffffffff | 1 |",
"//": "......",
"//": "[+] |-----|----------------|---|----------------|---|",
"//": "[+] ( 0:Failed / 1:Success )",
"key pattern": "\\|\\s*\\d{3}\\s*\\|\\s*.+?\\s*\\|\\s*.+?\\s*\\|\\s*.+?\\s*\\|\\s*.+?\\s*\\|",
"key A index": 2,
"key B index": 4
@ -27,6 +34,15 @@
"2k": "2k",
"4k": "4k"
},
"//": "[+] |-----|----------------|---|----------------|---|",
"//": "[+] | Sec | key A |res| key B |res|",
"//": "[+] |-----|----------------|---|----------------|---|",
"//": "[+] | 000 | ffffffffffff | 1 | ffffffffffff | 1 |",
"//": "......",
"//": "[+] | 004 | ------------ | 0 | ------------ | 0 |",
"//": "......",
"//": "[+] |-----|----------------|---|----------------|---|",
"//": "[+] ( 0:Failed / 1:Success )",
"key pattern": "\\|\\s*\\d{3}\\s*\\|\\s*.+?\\s*\\|\\s*.+?\\s*\\|\\s*.+?\\s*\\|\\s*.+?\\s*\\|",
"key A index": 2,
"key B index": 4
@ -44,10 +60,22 @@
"cmd": "trace list -t mf"
},
"dump": {
"cmd": "hf mf dump"
"cmd": "hf mf dump --<card type>",
"card type": {
"mini": "mini",
"1k": "1k",
"2k": "2k",
"4k": "4k"
}
},
"restore": {
"cmd": "hf mf restore"
"cmd": "hf mf restore --<card type>",
"card type": {
"mini": "mini",
"1k": "1k",
"2k": "2k",
"4k": "4k"
}
},
"emulator wipe": {
"cmd": "hf mf eclr"

@ -0,0 +1,241 @@
{
"//": "Based on Proxmark3 rrg repo v4.15864, commit 1f75adc",
"//": "You can change this file if the command format of client changes",
"mifare classic": {
"nested": {
"cmd": "hf mf nested --<card type> --blk <block> -<key type> -k <key>",
"static cmd": "hf mf staticnested --<card type> --blk <block> -<key type> -k <key>",
"card type": {
"mini": "mini",
"1k": "1k",
"2k": "2k",
"4k": "4k"
},
"key type": {
"A": "a",
"B": "b"
},
"//": "[+] -----+-----+--------------+---+--------------+----",
"//": "[+] Sec | Blk | key A |res| key B |res",
"//": "[+] -----+-----+--------------+---+--------------+----",
"//": "[+] 000 | 003 | FFFFFFFFFFFF | 1 | FFFFFFFFFFFF | 1",
"//": "......",
"//": "[+] -----+-----+--------------+---+--------------+----",
"//": "[+] ( 0:Failed / 1:Success )",
"key pattern": "\\s*\\d{3}\\s*\\|\\s*\\d{3}\\s*\\|\\s*.+?\\s*\\|\\s*.+?\\s*\\|\\s*.+?\\s*\\|\\s*.+?\\s*$",
"key A index": 2,
"key B index": 4
},
"check": {
"cmd": "hf mf chk --<card type>",
"card type": {
"mini": "mini",
"1k": "1k",
"2k": "2k",
"4k": "4k"
},
"//": "[+] -----+-----+--------------+---+--------------+----",
"//": "[+] Sec | Blk | key A |res| key B |res",
"//": "[+] -----+-----+--------------+---+--------------+----",
"//": "[+] 000 | 003 | FFFFFFFFFFFF | 1 | FFFFFFFFFFFF | 1",
"//": "......",
"//": "[+] 004 | 019 | ------------ | 0 | ------------ | 0",
"//": "......",
"//": "[+] -----+-----+--------------+---+--------------+----",
"//": "[+] ( 0:Failed / 1:Success )",
"key pattern": "\\s*\\d{3}\\s*\\|\\s*\\d{3}\\s*\\|\\s*.+?\\s*\\|\\s*.+?\\s*\\|\\s*.+?\\s*\\|\\s*.+?\\s*$",
"key A index": 2,
"key B index": 4
},
"info": {
"cmd": "hf 14a info"
},
"sniff": {
"cmd": "hf sniff"
},
"sniff 14a": {
"cmd": "hf 14a sniff"
},
"list": {
"cmd": "trace list -t mf"
},
"dump": {
"cmd": "hf mf dump --<card type>",
"card type": {
"mini": "mini",
"1k": "1k",
"2k": "2k",
"4k": "4k"
}
},
"restore": {
"cmd": "hf mf restore --<card type>",
"card type": {
"mini": "mini",
"1k": "1k",
"2k": "2k",
"4k": "4k"
}
},
"emulator wipe": {
"cmd": "hf mf eclr"
},
"Magic Card wipe": {
"cmd": "hf mf cwipe"
},
"emulator read block": {
"cmd": "hf mf egetblk --blk <block>",
"data pattern": "([0-9a-fA-F]{2} ){15}[0-9a-fA-F]{2}"
},
"Magic Card read block": {
"cmd": "hf mf cgetblk --blk <block>",
"data pattern": "([0-9a-fA-F]{2} ){15}[0-9a-fA-F]{2}"
},
"normal read block": {
"cmd": "hf mf rdbl --blk <block> -<key type> -k <key>",
"key type": {
"A": "a",
"B": "b"
},
"data pattern": "([0-9a-fA-F]{2} ){15}[0-9a-fA-F]{2}"
},
"darkside": {
"cmd": "hf mf darkside"
},
"save sniff": {
"cmd": "trace save -f <filename>"
},
"load sniff": {
"cmd": "trace load -f <filename>",
"show cmd": "trace list --buffer -t mf"
},
"hardnested": {
"cmd": "hf mf hardnested --blk <known key block> -<known key type> -k <known key> --tblk <target key block> --t<target key type>",
"known key type": {
"A": "a",
"B": "b"
},
"target key type": {
"A": "a",
"B": "b"
}
},
"normal read sector": {
"cmd": "hf mf rdsc --sec <sector> -<key type> -k <key>",
"key type": {
"A": "a",
"B": "b"
},
"data pattern": "([0-9a-fA-F]{2} ){15}[0-9a-fA-F]{2}"
},
"Magic Card read sector": {
"cmd": "hf mf cgetsc --sec <sector>",
"data pattern": "([0-9a-fA-F]{2} ){15}[0-9a-fA-F]{2}"
},
"//": "When writing a block, if the result is not empty and doesn't contain the failed flag, the function will return true",
"normal write block": {
"cmd": "hf mf wrbl --blk <block> -<key type> -k <key> -d <data>",
"key type": {
"A": "a",
"B": "b"
},
"failed flag": [
"fail",
"error"
]
},
"Magic Card write block": {
"cmd": "hf mf csetblk --blk <block> -d <data>",
"failed flag": [
"fail",
"error"
]
},
"emulator write block": {
"cmd": "hf mf esetblk --blk <block> -d <data>"
},
"Magic Card lock": {
"cmd": "hf 14a raw ",
"sequence": [
"-ak -b 7 40",
"-ak 43",
"-ak E0 00 39 F7",
"-ak E1 00 E1 EE",
"-ak 85 00 00 00 00 00 00 00 00 00 00 00 00 00 00 08 18 47",
"-a 52"
]
},
"Magic Card set parameter": {
"cmd": "hf mf csetuid --uid <uid> --atqa <atqa> --sak <sak>"
}
},
"lf": {
"read": {
"cmd": "lf read -v",
"show cmd": "data plot"
},
"sniff": {
"cmd": "lf sniff -v",
"show cmd": "data plot"
},
"search": {
"cmd": "lf search -u"
},
"tune": {
"cmd": "lf tune --divisor <divisor>"
},
"get config": {
"cmd": "hw status",
"field start": "LF Sampling config",
"field end": "\\[#\\] \\S",
"divisor": {
"flag": "divisor",
"pattern": "\\d+"
},
"bits per sample": {
"flag": "bits per sample",
"pattern": "\\d+"
},
"decimation": {
"flag": "decimation",
"pattern": "\\d+"
},
"averaging": {
"flag": "averaging",
"pattern": "\\d+",
"replace": {
"yes": "1",
"no": "0",
"Yes": "1",
"No": "0"
}
},
"trigger threshold": {
"flag": "trigger threshold",
"pattern": "\\d+"
},
"samples to skip": {
"flag": "samples to skip",
"pattern": "\\d+"
},
"//": "execute 'cmd' then find parameters between 'field stard' and 'field end'",
"//": "for each line, if the line doesn't have any flag, skip",
"//": "otherwise, delete characters before 'flag' and 'flag' itself, then use 'pattern' to get the parameter",
"//": "If 'replace' dict exists, replace all keys with respective values before getting parameters"
},
"set config": {
"cmd": "lf config --divisor <divisor> --bps <bits per sample> --dec <decimation> --avg <averaging> --trig <trigger threshold> --skip <samples to skip>",
"divisor cmd": "hw setlfdivisor -d <divisor>"
}
},
"t55xx": {
"clone em410x": {
"read": "lf em 410x reader",
"successful read flag": "EM 410x ID",
"pattern": "EM 410x ID\\s*\\K[0-9a-fA-F]{10}",
"clone cmd": "lf em 410x clone --id <id> <type>",
"t5555 flag": "--q5",
"t55x7 flag": ""
}
}
}

@ -1,17 +1,19 @@
import os, sys, shutil
from win32api import GetFileVersionInfo
from json import load
from re import fullmatch, IGNORECASE
from re import fullmatch, sub, IGNORECASE
compressDirList = []
def getPEVersion(fname):
try:
fileInfo = GetFileVersionInfo(fname, '\\')
version = "V%d.%d.%d" % (fileInfo['FileVersionMS'] / 65536,
fileInfo['FileVersionMS'] % 65536,
fileInfo['FileVersionLS'] / 65536)
fileInfo = GetFileVersionInfo(fname, "\\")
version = "V%d.%d.%d" % (
fileInfo["FileVersionMS"] / 65536,
fileInfo["FileVersionMS"] % 65536,
fileInfo["FileVersionLS"] / 65536,
)
except Exception:
print("Cannot get version number of", fname)
return version
@ -19,7 +21,7 @@ def getPEVersion(fname):
os.chdir(sys.path[0])
print("Current Directory:", os.getcwd())
targetName = os.path.abspath(os.getcwd()).split('\\')[-2]
targetName = os.path.abspath(os.getcwd()).split("\\")[-2]
print("Target Name", targetName)
src32Dir = ""
@ -63,11 +65,6 @@ elif not os.path.exists(dst32Dir):
print(dst32Dir, "doesn't exist, creating...")
shutil.copytree("./32", dst32Dir)
shutil.copyfile(src32Path, dst32Path)
configPath = dst32Dir + "/config"
if os.path.exists(configPath):
print(configPath, "exists, replacing...")
shutil.rmtree(configPath)
shutil.copytree("../config", configPath)
compressDirList.append(dst32Dir)
if os.path.exists(dst64Dir) and os.path.exists(dst64Path):
@ -77,19 +74,23 @@ elif not os.path.exists(dst64Dir):
print(dst64Dir, "doesn't exist, creating...")
shutil.copytree("./64", dst64Dir)
shutil.copyfile(src64Path, dst64Path)
configPath = dst64Dir + "/config"
if os.path.exists(configPath):
print(configPath, "exists, replacing...")
shutil.rmtree(configPath)
shutil.copytree("../config", configPath)
compressDirList.append(dst64Dir)
# TODO: GUI+client
clientList = [
"official-v3.1.0", "rrg_other-v4.13441", "rrg_other-v4.14434",
"rrg_other-v4.14831"
"official-v3.1.0",
"rrg_other-v4.13441",
"rrg_other-v4.14434",
"rrg_other-v4.14831",
"rrg_other-v4.15864",
]
configList = []
for config in os.listdir("../config"):
configPath = os.path.join("../config", config)
if os.path.isfile(configPath) and config.endswith(".json"):
configList.append(config)
def generateClient(clientName):
global compressDirList
@ -105,12 +106,34 @@ def generateClient(clientName):
shutil.copytree(clientSrcDir, clientDstDir)
shutil.copytree(dst64Dir, clientDstGUIDir)
if "official" in clientName:
shutil.copyfile("./client/GUIsettings_Official.ini",
clientDstGUIDir + "/GUIsettings.ini")
shutil.copyfile(
"./client/GUIsettings_Official.ini", clientDstGUIDir + "/GUIsettings.ini"
)
elif "rrg" in clientName:
shutil.copyfile("./client/GUIsettings_RRG.ini",
clientDstGUIDir + "/GUIsettings.ini")
shutil.copyfile(
"./client/GUIsettings_RRG.ini", clientDstGUIDir + "/GUIsettings.ini"
)
# Use exactly matched configFile if possible
version = clientName[clientName.find("v") :]
for config in configList:
if version in config:
print("Find matched config file", config)
with open(
clientDstGUIDir + "/GUIsettings.ini", "r", encoding="utf-8"
) as f:
data = f.read()
data = sub(
"configFile=:/config/.+\\.json",
"configFile=:/config/" + config,
data,
)
with open(
clientDstGUIDir + "/GUIsettings.ini", "w", encoding="utf-8"
) as f:
f.write(data)
compressDirList.append(clientDstDir)
return clientDstDir
for cl in clientList:

@ -2,6 +2,17 @@
[English](../../CHANGELOG.md)
### V0.2.6
+ 支持冰人版客户端 v4.15864 [#37](https://github.com/wh201906/Proxmark3GUI/issues/37)
+ 优化Mifare Classic卡写卡逻辑
+ 修复lf config默认配置
+ 添加客户端无法启动的提示
+ 添加PM3硬件连接失败的提示
+ 为PM3对应串口添加提示并自动选中
+ 修复原始指令框中有多余空行的问题
+ 内嵌不同客户端的配置文件
+ 去除从nested attack切换到staticnested attack的等待时间
### V0.2.5
+ 修复 [#28](https://github.com/wh201906/Proxmark3GUI/issues/28)

@ -59,7 +59,6 @@ release页面中有含客户端的GUI。这个GUI也可以搭配你自己的客
mkdir build && cd build
qmake ../src
make -j4 && make clean
cp -r ../config ./
./Proxmark3GUI
## 在macOS系统下编译

Binary file not shown.

File diff suppressed because it is too large Load Diff

@ -1,4 +1,3 @@
[Languages]
en_US=English
zh_CN=简体中文
ext=Load from external file

File diff suppressed because it is too large Load Diff

@ -51,18 +51,19 @@ FORMS += \
ui/mf_attack_hardnesteddialog.ui
TRANSLATIONS += \
i18n/zh_CN.ts \
i18n/en_US.ts
../i18n/zh_CN.ts \
../i18n/en_US.ts
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
VERSION = 0.2.5
VERSION = 0.2.6
QMAKE_TARGET_PRODUCT = "Proxmark3GUI"
QMAKE_TARGET_DESCRIPTION = "Proxmark3GUI"
QMAKE_TARGET_COMPANY = "wh201906"
RESOURCES += \
i18n/language.qrc
../i18n/language.qrc \
../config/config.qrc

@ -13,6 +13,8 @@ PM3Process::PM3Process(QThread* thread, QObject* parent): QProcess(parent)
connect(serialListener, &QTimer::timeout, this, &PM3Process::onTimeout);
connect(this, &PM3Process::readyRead, this, &PM3Process::onReadyRead);
portInfo = nullptr;
qRegisterMetaType<QProcess::ProcessError>("QProcess::ProcessError");
}
void PM3Process::connectPM3(const QString& path, const QStringList args)
@ -26,7 +28,8 @@ void PM3Process::connectPM3(const QString& path, const QStringList args)
currArgs = args;
// using "-f" option to make the client output flushed after every print.
start(path, args, QProcess::Unbuffered | QProcess::ReadWrite | QProcess::Text);
// single '\r' might appear. Don't use QProcess::Text there or '\r' is ignored.
start(path, args, QProcess::Unbuffered | QProcess::ReadWrite);
if(waitForStarted(10000))
{
waitForReadyRead(10000);
@ -59,10 +62,15 @@ void PM3Process::connectPM3(const QString& path, const QStringList args)
emit PM3StatedChanged(true, result);
}
else
{
emit HWConnectFailed();
kill();
}
}
setRequiringOutput(false);
}
void PM3Process::reconnectPM3()
{
connectPM3(currPath, currArgs);

@ -48,6 +48,7 @@ signals:
void PM3StatedChanged(bool st, const QString& info = "");
void newOutput(const QString& output);
void changeClientType(Util::ClientType);
void HWConnectFailed();
};
#endif // PM3PROCESS_H

@ -119,17 +119,29 @@ bool Util::chooseLanguage(QSettings* guiSettings, QMainWindow* window)
QStringList langList = langSettings->allKeys();
for(int i = 0; i < langList.size(); i++)
langMap.insert(langSettings->value(langList[i]).toString(), langList[i]);
langMap.insert(tr("Load from external file"), "(ext)");
langSettings->endGroup();
delete langSettings;
bool isOk = false;
QString selectedText = QInputDialog::getItem(window, "", "Choose a language:", langMap.keys(), 0, false, &isOk);
if(isOk)
QString selectedText = QInputDialog::getItem(window, "", tr("Choose a language:"), langMap.keys(), 0, false, &isOk);
if(!isOk)
return false;
if(langMap[selectedText] == "(ext)")
{
guiSettings->beginGroup("lang");
guiSettings->setValue("language", langMap[selectedText]);
QString extPath = QFileDialog::getOpenFileName(nullptr, tr("Select the translation file:"));
if(extPath.isEmpty())
return false;
guiSettings->beginGroup("language");
guiSettings->setValue("extPath", extPath);
guiSettings->endGroup();
guiSettings->sync();
}
guiSettings->beginGroup("language");
guiSettings->setValue("name", langMap[selectedText]);
guiSettings->endGroup();
guiSettings->sync();
return isOk;
}

@ -13,6 +13,7 @@
#include <QSettings>
#include <QMainWindow>
#include <QInputDialog>
#include <QFileDialog>
#include <QDockWidget>
#include "ui_mainwindow.h"

Binary file not shown.

@ -27,33 +27,34 @@ int main(int argc, char *argv[])
QSettings* settings = new QSettings("GUIsettings.ini", QSettings::IniFormat);
settings->setIniCodec("UTF-8");
settings->beginGroup("lang");
QString currLang = settings->value("language", "").toString();
settings->beginGroup("language");
QString languageFile = settings->value("extPath").toString();
QString languageName = settings->value("name").toString();
settings->endGroup();
if(currLang == "")
if(languageName == "")
{
if(Util::chooseLanguage(settings, &w))
{
settings->beginGroup("lang");
currLang = settings->value("language", "").toString();
settings->beginGroup("language");
languageName = settings->value("name").toString();
settings->endGroup();
}
else
currLang = "en_US";
languageName = "en_US";
}
if(languageName == "(ext)")
{
settings->beginGroup("language");
languageFile = settings->value("extPath").toString();
settings->endGroup();
}
if(currLang == "ext")
currLang = QFileDialog::getOpenFileName(nullptr, "Select the translation file:");
else
currLang = ":/i18n/" + currLang + ".qm";
languageFile = ":/i18n/" + languageName + ".qm";
QTranslator* translator = new QTranslator(&w);
if(translator->load(currLang))
{
if(translator->load(languageFile))
a.installTranslator(translator);
}
else
{
QMessageBox::information(&w, "Error", "Can't load " + currLang + " as translation file.");
}
QMessageBox::information(&w, "Error", "Can't load " + languageFile + " as translation file.");
delete settings;
w.initUI();
w.show();

@ -81,8 +81,10 @@ void LF::getLFConfig()
QVariantMap config = configMap["get config"].toMap();
QString cmd = config["cmd"].toString();
result = util->execCMDWithOutput(cmd, 400);
start = result.indexOf(config["field start"].toString());
end = result.indexOf(config["field end"].toString());
reMatch = QRegularExpression(config["field start"].toString(), QRegularExpression::MultilineOption).match(result);
start = reMatch.hasMatch() ? reMatch.capturedEnd() : 0;
reMatch = QRegularExpression(config["field end"].toString(), QRegularExpression::MultilineOption).match(result, start);
end = reMatch.hasMatch() ? reMatch.capturedStart() : result.length();
result = result.mid(start, end - start);
#if (QT_VERSION <= QT_VERSION_CHECK(5,14,0))
resultList = result.split("\n", QString::SkipEmptyParts);

@ -130,7 +130,7 @@ void Mifare::chk()
QString cmd = config["cmd"].toString();
int keyAindex = config["key A index"].toInt();
int keyBindex = config["key B index"].toInt();
QRegularExpression keyPattern = QRegularExpression(config["key pattern"].toString());
QRegularExpression keyPattern = QRegularExpression(config["key pattern"].toString(), QRegularExpression::MultilineOption);
cmd.replace("<card type>", config["card type"].toMap()[cardType.typeText].toString());
result = util->execCMDWithOutput(
@ -169,7 +169,7 @@ void Mifare::nested(bool isStaticNested)
cmd = config["cmd"].toString();
int keyAindex = config["key A index"].toInt();
int keyBindex = config["key B index"].toInt();
QRegularExpression keyPattern = QRegularExpression(config["key pattern"].toString());
QRegularExpression keyPattern = QRegularExpression(config["key pattern"].toString(), QRegularExpression::MultilineOption);
QRegularExpressionMatch reMatch;
QString result;
int offset = 0;
@ -212,7 +212,7 @@ void Mifare::nested(bool isStaticNested)
}
result = util->execCMDWithOutput(
cmd,
Util::ReturnTrigger(15000, {"Can't found", "Can't authenticate", keyPattern_res->pattern()}),
Util::ReturnTrigger(15000, {"Quit", "Can't found", "Can't authenticate", keyPattern_res->pattern()}),
true);
if(result.contains("static") && !isStaticNested)
@ -691,10 +691,14 @@ void Mifare::writeSelected(TargetType targetType)
{
result = _writeblk(item, KEY_B, keyBList->at(data_b2s(item)), dataList->at(item), TARGET_MIFARE);
}
if(!result)
if(!result && keyAList->at(data_b2s(item)) != "FFFFFFFFFFFF")
{
result = _writeblk(item, KEY_A, "FFFFFFFFFFFF", dataList->at(item), TARGET_MIFARE);
}
if(!result && keyBList->at(data_b2s(item)) != "FFFFFFFFFFFF") // for access bits like "80 f7 87", the block can only be written with keyB
{
result = _writeblk(item, KEY_B, "FFFFFFFFFFFF", dataList->at(item), TARGET_MIFARE);
}
}
else // key doesn't matter when writing to Chinese Magic Card and Emulator Memory
{
@ -743,14 +747,20 @@ void Mifare::writeSelected(TargetType targetType)
void Mifare::dump()
{
QVariantMap config = configMap["dump"].toMap();
util->execCMD(config["cmd"].toString());
QString cmd = config["cmd"].toString();
if(cmd.contains("<card type>"))
cmd.replace("<card type>", config["card type"].toMap()[cardType.typeText].toString());
util->execCMD(cmd);
Util::gotoRawTab();
}
void Mifare::restore()
{
QVariantMap config = configMap["restore"].toMap();
util->execCMD(config["cmd"].toString());
QString cmd = config["cmd"].toString();
if(cmd.contains("<card type>"))
cmd.replace("<card type>", config["card type"].toMap()[cardType.typeText].toString());
util->execCMD(cmd);
Util::gotoRawTab();
}

@ -2,6 +2,7 @@
#include "ui_mainwindow.h"
#include <QJsonDocument>
#include <QDirIterator>
MainWindow::MainWindow(QWidget *parent):
QMainWindow(parent)
@ -30,7 +31,9 @@ MainWindow::MainWindow(QWidget *parent):
settings->setIniCodec("UTF-8");
pm3Thread = new QThread(this);
connect(QApplication::instance(), &QApplication::aboutToQuit, pm3Thread, &QThread::quit);
pm3 = new PM3Process(pm3Thread);
connect(pm3Thread, &QThread::finished, pm3, &PM3Process::deleteLater);
pm3Thread->start();
pm3state = false;
clientWorkingDir = new QDir;
@ -77,7 +80,11 @@ MainWindow::~MainWindow()
void MainWindow::loadConfig()
{
QFile configList(ui->Set_Client_configPathEdit->text());
QString filename = ui->Set_Client_configFileBox->currentData().toString();
if(filename == "(ext)")
filename = ui->Set_Client_configPathEdit->text();
qDebug() << "config file:" << filename;
QFile configList(filename);
if(!configList.open(QFile::ReadOnly | QFile::Text))
{
QMessageBox::information(this, tr("Info"), tr("Failed to load config file"));
@ -104,18 +111,46 @@ void MainWindow::initUI() // will be called by main.app
void MainWindow::on_portSearchTimer_timeout()
{
QStringList newPortList;
QStringList newPortList; // for actural port name
QStringList newPortNameList; // for display name
const QString hint = " *";
foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts())
{
// qDebug() << info.isBusy() << info.isNull() << info.portName() << info.description();
// qDebug() << info.isNull() << info.portName() << info.description() << info.serialNumber() << info.manufacturer();
if(!info.isNull())
newPortList << info.portName();
{
QString idString = (info.description() + info.serialNumber() + info.manufacturer()).toLower();
QString portName = info.portName();
newPortList << portName;
if(info.hasVendorIdentifier() && info.hasProductIdentifier())
{
quint16 vid = info.vendorIdentifier();
quint16 pid = info.productIdentifier();
if(vid == 0x9AC4 && pid == 0x4B8F)
portName += hint;
else if(vid == 0x2D2D && pid == 0x504D)
portName += hint;
}
else if(idString.contains("proxmark") || idString.contains("iceman"))
portName += hint;
newPortNameList << portName;
}
}
if(newPortList != portList) // update PM3_portBox when available ports changed
{
portList = newPortList;
ui->PM3_portBox->clear();
ui->PM3_portBox->addItems(portList);
int selectId = -1;
for(int i = 0; i < portList.size(); i++)
{
ui->PM3_portBox->addItem(newPortNameList[i], newPortList[i]);
if(selectId == -1 && newPortNameList[i].endsWith(hint))
selectId = i;
}
if(selectId != -1)
ui->PM3_portBox->setCurrentIndex(selectId);
}
}
@ -123,7 +158,7 @@ void MainWindow::on_PM3_connectButton_clicked()
{
qDebug() << "Main:" << QThread::currentThread();
QString port = ui->PM3_portBox->currentText();
QString port = ui->PM3_portBox->currentData().toString();
QString startArgs = ui->Set_Client_startArgsEdit->text();
// on RRG repo, if no port is specified, the client will search the available port
@ -193,6 +228,18 @@ void MainWindow::on_PM3_connectButton_clicked()
envSetProcess.kill();
}
void MainWindow::onPM3ErrorOccurred(QProcess::ProcessError error)
{
qDebug() << "PM3 Error:" << error << pm3->errorString();
if(error == QProcess::FailedToStart)
QMessageBox::information(this, tr("Info"), tr("Failed to start the client"));
}
void MainWindow::onPM3HWConnectFailed()
{
QMessageBox::information(this, tr("Info"), tr("Failed to connect to the hardware"));
}
void MainWindow::onPM3StateChanged(bool st, const QString& info)
{
pm3state = st;
@ -220,7 +267,8 @@ void MainWindow::on_PM3_disconnectButton_clicked()
void MainWindow::refreshOutput(const QString& output)
{
// qDebug() << "MainWindow::refresh:" << output;
ui->Raw_outputEdit->appendPlainText(output);
ui->Raw_outputEdit->moveCursor(QTextCursor::End);
ui->Raw_outputEdit->insertPlainText(output);
ui->Raw_outputEdit->moveCursor(QTextCursor::End);
}
@ -1054,6 +1102,8 @@ void MainWindow::uiInit()
ui->PM3_pathEdit->setText(settings->value("path", "proxmark3").toString());
settings->endGroup();
ui->Set_Client_GUIWorkingDirLabel->setText(QDir::currentPath());
settings->beginGroup("Client_Args");
ui->Set_Client_startArgsEdit->setText(settings->value("args", "<port> -f").toString());
settings->endGroup();
@ -1068,11 +1118,27 @@ void MainWindow::uiInit()
ui->Set_Client_keepClientActiveBox->setChecked(keepClientActive);
settings->endGroup();
QDirIterator configFiles(":/config/");
ui->Set_Client_configFileBox->blockSignals(true);
while(configFiles.hasNext())
{
configFiles.next();
ui->Set_Client_configFileBox->addItem(configFiles.fileName(), configFiles.filePath());
}
ui->Set_Client_configFileBox->addItem(tr("External file"), "(ext)");
int configId = -1;
settings->beginGroup("Client_Env");
ui->Set_Client_envScriptEdit->setText(settings->value("scriptPath").toString());
ui->Set_Client_workingDirEdit->setText(settings->value("workingDir", "../data").toString());
ui->Set_Client_configPathEdit->setText(settings->value("configPath", "config.json").toString());
configId = ui->Set_Client_configFileBox->findData(settings->value("configFile"));
ui->Set_Client_configPathEdit->setText(settings->value("extConfigFilePath", "config.json").toString());
settings->endGroup();
if(configId != -1)
ui->Set_Client_configFileBox->setCurrentIndex(configId);
ui->Set_Client_configFileBox->blockSignals(false);
on_Set_Client_configFileBox_currentIndexChanged(ui->Set_Client_configFileBox->currentIndex());
ui->MF_RW_keyTypeBox->addItem("A", Mifare::KEY_A);
ui->MF_RW_keyTypeBox->addItem("B", Mifare::KEY_B);
@ -1091,6 +1157,8 @@ void MainWindow::signalInit()
connect(this, &MainWindow::reconnectPM3, pm3, &PM3Process::reconnectPM3);
connect(pm3, &PM3Process::PM3StatedChanged, this, &MainWindow::onPM3StateChanged);
connect(pm3, &PM3Process::PM3StatedChanged, util, &Util::setRunningState);
connect(pm3, &PM3Process::errorOccurred, this, &MainWindow::onPM3ErrorOccurred);
connect(pm3, &PM3Process::HWConnectFailed, this, &MainWindow::onPM3HWConnectFailed);
connect(this, &MainWindow::killPM3, pm3, &PM3Process::killPM3);
connect(this, &MainWindow::setProcEnv, pm3, &PM3Process::setProcEnv);
connect(this, &MainWindow::setWorkingDir, pm3, &PM3Process::setWorkingDir);
@ -1274,7 +1342,7 @@ void MainWindow::on_Set_Client_workingDirEdit_editingFinished()
void MainWindow::on_Set_Client_configPathEdit_editingFinished()
{
settings->beginGroup("Client_Env");
settings->setValue("configPath", ui->Set_Client_configPathEdit->text());
settings->setValue("extConfigFilePath", ui->Set_Client_configPathEdit->text());
settings->endGroup();
}
@ -1412,3 +1480,11 @@ void MainWindow::on_LF_LFConf_resetButton_clicked()
setState(true);
}
void MainWindow::on_Set_Client_configFileBox_currentIndexChanged(int index)
{
ui->Set_Client_configPathEdit->setVisible(ui->Set_Client_configFileBox->itemData(index).toString() == "(ext)");
settings->beginGroup("Client_Env");
settings->setValue("configFile", ui->Set_Client_configFileBox->currentData());
settings->endGroup();
}

@ -51,7 +51,7 @@ public:
~MainWindow();
void initUI();
bool eventFilter(QObject *watched, QEvent *event);
bool eventFilter(QObject *watched, QEvent *event) override;
public slots:
void refreshOutput(const QString& output);
void refreshCMD(const QString& cmd);
@ -60,6 +60,8 @@ public slots:
void MF_onMFCardTypeChanged(int id, bool st);
void on_Raw_keyPressed(QObject *obj_addr, QEvent &event);
void on_MF_keyWidget_resized(QObject *obj_addr, QEvent &event);
void onPM3ErrorOccurred(QProcess::ProcessError error);
void onPM3HWConnectFailed();
private slots:
void on_PM3_connectButton_clicked();
@ -207,6 +209,9 @@ private slots:
void on_Set_Client_configPathEdit_editingFinished();
void setState(bool st);
void on_Set_Client_configFileBox_currentIndexChanged(int index);
private:
Ui::MainWindow* ui;
QButtonGroup* MFCardTypeBtnGroup;

@ -1454,8 +1454,8 @@ When setting the freq, the &quot;hw setlfdivisor&quot; will also be called.</str
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="LF_LFConf_averagingBox">
<property name="text">
<string/>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
@ -2650,6 +2650,23 @@ or the communication between a tag and a reader.</string>
<string>Client</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_10">
<item>
<widget class="QLabel" name="label_65">
<property name="text">
<string>GUI working directory:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="Set_Client_GUIWorkingDirLabel"/>
</item>
<item>
<widget class="Line" name="line_9">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_11">
<property name="text">
@ -2746,10 +2763,34 @@ or the communication between a tag and a reader.</string>
<item>
<widget class="QLabel" name="label_63">
<property name="text">
<string>Config file path(Reconnect to apply):</string>
<string>Config file(Reconnect to apply):</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_15">
<item>
<widget class="QComboBox" name="Set_Client_configFileBox">
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToContents</enum>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_13">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QLineEdit" name="Set_Client_configPathEdit">
<property name="text">

Loading…
Cancel
Save