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 - + Hardnested Attack - + Known Block: - - + + Block: - - + + A - - + + B - + Target Block: @@ -40,107 +40,107 @@ MF_Sim_simDialog - + Simulate - + 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(--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 @@ -148,22 +148,22 @@ MF_UID_parameterDialog - + Set Parameter - + UID: - + ATQA: - + SAK: @@ -171,155 +171,155 @@ MF_trailerDecoderDialog - + Trailer Decoder - + Blocks - + 4 - + 16 - + Trailer Data: (like "FF0780" or "FF 07 80") - + Or set bits manually - + Cx0 - + Cx1 - + Cx2 - + Cx3 - + Data Block Permission: - + Block0 - + Block1 - + Block2 - - + + Read - - + + Write - + Increase - + Decrease/Transfer/Restore - + Trailer Block Permission: - - + + KeyA - + Access Bits - - + + KeyB - + Reference: MF1S70YYX_V1 Product data sheet Rev. 3.2 — 23 November 2017 - + Note:the Access Bits usually contains 4 bytes(8 hex symbols), but only the first 3 bytes matters. You can set the 4th byte randomly. - + Invalid! It could make the whole sector blocked irreversibly! - + Valid - - - - - - + + + + + + Block - + KeyA+B @@ -327,1027 +327,1049 @@ It could make the whole sector blocked irreversibly! MainWindow - + Proxmark3GUI - + 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 - + Darkside - + Read/Write - + Block: - + Key: - + Key Type: - + List Data - + LF - + other - + Divisor: - + Actural Freq: 125.000kHz - + Trigger threshold: - + Samples to skip: - + Get Config - + Set Config - - + + 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 - + Sniff(14a) - + LF Config - + Frequency - + 125k - + 134k - + You might need a modified LF antenna if the freq is not 125k/134k. When setting the freq, the "hw setlfdivisor" will also be called. - + Bits per sample: - + Decimation: - + Averaging: - + Reset - + LF Operation - + Search - + Read and search for valid known tag. - + Read - + Sniff low frequency signal with LF field ON. Use this to get raw data from a tag. - + Tune - + Measure LF antenna tuning. If the antenna voltage has a obvious drop after putting card on the antenna, it is likely that the tag is a LF tag. On Iceman/RRG repo, press the button on PM3 to stop measuring - + Sniff low frequency signal with LF field OFF. Use this to get raw data from a reader or the communication between a tag and a reader. - - + + T55xx - + Basic Configuration(Page 0 Block 0) - - + + Hex: - - + + Bin: - - + + Get from Data - - + + Set to Data - - + + Locked: - + Master Key: - + Data Bit Rate: - + eXtended Mode: - - + + Modulation: - + PSK Clock Freq: - + Answer on Request: - + One Time Pad: - + Max Block: - + Password: - + Seq. Terminator: - + Seq. Start Marker: - + Fast Downlink: - + Inverse Data: - + Init-Delay: - + Analog Front-End Option(Page 1 Block 3) - + Option Key: - + Soft Modulation: - + Clamp Voltage: - + Modulation Voltage: - + Clock Detection Threshold: - + Gap Detection Threshold: - + Write Dampling: - + Demod Delay: - + Downlink Protocol: - + T55xx Read Config - + Bit Rate: - + Seq. Term. - + Offset: - + Inverted: - + T5577 - + T5555 - + RawCommand - - + + History: - + ClearHistory - + Send - + ClearOutput - + Settings - + Client - + + GUI working directory: + + + + Preload script path(Reconnect to apply): - + 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. - + Client working directory(Reconnect to apply): - + On Windows, the client working directory should not be identical to the path of GUI, otherwise the client will use the wrong .dll file. - + Start arguments(Reconnect to apply): - + -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". - - Config file path(Reconnect to apply): - - - - + config.json - + Different clients require different config files. You can change the content of config file if the command format changes. - + Keep the client active even the PM3 hardware is disconnected.(Experimental) - + ../data - + <port> -f - + + Config file(Reconnect to apply): + + + + 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 - - - + + + Failed to open - + Continue? - + Dock all windows - + Ver: - + Check Update - + Failed to load config file - + 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: - - - + + + Binary Data Files(*.bin *.dump) - - - + + + All Files(*.*) - + Plz select the key file: - + Plz select the location to save data file: - - - + + + Failed to save to - + Plz select the location to save key file: - - - + + + Binary Key Files(*.bin *.dump) - - + + Failed to start the client + + + + + Failed to connect to the hardware + + + + + Text Data Files(*.txt *.eml) - + 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: - + Plz select the location to save trace file: - - + + External file + + + + + Trace Files(*.trc) - - + + Idle - + Stop - - + + Sec - + Blk - + KeyA - + KeyB - + HW Version: - + PM3: - + State: - + Running - + Actural Freq: @@ -1355,56 +1377,56 @@ or the communication between a tag and a reader. Mifare - + Succeed! - - - - - - - + + + + + + + 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? - + Succeed! - + Failed to write to these blocks: - + Select them? - + Failed to read card. @@ -1412,39 +1434,57 @@ Continue to write? T55xxTab - + Clone to T55xx - + Target Type: - + T5555 - + T55x7 - + EM410x - + Read - + Clone + + Util + + + Load from external file + + + + + Choose a language: + + + + + Select the translation file: + + + 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 Attack Hardnested攻击 - + Known Block: 已知块: - - + + Block: 块: - - + + A - - + + B - + Target Block: 目标块: @@ -40,107 +40,107 @@ MF_Sim_simDialog - + Simulate 模拟 - + 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(--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攻击 @@ -148,22 +148,22 @@ MF_UID_parameterDialog - + Set Parameter 设置卡参数 - + UID: 卡号: - + ATQA: - + SAK: @@ -171,123 +171,123 @@ MF_trailerDecoderDialog - + Trailer Decoder Trailer解码 - + Blocks 块大小 - + 4 - + 16 - + Trailer Data: (like "FF0780" or "FF 07 80") 输入控制位数据 (形如“FF0780”或“FF 07 80”) - + Or set bits manually 手动设置访问情况: - + Cx0 - + Cx1 - + Cx2 - + Cx3 - + Data Block Permission: 数据块访问权限: - + Block0 块0 - + Block1 块1 - + Block2 块2 - - + + Read - - + + Write - + Increase 增加 - + Decrease/Transfer/Restore 减少/从缓冲区写入/读入至缓冲区 - + Trailer Block Permission: Trailer访问权限: - - + + KeyA 密钥A - + Access Bits 控制位 - - + + KeyB 密钥B - + Reference: MF1S70YYX_V1 Product data sheet Rev. 3.2 — 23 November 2017 @@ -296,34 +296,34 @@ MF1S70YYX_V1 Product data sheet Rev. 3.2 — 23 November 2017 - + Note:the Access Bits usually contains 4 bytes(8 hex symbols), but only the first 3 bytes matters. You can set the 4th byte randomly. 注意:Access Bits一般包含4个字节(8个16进制字符),但只有前3个字节决定访问情况,最后一个字节可任意设置。 - + Invalid! It could make the whole sector blocked irreversibly! 无效! 可能导致整个扇区被不可逆转地锁定! - + Valid 有效 - - - - - - + + + + + + Block - + KeyA+B 密钥A+B @@ -331,365 +331,365 @@ It could make the whole sector blocked irreversibly! MainWindow - + Proxmark3GUI - + 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: 密钥类型: - + 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 Config 低频配置 - + Frequency 频率 - + 125k - + 134k - + You might need a modified LF antenna if the freq is not 125k/134k. When setting the freq, the "hw setlfdivisor" will also be called. 如果频率不为125k/134k,则原装天线可能不适用。 设置频率后GUI会使用"hw setlfdivisor"改变底层分频系数。 - + Bits per sample: 采样精度(Bits per sample): - + Decimation: 抽取(Decimation): - + Averaging: 平均化(Averaging): - + Reset 重置 - + LF Operation 低频操作 - + Search 搜索 - + Read and search for valid known tag. 读卡并寻找已知类型的卡 - + Read 读取 - + Sniff low frequency signal with LF field ON. Use this to get raw data from a tag. 激活低频电磁场并读取原始信号。 该功能用于获取卡片原始数据。 - + Tune 调谐 - + Measure LF antenna tuning. If the antenna voltage has a obvious drop after putting card on the antenna, it is likely that the tag is a LF tag. On Iceman/RRG repo, press the button on PM3 to stop measuring @@ -698,7 +698,7 @@ On Iceman/RRG repo, press the button on PM3 to stop measuring 在冰人版固件下,如果需要停止测量,请按下PM3侧面的按钮 - + Sniff low frequency signal with LF field OFF. Use this to get raw data from a reader or the communication between a tag and a reader. @@ -707,499 +707,520 @@ or the communication between a tag and a reader. 或者卡片与读卡器的交互过程。 - - + + T55xx - + Basic Configuration(Page 0 Block 0) 基本配置区(页0 块0) - - + + Hex: 16进制: - - + + Bin: 2进制: - - + + Get from Data 从数据区导入 - - + + Set to Data 导出到数据区 - - + + Locked: 锁定(Locked): - + Master Key: - + Data Bit Rate: 数据比特率(Data Bit Rate): - + eXtended Mode: 扩展模式(eXtended Mode): - - + + Modulation: 调制方式(Modulation): - + PSK Clock Freq: - + Answer on Request: - + One Time Pad: - + Max Block: - + Password: 密码(Password): - + Seq. Terminator: - + Seq. Start Marker: - + Fast Downlink: - + Inverse Data: - + Init-Delay: - + Analog Front-End Option(Page 1 Block 3) 模拟前端选项区(页1 块3) - + Option Key: - + Soft Modulation: - + Clamp Voltage: - + Modulation Voltage: - + Clock Detection Threshold: - + Gap Detection Threshold: - + Write Dampling: - + Demod Delay: - + Downlink Protocol: - + T55xx Read Config T55xx读卡配置区 - + Bit Rate: 比特率(Bit Rate): - + Seq. Term. - + Offset: - + Inverted: - + T5577 - + T5555 - + RawCommand 原始命令 - - + + History: 命令历史: - + ClearHistory 清空历史 - + Send 发送 - + ClearOutput 清空输出 - + Settings 设置 - + Client 客户端 - + + GUI working directory: + GUI工作路径: + + + ../data - + <port> -f - + + Config file(Reconnect to apply): + 配置文件(重连后生效): + + + Language: 语言: - + Choose Language 选择语言 - + (Restart this app to use new language) (重启此程序以使用新语言) - + Keep buttons enabled even the client is running or disconnected 保持所有按钮可点击,即使未连接客户端或有任务正在运行 - + LF 低频 - + other 其它 - + Divisor: 分频系数(Divisor): - + Actural Freq: 125.000kHz 实际频率: 125.000kHz - + Trigger threshold: 触发阈值(Trigger threshold): - + Samples to skip: 跳过前n个采样(Samples to skip): - + Get Config 获取当前配置 - + Set Config 改变当前配置 - + Preload script path(Reconnect to apply): 预加载脚本路径(重连后生效): - + 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)路径填入此处。 - + Client working directory(Reconnect to apply): 客户端工作路径(重连后生效): - + On Windows, the client working directory should not be identical to the path of GUI, otherwise the client will use the wrong .dll file. 在Windows系统中,客户端工作路径与GUI程序所在路径不能相同,否则客户端会使用错误的.dll文件。 - + Start arguments(Reconnect to apply): 启动参数(重连后生效): - + -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"。 - Config file path(Reconnect to apply): - 配置文件路径(重连后生效): + 配置文件路径(重连后生效): - + config.json - + Different clients require different config files. You can change the content of config file if the command format changes. 不同客户端需要使用不同的配置文件。若命令格式发生改变,你可以尝试手动修改配置文件以适配。 - + Keep the client active even the PM3 hardware is disconnected.(Experimental) 在PM3断开后保持客户端运行(实验性功能) - + GUI 图形化界面 - - - - - - - - - - - - + + + + + + + + + + + + + + Info 信息 - + Plz choose a port first 请先选择端口 - + Connected 已连接 - - + + Not Connected 未连接 - - - + + + Failed to open 无法打开 - + Continue? 确定? - + Dock all windows 吸附所有悬浮窗口 - + Ver: 版本: - + Check Update 检查更新 - + Failed to load config file 无法打开配置文件 - + + Failed to start the client + 无法启动客户端 + + + + Failed to connect to the hardware + 无法连接到PM3硬件 + + + 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) 数据必须由32个十六进制字符组成(中间可含有空格) - - + + Key must consists of 12 Hex symbols(Whitespace is allowed) 密钥必须由12个十六进制字符组成(中间可含有空格) - + Plz select the data file: 请选择数据文件: - - - + + + Binary Data Files(*.bin *.dump) 二进制数据文件(*.bin *.dump) - - - + + + All Files(*.*) 所有文件(*.*) - + Plz select the key file: 请选择密钥文件: - + Plz select the location to save data file: 请选择数据文件保存的位置: - - - + + + Failed to save to 无法保存至 - + Plz select the location to save key file: 请选择密钥文件保存的位置: - - - + + + Binary Key Files(*.bin *.dump) 二进制密码文件(*.bin *.dump) - - + + Text Data Files(*.txt *.eml) 文本数据文件(*.txt *.eml) @@ -1208,160 +1229,165 @@ or the communication between a tag and a reader. 文本密码文件(*.txt *.eml) - + 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卡的块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)的块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. 可通过普通的写块命令来写块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). 块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卡一样可以反复读写块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文件: - + Plz select the location to save trace file: 请选择trace文件保存的位置: - - + + External file + 外部文件 + + + + Trace Files(*.trc) Trace文件(*.trc) - - + + Idle 空闲 - + Stop 停止 - - + + Sec 扇区 - + Blk - + KeyA 密钥A - + KeyB 密钥B - + HW Version: 固件版本: - + PM3: 连接状态: - + State: 运行状态: - + Running 正在运行 - + Actural Freq: 实际频率: @@ -1369,34 +1395,34 @@ or the communication between a tag and a reader. Mifare - + Succeed! 成功! - - - - - - - + + + + + + + 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? @@ -1405,22 +1431,22 @@ Continue to write? 确定要写入吗? - + Succeed! 成功! - + Failed to write to these blocks: 写入以下块失败: - + Select them? 选中这些块? - + Failed to read card. 读卡失败。 @@ -1428,39 +1454,57 @@ Continue to write? T55xxTab - + Clone to T55xx 复制到T55xx卡 - + Target Type: 目标卡片类型: - + T5555 - + T55x7 - + EM410x - + Read 读卡 - + Clone 复制 + + Util + + + Load from external file + 从外部文件加载 + + + + Choose a language: + 选择语言: + + + + Select the translation file: + 选择翻译文件: + + 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 + + + + + +