diff --git a/common/util.cpp b/common/util.cpp
index 1da8256..e9bf045 100644
--- a/common/util.cpp
+++ b/common/util.cpp
@@ -22,13 +22,16 @@ void Util::processOutput(const QString& output)
void Util::execCMD(const QString& cmd)
{
- qDebug() << cmd;
+ qDebug() << "executing: " << cmd;
if(isRunning)
emit write(cmd + "\r\n");
}
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);
@@ -42,27 +45,32 @@ QString Util::execCMDWithOutput(const QString& cmd, ReturnTrigger trigger)
execCMD(cmd);
while(QTime::currentTime() < targetTime)
{
+ if(!isRunning)
+ break;
QApplication::processEvents();
for(QString otpt : trigger.expectedOutputs)
{
re.setPattern(otpt);
isResultFound = re.match(*requiredOutput).hasMatch();
- if(requiredOutput->contains(otpt))
+ if(isResultFound)
+ {
+ qDebug() << "output Matched: " << *requiredOutput;
break;
+ }
}
if(isResultFound)
{
delay(200);
break;
}
- if(timeStamp > currTime)
+ if(timeStamp > currTime) //has new output
{
currTime = timeStamp;
targetTime = timeStamp.addMSecs(trigger.waitTime);
}
}
isRequiringOutput = false;
- return *requiredOutput;
+ return (isResultFound || trigger.expectedOutputs.isEmpty() ? *requiredOutput : "");
}
void Util::delay(unsigned int msec)
diff --git a/common/util.h b/common/util.h
index 04a4ec0..05d0467 100644
--- a/common/util.h
+++ b/common/util.h
@@ -50,6 +50,7 @@ public:
QString execCMDWithOutput(const QString& cmd, ReturnTrigger trigger = 10000);
void delay(unsigned int msec);
ClientType getClientType();
+ static const int rawTabIndex = 1;
public slots:
void processOutput(const QString& output);
void setClientType(Util::ClientType clientType);
diff --git a/lang/en_US.ts b/lang/en_US.ts
index b881d0e..5a58b0b 100644
--- a/lang/en_US.ts
+++ b/lang/en_US.ts
@@ -310,534 +310,681 @@ It could make the whole sector blocked irreversibly!
-
+
-
+
-
+
320
-
+
1024
-
+
2048
-
+
4096
-
+
-
-
+
+
-
-
+
+
+
-
-
+
+
-
+
+
-
+
-
+
-
+
-
+
-
+
-
+
+
+
+
+
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
-
+
+
-
+
-
+
-
-
+
+
-
-
+
+
+
-
+
-
+
-
+
-
+
-
+
-
+
-
-
+
+
-
-
+
+
-
-
-
+
+
+
-
-
-
+
+
+
-
-
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
-
+
-
+
-
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
+
-
-
-
+
+
-
+
-
-
-
+
+
+
-
+
-
+
-
+
-
+
-
+
-
-
+
+
-
+
-
+
-
+
-
+
-
+
-
-
-
+
+
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
-
+
+
-
-
+
+
+
+
+
+
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -845,31 +992,56 @@ It could make the whole sector blocked irreversibly!
Mifare
-
+
-
-
-
-
+
+
+
+
+
+
+
-
+
-
-
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lang/languages.ini b/lang/languages.ini
new file mode 100644
index 0000000..e69de29
diff --git a/lang/zh_CN.ts b/lang/zh_CN.ts
index 0990cac..ac86f92 100644
--- a/lang/zh_CN.ts
+++ b/lang/zh_CN.ts
@@ -314,534 +314,690 @@ It could make the whole sector blocked irreversibly!
Mifare(IC)卡
-
+
选中密码块
-
+
卡片类型
-
+
320
-
+
1024
-
+
2048
-
+
4096
-
+
文件
-
-
+
+
加载
-
-
+
+
+
保存
-
-
+
+
数据
-
+
+
密钥
-
+
破解
-
+
读卡信息
-
+
验证默认密码
-
+
Nested攻击
-
+
Hardested攻击
-
+
+
+ Darkside攻击
+
+
+
读/写
-
+
块:
-
+
密钥:
-
+
密钥类型:
-
+
嗅探(Snoop)
-
+
列出嗅探数据
-
+
数据:
-
+
普通卡(需要密码)
-
+
Dump命令
-
+
Restore命令
-
+
UID卡(不需要密码)
-
+
锁定UFUID卡
-
-
+
+
关于UID卡
-
+
设置卡参数
-
+
擦除
-
-
+
+
模拟
-
-
+
+
+
清空
-
+
全选
-
+
密码区->密码
-
+
密码区<-密码
-
+
填充密码
-
+
Trailer解码
-
+
设置字体
-
-
+
+
读取单个区
-
-
+
+
写入单个区
-
-
-
+
+
+
读取选中块
-
-
-
+
+
+
写入选中块
-
-
+
+
嗅探
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
原始命令
-
-
+
+
命令历史:
-
+
清空历史
-
+
发送
-
+
清空输出
-
-
-
-
-
-
-
-
-
-
-
+
+
+ 设置
+
+
+
+
+ 客户端
+
+
+
+
+ 预加载环境变量
+
+
+
+
+ 值
+
+
+
+
+ 添加
+
+
+
+
+ 删除
+
+
+
+
+ 注意:
+如果变量名已经存在,则会将新变量值添加至现有变量值之前,启动客户端时会优先使用此处添加的变量值。
+此处添加的环境变量不会影响其它程序。
+
+
+
+
+ 启动参数
+
+
+
+
+
+
+
+
+
+ 注意:
+-f选项用于使客户端实时返回命令回显,必须添加
+部分情况下启动参数需设置为"-p /dev/<port> -f"
+或"-p <port> -f"
+
+
+
+ 注意:-f选项用于使客户端实时返回命令回显,必须添加
+
+
+
+
+ 保持所有按钮可点击,即使未连接客户端或有任务正在运行
+
+
+
+
+ 图形化界面
+
+
+
+
+ 语言
+
+
+
+
+
+
+
+
+
+
+
+
+
信息
-
+
请先选择端口
-
+
已连接
-
-
-
+
+
未连接
-
+
二进制数据文件(*.bin *.dump);;文本数据文件(*.txt *.eml);;所有文件(*.*)
-
-
-
+
+
+
无法打开
-
+
确定?
-
+
检查更新
-
+
部分数据和密码将被清除
-
+
请选择数据窗口和密钥窗口的字体
-
+
数据必须由32个十六进制字符组成(中间可含有空格)
-
-
+
+
密钥必须由12个十六进制字符组成(中间可含有空格)
-
+
请选择数据文件:
-
+
请选择密钥文件:
-
+
二进制密钥文件(*.bin *.dump)二进制密钥文件(*.bin *.dump);所有文件(*.*)
-
+
请选择数据文件保存的位置:
-
+
二进制数据文件(*.bin *.dump);文本数据文件(*.txt *.eml)
-
-
-
+
+
+
无法保存至
-
+
请选择密钥文件保存的位置:
-
+
二进制密码文件(*.bin *.dump)
-
+
普通Mifare卡的块0无法写入,卡号也不能更改
-
+
UID卡(在国外叫Chinese Magic Card)的块0可写,卡号可变。
-
+
国外把UID卡分为Chinese Magic Card Gen1和Gen2
-
+
-
+
指通常所说的UID卡,可以通过后门指令直接读写块而无需密码,在PM3和此GUI中有特殊命令处理这类卡片
-
+
-
+
这个叫法在国内比较罕见,在国外指CUID/FUID/UFUID这类对后门指令不响应的卡(防火墙卡)
-
+
以下是Gen2卡的详细介绍
-
+
CUID卡:
-
+
可通过普通的写块命令来写块0,可重复擦写
-
+
(hf mf wrbl 0 A FFFFFFFFFFFF <待写入数据>)
-
+
FUID卡:
-
+
块0只能写入一次
-
+
(更高级的穿防火墙卡,可以过一些能识别出CUID卡的读卡器)
-
+
UFUID卡:
-
+
锁卡前和普通UID/CUID卡一样可以反复读写块0,用特殊命令锁卡后就和FUID卡一样了
-
+
所有UID卡都似乎更容易被Nested攻击破解
-
+
请选择trace文件:
-
+
Trace文件(*.trc);;所有文件(*.*)
-
+
请选择trace文件保存的位置:
-
+
Trace文件(*.trc)
-
-
+
+
空闲
-
-
+
+
+ 停止
+
+
+
+
扇区
-
+
块
-
+
密钥A
-
+
密钥B
-
+
固件版本:
-
+
连接状态:
-
+
运行状态:
-
+
正在运行
@@ -849,31 +1005,58 @@ It could make the whole sector blocked irreversibly!
Mifare
-
+
成功!
-
-
-
-
+
+
+
+
+
+
+
信息
-
+
请至少提供一个已知密码
-
-
+
+
失败!
-
+
+
+ 控制位无效!
+使用该控制位可能导致目标扇区损坏且无法恢复!
+确定要写入吗?
+
+
+
+
+ 成功!
+
+
+
+
+ 写入以下块失败:
+
+
+
+
+ 选中这些块?
+
+
+
读卡失败。
diff --git a/module/mifare.cpp b/module/mifare.cpp
index 4ac028b..bc6b10e 100644
--- a/module/mifare.cpp
+++ b/module/mifare.cpp
@@ -106,7 +106,7 @@ QString Mifare::info(bool isRequiringOutput)
else
{
util->execCMD("hf 14a info");
- ui->funcTab->setCurrentIndex(1);
+ ui->funcTab->setCurrentIndex(Util::rawTabIndex);
return "";
}
}
@@ -115,17 +115,16 @@ QString Mifare::info(bool isRequiringOutput)
void Mifare::chk()
{
QRegularExpressionMatch reMatch;
- QString result = util->execCMDWithOutput(
- "hf mf chk *"
- + QString::number(cardType.type)
- + " ?",
- Util::ReturnTrigger(1000 + cardType.sector_size * 200, {"No valid", "\\|---\\|----------------\\|----------------\\|"}));
- qDebug() << result;
-
+ QString result;
int offset = 0;
QString data;
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", "\\|---\\|----------------\\|----------------\\|"}));
for(int i = 0; i < cardType.sector_size; i++)
{
reMatch = keyPattern->match(result, offset);
@@ -148,6 +147,11 @@ void Mifare::chk()
}
else if(util->getClientType() == Util::CLIENTTYPE_ICEMAN)
{
+ result = util->execCMDWithOutput(
+ "hf mf chk *"
+ + QString::number(cardType.type)
+ + " ?",
+ Util::ReturnTrigger(1000 + cardType.sector_size * 200, {"No valid", "\\|---\\|----------------\\|---\\|----------------\\|"}));
for(int i = 0; i < cardType.sector_size; i++)
{
reMatch = keyPattern_res->match(result, offset);
@@ -213,7 +217,8 @@ void Mifare::nested()
result = util->execCMDWithOutput(
"hf mf nested "
+ QString::number(cardType.type)
- + knownKeyInfo, 10000);
+ + knownKeyInfo,
+ Util::ReturnTrigger(10000, {"key is wrong", "\\|000\\|"}));
}
else
{
@@ -249,7 +254,7 @@ 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()
@@ -257,12 +262,12 @@ void Mifare::darkside()
if(util->getClientType() == Util::CLIENTTYPE_OFFICIAL)
{
util->execCMD("hf mf mifare");
- ui->funcTab->setCurrentIndex(1);
+ ui->funcTab->setCurrentIndex(Util::rawTabIndex);
}
else if(util->getClientType() == Util::CLIENTTYPE_ICEMAN)
{
util->execCMD("hf mf darkside");
- ui->funcTab->setCurrentIndex(1);
+ ui->funcTab->setCurrentIndex(Util::rawTabIndex);
}
}
@@ -270,19 +275,19 @@ void Mifare::darkside()
void Mifare::sniff()
{
util->execCMD("hf mf sniff");
- ui->funcTab->setCurrentIndex(1);
+ ui->funcTab->setCurrentIndex(Util::rawTabIndex);
}
void Mifare::snoop()
{
util->execCMD("hf 14a snoop");
- ui->funcTab->setCurrentIndex(1);
+ ui->funcTab->setCurrentIndex(Util::rawTabIndex);
}
void Mifare::list()
{
util->execCMD("hf list mf");
- ui->funcTab->setCurrentIndex(1);
+ ui->funcTab->setCurrentIndex(Util::rawTabIndex);
}
QString Mifare::_readblk(int blockId, KeyType keyType, const QString& key, TargetType targetType, int waitTime)
@@ -709,13 +714,13 @@ void Mifare::writeSelected(TargetType targetType)
void Mifare::dump()
{
util->execCMD("hf mf dump");
- ui->funcTab->setCurrentIndex(1);
+ ui->funcTab->setCurrentIndex(Util::rawTabIndex);
}
void Mifare::restore()
{
util->execCMD("hf mf restore");
- ui->funcTab->setCurrentIndex(1);
+ ui->funcTab->setCurrentIndex(Util::rawTabIndex);
}
void Mifare::wipeC()
@@ -724,7 +729,7 @@ void Mifare::wipeC()
"hf mf cwipe "
+ QString::number(cardType.type)
+ " f");
- ui->funcTab->setCurrentIndex(1);
+ ui->funcTab->setCurrentIndex(Util::rawTabIndex);
}
void Mifare::setParameterC()
@@ -741,7 +746,7 @@ void Mifare::setParameterC()
MF_UID_parameterDialog dialog(lis[0].toUpper(), lis[1].toUpper(), lis[2].mid(0, 2).toUpper());
connect(&dialog, &MF_UID_parameterDialog::sendCMD, util, &Util::execCMD);
if(dialog.exec() == QDialog::Accepted)
- ui->funcTab->setCurrentIndex(1);
+ ui->funcTab->setCurrentIndex(Util::rawTabIndex);
}
}
@@ -765,19 +770,19 @@ void Mifare::simulate()
MF_Sim_simDialog dialog(cardType.type);
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);
+ ui->funcTab->setCurrentIndex(Util::rawTabIndex);
}
void Mifare::saveSniff(const QString& file)
{
util->execCMD("hf list mf -s " + file);
- ui->funcTab->setCurrentIndex(1);
+ ui->funcTab->setCurrentIndex(Util::rawTabIndex);
}
void Mifare::data_syncWithDataWidget(bool syncAll, int block)
diff --git a/ui/mainwindow.cpp b/ui/mainwindow.cpp
index ed0f0ca..c4d0f7e 100644
--- a/ui/mainwindow.cpp
+++ b/ui/mainwindow.cpp
@@ -92,6 +92,9 @@ void MainWindow::on_PM3_connectButton_clicked()
saveClientPath(ui->PM3_pathEdit->text());
emit connectPM3(ui->PM3_pathEdit->text(), port);
}
+ QProcess proc;
+ QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
+ //env.insert();
}
void MainWindow::onPM3StateChanged(bool st, const QString& info)
@@ -112,11 +115,8 @@ void MainWindow::onPM3StateChanged(bool st, const 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)
@@ -128,7 +128,19 @@ void MainWindow::refreshOutput(const QString& output)
void MainWindow::on_stopButton_clicked()
{
-
+ if(!pm3state)
+ on_PM3_disconnectButton_clicked();
+ else
+ {
+ on_PM3_disconnectButton_clicked();
+ for(int i = 0; i < 10; i++)
+ {
+ util->delay(200);
+ if(!pm3state)
+ break;
+ }
+ on_PM3_connectButton_clicked();
+ }
}
// *********************************************************
@@ -866,7 +878,6 @@ void MainWindow::uiInit()
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, 450);
@@ -875,7 +886,6 @@ void MainWindow::uiInit()
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, 125);
ui->MF_keyWidget->setColumnWidth(2, 125);
@@ -917,11 +927,21 @@ 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");
+ ui->Set_Client_forceEnabledBox->setChecked(settings->value("state", false).toBool());
+ 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();
+
+ loadClientPreloadEnv();
}
void MainWindow::signalInit()
@@ -1059,3 +1079,65 @@ void MainWindow::on_MF_Attack_darksideButton_clicked()
mifare->darkside();
setState(true);
}
+
+void MainWindow::on_Set_Client_envDeleteButton_clicked()
+{
+ ui->Set_Client_envTable->removeRow(ui->Set_Client_envTable->currentRow());
+}
+
+void MainWindow::on_Set_Client_envAddButton_clicked()
+{
+ ui->Set_Client_envTable->insertRow(ui->Set_Client_envTable->rowCount());
+}
+
+void MainWindow::on_Set_Client_envClearButton_clicked()
+{
+ ui->Set_Client_envTable->clearContents();
+ ui->Set_Client_envTable->setRowCount(0);
+}
+
+void MainWindow::on_Set_Client_envSaveButton_clicked()
+{
+ settings->beginGroup("Client_Env");
+ for(int i = 0; i < ui->Set_Client_envTable->rowCount(); i++)
+ {
+ QTableWidgetItem* key = ui->Set_Client_envTable->item(i, 0);
+ QTableWidgetItem* val = ui->Set_Client_envTable->item(i, 1);
+ if(key == nullptr || val == nullptr || key->text().isEmpty() || val->text().isEmpty())
+ continue;
+ settings->setValue(key->text(), val->text());
+ qDebug() << "Env saved: " << i << key->text() << val->text();
+ }
+ settings->endGroup();
+}
+
+void MainWindow::loadClientPreloadEnv()
+{
+ ui->Set_Client_envTable->clearContents();
+ settings->beginGroup("Client_Env");
+ QStringList keyList = settings->allKeys();
+ ui->Set_Client_envTable->setRowCount(keyList.size());
+ for(int i = 0; i < keyList.size(); i++)
+ {
+ ui->Set_Client_envTable->setItem(i, 0, new QTableWidgetItem(keyList[i]));
+ ui->Set_Client_envTable->setItem(i, 1, new QTableWidgetItem(settings->value(keyList[i]).toString()));
+ }
+ settings->endGroup();
+}
+
+
+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");
+ settings->setValue("state", arg1 == Qt::Checked);
+ settings->endGroup();
+}
+
+
diff --git a/ui/mainwindow.h b/ui/mainwindow.h
index 948e991..e0f9e9d 100644
--- a/ui/mainwindow.h
+++ b/ui/mainwindow.h
@@ -20,6 +20,7 @@
#include
#include
#include
+#include
#include "common/myeventfilter.h"
#include "common/pm3process.h"
@@ -90,7 +91,6 @@ private slots:
void on_MF_RW_writeSelectedButton_clicked();
-
void on_MF_RW_dumpButton_clicked();
void on_MF_RW_restoreButton_clicked();
@@ -156,6 +156,20 @@ private slots:
void on_MF_Attack_darksideButton_clicked();
+ void on_Set_Client_envDeleteButton_clicked();
+
+ void on_Set_Client_envAddButton_clicked();
+
+ void on_Set_Client_envSaveButton_clicked();
+
+ void loadClientPreloadEnv();
+
+ void on_Set_Client_startArgsEdit_editingFinished();
+
+ void on_Set_Client_forceEnabledBox_stateChanged(int arg1);
+
+ void on_Set_Client_envClearButton_clicked();
+
private:
Ui::MainWindow* ui;
QButtonGroup* typeBtnGroup;
diff --git a/ui/mainwindow.ui b/ui/mainwindow.ui
index 6801816..813c8c3 100644
--- a/ui/mainwindow.ui
+++ b/ui/mainwindow.ui
@@ -6,7 +6,7 @@
0
0
- 970
+ 1029
770
@@ -120,7 +120,7 @@
- 0
+ 4
@@ -170,6 +170,12 @@
QAbstractItemView::SingleSelection
+
+ true
+
+
+ false
+
20
@@ -329,6 +335,9 @@
QAbstractItemView::SingleSelection
+
+ false
+
20
@@ -1515,6 +1524,259 @@
+
+
+ Settings
+
+
+ -
+
+
-
+
+
+ Client
+
+
+
-
+
+
+ Preload environment variables
+
+
+
+ -
+
+
-
+
+
+ QAbstractItemView::SelectRows
+
+
+ true
+
+
+ false
+
+
+
+ Key
+
+
+
+
+ Value
+
+
+
+
+ -
+
+
-
+
+
+ Add
+
+
+
+ -
+
+
+ Delete
+
+
+
+ -
+
+
+ Clear
+
+
+
+ -
+
+
+ Save
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
+
+
+ -
+
+
+ Note:
+If the variable name already exists, this app will add the new value to the head of the existing one, so these new values have higher priority when calling Proxmark3 client.
+The environment variables added here won't affect other apps.
+
+
+ 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
+
+
+
+
+
+ -
+
+
+ GUI
+
+
+
-
+
+
+ Language
+
+
+
+ -
+
+
-
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+