diff --git a/CHANGELOG.md b/CHANGELOG.md
index 698e40f..9b55da5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,15 +2,26 @@
[中文](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)
++ Fix bug [#28](https://github.com/wh201906/Proxmark3GUI/issues/28)
### V0.2.4
+ Clone EM410x card to T55xx card
### V0.2.3
-+ Fix bug [#27](https://github.com/wh201906/Proxmark3GUI/issues/27)
-+ Try to support Non-ASCII path
++ Fix bug [#27](https://github.com/wh201906/Proxmark3GUI/issues/27)
++ Try to support Non-ASCII path
### V0.2.2
+ Load command format from external json file
diff --git a/README.md b/README.md
index fca86dd..87b0920 100644
--- a/README.md
+++ b/README.md
@@ -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
diff --git a/config/config.qrc b/config/config.qrc
new file mode 100644
index 0000000..6d3107c
--- /dev/null
+++ b/config/config.qrc
@@ -0,0 +1,7 @@
+
+
+ config_official.json
+ config_rrgv4.13441.json
+ config_rrgv4.15864.json
+
+
diff --git a/config/config_official.json b/config/config_official.json
index 0438ed8..b28c942 100644
--- a/config/config_official.json
+++ b/config/config_official.json
@@ -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": {
+ "mini": "0",
+ "1k": "1",
+ "2k": "2",
+ "4k": "4"
+ }
},
"restore": {
- "cmd": "hf mf restore"
+ "cmd": "hf mf restore ",
+ "card type": {
+ "mini": "0",
+ "1k": "1",
+ "2k": "2",
+ "4k": "4"
+ }
},
"emulator wipe": {
"cmd": "hf mf eclr"
@@ -192,14 +220,14 @@
"divisor cmd": "hw setlfdivisor "
}
},
- "t55xx":{
- "clone em410x":{
- "read":"lf search",
- "successful read flag":"Valid EM410x ID",
- "pattern":"EM TAG ID\\s*:\\s\\K[0-9a-fA-F]{10}",
- "clone cmd":"lf em 410xwrite ",
- "t5555 flag":"0",
- "t55x7 flag":"1"
+ "t55xx": {
+ "clone em410x": {
+ "read": "lf search",
+ "successful read flag": "Valid EM410x ID",
+ "pattern": "EM TAG ID\\s*:\\s\\K[0-9a-fA-F]{10}",
+ "clone cmd": "lf em 410xwrite ",
+ "t5555 flag": "0",
+ "t55x7 flag": "1"
}
}
}
\ No newline at end of file
diff --git a/config/config_rrgv4.13.json b/config/config_rrgv4.13441.json
similarity index 79%
rename from config/config_rrgv4.13.json
rename to config/config_rrgv4.13441.json
index 8d5680d..40784b4 100644
--- a/config/config_rrgv4.13.json
+++ b/config/config_rrgv4.13441.json
@@ -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": {
+ "mini": "mini",
+ "1k": "1k",
+ "2k": "2k",
+ "4k": "4k"
+ }
},
"restore": {
- "cmd": "hf mf restore"
+ "cmd": "hf mf restore --",
+ "card type": {
+ "mini": "mini",
+ "1k": "1k",
+ "2k": "2k",
+ "4k": "4k"
+ }
},
"emulator wipe": {
"cmd": "hf mf eclr"
@@ -200,14 +228,14 @@
"divisor cmd": "hw setlfdivisor -d "
}
},
- "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 ",
- "t5555 flag":"--q5",
- "t55x7 flag":""
+ "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 ",
+ "t5555 flag": "--q5",
+ "t55x7 flag": ""
}
}
}
\ No newline at end of file
diff --git a/config/config_rrgv4.15864.json b/config/config_rrgv4.15864.json
new file mode 100644
index 0000000..a9aad17
--- /dev/null
+++ b/config/config_rrgv4.15864.json
@@ -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 -- --blk - -k ",
+ "static cmd": "hf mf staticnested -- --blk - -k ",
+ "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": {
+ "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": {
+ "mini": "mini",
+ "1k": "1k",
+ "2k": "2k",
+ "4k": "4k"
+ }
+ },
+ "restore": {
+ "cmd": "hf mf restore --",
+ "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 ",
+ "data pattern": "([0-9a-fA-F]{2} ){15}[0-9a-fA-F]{2}"
+ },
+ "Magic Card read block": {
+ "cmd": "hf mf cgetblk --blk ",
+ "data pattern": "([0-9a-fA-F]{2} ){15}[0-9a-fA-F]{2}"
+ },
+ "normal read block": {
+ "cmd": "hf mf rdbl --blk - -k ",
+ "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 "
+ },
+ "load sniff": {
+ "cmd": "trace load -f ",
+ "show cmd": "trace list --buffer -t mf"
+ },
+ "hardnested": {
+ "cmd": "hf mf hardnested --blk - -k --tblk --t",
+ "known key type": {
+ "A": "a",
+ "B": "b"
+ },
+ "target key type": {
+ "A": "a",
+ "B": "b"
+ }
+ },
+ "normal read sector": {
+ "cmd": "hf mf rdsc --sec - -k ",
+ "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 ",
+ "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 - -k -d ",
+ "key type": {
+ "A": "a",
+ "B": "b"
+ },
+ "failed flag": [
+ "fail",
+ "error"
+ ]
+ },
+ "Magic Card write block": {
+ "cmd": "hf mf csetblk --blk -d ",
+ "failed flag": [
+ "fail",
+ "error"
+ ]
+ },
+ "emulator write block": {
+ "cmd": "hf mf esetblk --blk -d "
+ },
+ "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 --atqa --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 "
+ },
+ "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 --bps --dec --avg --trig --skip ",
+ "divisor cmd": "hw setlfdivisor -d "
+ }
+ },
+ "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 ",
+ "t5555 flag": "--q5",
+ "t55x7 flag": ""
+ }
+ }
+}
\ No newline at end of file
diff --git a/deploy/deploy.py b/deploy/deploy.py
index 44ced91..f11e277 100644
--- a/deploy/deploy.py
+++ b/deploy/deploy.py
@@ -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:
diff --git a/doc/CHANGELOG/CHANGELOG_zh_CN.md b/doc/CHANGELOG/CHANGELOG_zh_CN.md
index f71b335..8e660a4 100644
--- a/doc/CHANGELOG/CHANGELOG_zh_CN.md
+++ b/doc/CHANGELOG/CHANGELOG_zh_CN.md
@@ -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)
diff --git a/doc/README/README_zh_CN.md b/doc/README/README_zh_CN.md
index 2f5557f..3590bde 100644
--- a/doc/README/README_zh_CN.md
+++ b/doc/README/README_zh_CN.md
@@ -59,7 +59,6 @@ release页面中有含客户端的GUI。这个GUI也可以搭配你自己的客
mkdir build && cd build
qmake ../src
make -j4 && make clean
- cp -r ../config ./
./Proxmark3GUI
## 在macOS系统下编译
diff --git a/i18n/en_US.qm b/i18n/en_US.qm
new file mode 100644
index 0000000..937ea3e
Binary files /dev/null and b/i18n/en_US.qm differ
diff --git a/src/i18n/en_US.ts b/i18n/en_US.ts
similarity index 59%
rename from src/i18n/en_US.ts
rename to i18n/en_US.ts
index 0404a72..f8b79a6 100644
--- a/src/i18n/en_US.ts
+++ b/i18n/en_US.ts
@@ -4,35 +4,35 @@
MF_Attack_hardnestedDialog
-
+
-
+
-
-
+
+
-
-
+
+
-
-
+
+
-
+
@@ -40,107 +40,107 @@
MF_Sim_simDialog
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -148,22 +148,22 @@
MF_UID_parameterDialog
-
+
-
+
-
+
-
+
@@ -171,155 +171,155 @@
MF_trailerDecoderDialog
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
-
+
+
-
-
+
+
-
+
-
+
-
+
-
-
+
+
-
+
-
-
+
+
-
+
-
+
-
+
-
+
-
-
-
-
-
-
+
+
+
+
+
+
-
+
@@ -327,1027 +327,1049 @@ It could make the whole sector blocked irreversibly!
MainWindow
-
+
-
+
-
+
-
+
-
+
-
+
-
+
320
-
+
1024
-
+
2048
-
+
4096
-
+
-
-
+
+
-
-
+
+
-
-
+
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
-
+
+
-
+
-
+
-
+
-
+
-
+
-
-
+
+
-
+
-
+
-
-
+
+
-
-
+
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
-
+
+
-
-
+
+
-
-
-
+
+
+
-
-
-
+
+
+
-
-
-
+
+
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
-
+
+
-
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
+
-
+
-
+
-
-
+
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
-
+
+
-
+
-
+
-
+
-
+
-
+
-
+
+
+
+
+
+
-
+
-
+
-
+
-
+
-
+
-
-
-
-
-
-
+
-
+
-
+
-
+
-
+
-
+
+
+
+
+
+
-
+
-
+
-
+
-
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
+
-
-
+
+
-
-
-
+
+
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
-
+
+
-
+
-
-
-
+
+
+
-
-
-
+
+
+
-
+
-
+
-
-
-
+
+
+
-
+
-
-
-
+
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
-
+
+
+
+
+
+
+
-
-
+
+
-
+
-
-
+
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -1355,56 +1377,56 @@ or the communication between a tag and a reader.
Mifare
-
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
+
-
-
+
+
-
+
-
+
-
+
-
+
-
+
@@ -1412,39 +1434,57 @@ Continue to write?
T55xxTab
-
+
-
+
-
+
-
+
-
+
-
+
-
+
+
+ Util
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/i18n/language.qrc b/i18n/language.qrc
similarity index 100%
rename from src/i18n/language.qrc
rename to i18n/language.qrc
diff --git a/src/i18n/languages.ini b/i18n/languages.ini
similarity index 63%
rename from src/i18n/languages.ini
rename to i18n/languages.ini
index 10c2e3f..6d802f0 100644
--- a/src/i18n/languages.ini
+++ b/i18n/languages.ini
@@ -1,4 +1,3 @@
[Languages]
en_US=English
zh_CN=简体中文
-ext=Load from external file
diff --git a/src/i18n/zh_CN.qm b/i18n/zh_CN.qm
similarity index 88%
rename from src/i18n/zh_CN.qm
rename to i18n/zh_CN.qm
index f2a0336..05dc7be 100644
Binary files a/src/i18n/zh_CN.qm and b/i18n/zh_CN.qm differ
diff --git a/src/i18n/zh_CN.ts b/i18n/zh_CN.ts
similarity index 60%
rename from src/i18n/zh_CN.ts
rename to i18n/zh_CN.ts
index 39e2fa9..7c3f628 100644
--- a/src/i18n/zh_CN.ts
+++ b/i18n/zh_CN.ts
@@ -4,35 +4,35 @@
MF_Attack_hardnestedDialog
-
+
Hardnested攻击
-
+
已知块:
-
-
+
+
块:
-
-
+
+
-
-
+
+
-
+
目标块:
@@ -40,107 +40,107 @@
MF_Sim_simDialog
-
+
模拟
-
+
-
+
4或7字节的UID,如果不指定,则使用模拟器内存中的4字节UID
-
+
-
+
指定ATQA(2个字节)
-
+
-
+
指定SAK(1个字节)
-
+
-
+
在读卡器读取<n>个块后自动退出模拟,n为0或不指定时永远不退出
-
+
-
+
交互模式,勾选后PM3客户端将在模拟完成或者模拟中断后才可继续使用
-
+
-
+
破解,对读卡器进行攻击,通过nr/ar攻击来钓出密码(无卡嗅探)
-
+
-
+
在获得密码后自动将密码写入模拟器内存(自动勾选x(--crack)和i)
-
+
-
+
更多输出内容
-
+
-
+
从<filename.txt>当中获取用于破解读卡器的UID(批量模拟)(自动勾选x和i)
-
+
-
+
生成随机nonce而不是顺序的nonce,这种情况下PM3将不对读卡器进行标准攻击,只进行moebius攻击
@@ -148,22 +148,22 @@
MF_UID_parameterDialog
-
+
设置卡参数
-
+
卡号:
-
+
-
+
@@ -171,123 +171,123 @@
MF_trailerDecoderDialog
-
+
Trailer解码
-
+
块大小
-
+
-
+
-
+
输入控制位数据
(形如“FF0780”或“FF 07 80”)
-
+
手动设置访问情况:
-
+
-
+
-
+
-
+
-
+
数据块访问权限:
-
+
块0
-
+
块1
-
+
块2
-
-
+
+
读
-
-
+
+
写
-
+
增加
-
+
减少/从缓冲区写入/读入至缓冲区
-
+
Trailer访问权限:
-
-
+
+
密钥A
-
+
控制位
-
-
+
+
密钥B
-
+
@@ -296,34 +296,34 @@ MF1S70YYX_V1 Product data sheet
Rev. 3.2 — 23 November 2017
-
+
注意:Access Bits一般包含4个字节(8个16进制字符),但只有前3个字节决定访问情况,最后一个字节可任意设置。
-
+
无效!
可能导致整个扇区被不可逆转地锁定!
-
+
有效
-
-
-
-
-
-
+
+
+
+
+
+
块
-
+
密钥A+B
@@ -331,365 +331,365 @@ It could make the whole sector blocked irreversibly!
MainWindow
-
+
-
+
连接
-
+
断开
-
+
Mifare(IC)卡
-
+
选中密码块
-
+
卡片类型
-
+
320
-
+
1024
-
+
2048
-
+
4096
-
+
文件
-
-
+
+
加载
-
-
+
+
保存
-
-
+
+
数据
-
+
密钥
-
+
破解
-
+
读卡信息
-
+
验证默认密码
-
+
Nested攻击
-
+
Hardested攻击
-
+
Darkside攻击
-
+
读/写
-
+
块:
-
+
密钥:
-
+
密钥类型:
-
+
列出嗅探数据
-
-
+
+
数据:
-
+
普通卡(需要密码)
-
+
Dump命令
-
+
Restore命令
-
+
UID卡(不需要密码)
-
+
锁定UFUID卡
-
-
+
+
关于UID卡
-
+
设置卡参数
-
+
擦除
-
-
+
+
模拟
-
-
+
+
清空
-
+
客户端路径:
-
+
端口:
-
+
刷新端口
-
+
全选
-
+
密码区->密码
-
+
密码区<-密码
-
+
填充密码
-
+
Trailer解码
-
+
设置字体
-
-
+
+
读取单个块
-
-
+
+
写入单个块
-
-
-
+
+
+
读取选中块
-
-
-
+
+
+
写入选中块
-
-
-
+
+
+
嗅探
-
+
嗅探(14a)
-
+
低频配置
-
+
频率
-
+
-
+
-
+
如果频率不为125k/134k,则原装天线可能不适用。
设置频率后GUI会使用"hw setlfdivisor"改变底层分频系数。
-
+
采样精度(Bits per sample):
-
+
抽取(Decimation):
-
+
平均化(Averaging):
-
+
重置
-
+
低频操作
-
+
搜索
-
+
读卡并寻找已知类型的卡
-
+
读取
-
+
激活低频电磁场并读取原始信号。
该功能用于获取卡片原始数据。
-
+
调谐
-
+
@@ -698,7 +698,7 @@ On Iceman/RRG repo, press the button on PM3 to stop measuring
在冰人版固件下,如果需要停止测量,请按下PM3侧面的按钮
-
+
@@ -707,499 +707,520 @@ or the communication between a tag and a reader.
或者卡片与读卡器的交互过程。
-
-
+
+
-
+
基本配置区(页0 块0)
-
-
+
+
16进制:
-
-
+
+
2进制:
-
-
+
+
从数据区导入
-
-
+
+
导出到数据区
-
-
+
+
锁定(Locked):
-
+
-
+
数据比特率(Data Bit Rate):
-
+
扩展模式(eXtended Mode):
-
-
+
+
调制方式(Modulation):
-
+
-
+
-
+
-
+
-
+
密码(Password):
-
+
-
+
-
+
-
+
-
+
-
+
模拟前端选项区(页1 块3)
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
T55xx读卡配置区
-
+
比特率(Bit Rate):
-
+
-
+
-
+
-
+
-
+
-
+
原始命令
-
-
+
+
命令历史:
-
+
清空历史
-
+
发送
-
+
清空输出
-
+
设置
-
+
客户端
-
+
+
+ GUI工作路径:
+
+
+
-
+
-
+
+
+ 配置文件(重连后生效):
+
+
+
语言:
-
+
选择语言
-
+
(重启此程序以使用新语言)
-
+
保持所有按钮可点击,即使未连接客户端或有任务正在运行
-
+
低频
-
+
其它
-
+
分频系数(Divisor):
-
+
实际频率: 125.000kHz
-
+
触发阈值(Trigger threshold):
-
+
跳过前n个采样(Samples to skip):
-
+
获取当前配置
-
+
改变当前配置
-
+
预加载脚本路径(重连后生效):
-
+
如果客户端需要配置环境变量才能正常运行,可以将配置环境变量所需的脚本文件(Windows系统内为*.bat,linux系统内为*.sh)路径填入此处。
-
+
客户端工作路径(重连后生效):
-
+
在Windows系统中,客户端工作路径与GUI程序所在路径不能相同,否则客户端会使用错误的.dll文件。
-
+
启动参数(重连后生效):
-
+
-f选项用于使客户端实时返回命令回显,必须添加。部分情况下启动参数需设置为"-p /dev/<port> -f"或"-p <port> -f"。
-
- 配置文件路径(重连后生效):
+ 配置文件路径(重连后生效):
-
+
-
+
不同客户端需要使用不同的配置文件。若命令格式发生改变,你可以尝试手动修改配置文件以适配。
-
+
在PM3断开后保持客户端运行(实验性功能)
-
+
图形化界面
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
信息
-
+
请先选择端口
-
+
已连接
-
-
+
+
未连接
-
-
-
+
+
+
无法打开
-
+
确定?
-
+
吸附所有悬浮窗口
-
+
版本:
-
+
检查更新
-
+
无法打开配置文件
-
+
+
+ 无法启动客户端
+
+
+
+
+ 无法连接到PM3硬件
+
+
+
部分数据和密码将被清除
-
+
请选择数据窗口和密钥窗口的字体
-
+
数据必须由32个十六进制字符组成(中间可含有空格)
-
-
+
+
密钥必须由12个十六进制字符组成(中间可含有空格)
-
+
请选择数据文件:
-
-
-
+
+
+
二进制数据文件(*.bin *.dump)
-
-
-
+
+
+
所有文件(*.*)
-
+
请选择密钥文件:
-
+
请选择数据文件保存的位置:
-
-
-
+
+
+
无法保存至
-
+
请选择密钥文件保存的位置:
-
-
-
+
+
+
二进制密码文件(*.bin *.dump)
-
-
+
+
文本数据文件(*.txt *.eml)
@@ -1208,160 +1229,165 @@ or the communication between a tag and a reader.
文本密码文件(*.txt *.eml)
-
+
普通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文件保存的位置:
-
-
+
+
+ 外部文件
+
+
+
+
Trace文件(*.trc)
-
-
+
+
空闲
-
+
停止
-
-
+
+
扇区
-
+
块
-
+
密钥A
-
+
密钥B
-
+
固件版本:
-
+
连接状态:
-
+
运行状态:
-
+
正在运行
-
+
实际频率:
@@ -1369,34 +1395,34 @@ or the communication between a tag and a reader.
Mifare
-
+
成功!
-
-
-
-
-
-
-
+
+
+
+
+
+
+
信息
-
+
请至少提供一个已知密码
-
-
+
+
失败!
-
+
@@ -1405,22 +1431,22 @@ Continue to write?
确定要写入吗?
-
+
成功!
-
+
写入以下块失败:
-
+
选中这些块?
-
+
读卡失败。
@@ -1428,39 +1454,57 @@ Continue to write?
T55xxTab
-
+
复制到T55xx卡
-
+
目标卡片类型:
-
+
-
+
-
+
-
+
读卡
-
+
复制
+
+ Util
+
+
+
+ 从外部文件加载
+
+
+
+
+ 选择语言:
+
+
+
+
+ 选择翻译文件:
+
+
diff --git a/src/Proxmark3GUI.pro b/src/Proxmark3GUI.pro
index e915502..761d1fa 100644
--- a/src/Proxmark3GUI.pro
+++ b/src/Proxmark3GUI.pro
@@ -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
diff --git a/src/common/pm3process.cpp b/src/common/pm3process.cpp
index 0546e2c..2b46748 100644
--- a/src/common/pm3process.cpp
+++ b/src/common/pm3process.cpp
@@ -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");
}
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,8 +62,13 @@ void PM3Process::connectPM3(const QString& path, const QStringList args)
emit PM3StatedChanged(true, result);
}
else
+ {
+ emit HWConnectFailed();
kill();
+ }
}
+
+ setRequiringOutput(false);
}
void PM3Process::reconnectPM3()
diff --git a/src/common/pm3process.h b/src/common/pm3process.h
index 44c7b3e..5ae747b 100644
--- a/src/common/pm3process.h
+++ b/src/common/pm3process.h
@@ -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
diff --git a/src/common/util.cpp b/src/common/util.cpp
index 9f3199b..0130a42 100644
--- a/src/common/util.cpp
+++ b/src/common/util.cpp
@@ -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;
}
diff --git a/src/common/util.h b/src/common/util.h
index 23705d4..bda759b 100644
--- a/src/common/util.h
+++ b/src/common/util.h
@@ -13,6 +13,7 @@
#include
#include
#include
+#include
#include
#include "ui_mainwindow.h"
diff --git a/src/i18n/en_US.qm b/src/i18n/en_US.qm
deleted file mode 100644
index 9dad8df..0000000
Binary files a/src/i18n/en_US.qm and /dev/null differ
diff --git a/src/main.cpp b/src/main.cpp
index 40b577b..9b6054f 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -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();
diff --git a/src/module/lf.cpp b/src/module/lf.cpp
index 758f0f8..98a2899 100644
--- a/src/module/lf.cpp
+++ b/src/module/lf.cpp
@@ -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);
diff --git a/src/module/mifare.cpp b/src/module/mifare.cpp
index 94d5b1f..ee6898c 100644
--- a/src/module/mifare.cpp
+++ b/src/module/mifare.cpp
@@ -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("", 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(""))
+ cmd.replace("", 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(""))
+ cmd.replace("", config["card type"].toMap()[cardType.typeText].toString());
+ util->execCMD(cmd);
Util::gotoRawTab();
}
diff --git a/src/ui/mainwindow.cpp b/src/ui/mainwindow.cpp
index d2e338b..6434627 100644
--- a/src/ui/mainwindow.cpp
+++ b/src/ui/mainwindow.cpp
@@ -2,6 +2,7 @@
#include "ui_mainwindow.h"
#include
+#include
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", " -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();
+}
+
diff --git a/src/ui/mainwindow.h b/src/ui/mainwindow.h
index 5eaef2f..1fcdb7f 100644
--- a/src/ui/mainwindow.h
+++ b/src/ui/mainwindow.h
@@ -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;
diff --git a/src/ui/mainwindow.ui b/src/ui/mainwindow.ui
index df5e334..f734d6a 100644
--- a/src/ui/mainwindow.ui
+++ b/src/ui/mainwindow.ui
@@ -1454,8 +1454,8 @@ When setting the freq, the "hw setlfdivisor" will also be called.
-
-
-
+
+ true
@@ -2650,6 +2650,23 @@ or the communication between a tag and a reader.
Client
+ -
+
+
+ GUI working directory:
+
+
+
+ -
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
-
@@ -2746,10 +2763,34 @@ or the communication between a tag and a reader.
-
- Config file path(Reconnect to apply):
+ Config file(Reconnect to apply):
+ -
+
+
-
+
+
+ QComboBox::AdjustToContents
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
-